diff options
Diffstat (limited to 'gold')
67 files changed, 34189 insertions, 0 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am new file mode 100644 index 000000000000..a411127df94d --- /dev/null +++ b/gold/Makefile.am @@ -0,0 +1,112 @@ +# Process this file with automake to generate Makefile.in + +AUTOMAKE_OPTIONS = + +SUBDIRS = po testsuite + +tooldir = $(exec_prefix)/$(target_alias) + +ACLOCAL_AMFLAGS = -I ../bfd -I ../config + +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CXXFLAGS) + +INCLUDES = -D_GNU_SOURCE \ + -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../elfcpp \ + -DLOCALEDIR="\"$(datadir)/locale\"" \ + @INCINTL@ + +YFLAGS = -d + +noinst_PROGRAMS = ld-new +noinst_LIBRARIES = libgold.a + +CCFILES = \ + archive.cc \ + common.cc \ + defstd.cc \ + dirsearch.cc \ + dynobj.cc \ + fileread.cc \ + gold.cc \ + gold-threads.cc \ + layout.cc \ + merge.cc \ + object.cc \ + options.cc \ + output.cc \ + readsyms.cc \ + reloc.cc \ + resolve.cc \ + script.cc \ + symtab.cc \ + stringpool.cc \ + target-select.cc \ + workqueue.cc + +HFILES = \ + archive.h \ + common.h \ + defstd.h \ + dirsearch.h \ + dynobj.h \ + fileread.h \ + gold.h \ + gold-threads.h \ + layout.h \ + merge.h \ + object.h \ + options.h \ + output.h \ + readsyms.h \ + reloc.h \ + reloc-types.h \ + script.h \ + script-c.h \ + stringpool.h \ + symtab.h \ + target.h \ + target-reloc.h \ + target-select.h \ + workqueue.h + +TARGETFILES = \ + i386.cc + +YFILES = \ + yyscript.y + +EXTRA_DIST = yyscript.c yyscript.h + +libgold_a_SOURCES = $(CCFILES) $(HFILES) $(YFILES) + +ld_new_SOURCES = main.cc $(TARGETFILES) +ld_new_DEPENDENCIES = libgold.a $(LIBINTL_DEP) +ld_new_LDADD = libgold.a $(LIBINTL) + +# Use an explicit dependency for the bison generated header file. +script.$(OBJEXT): yyscript.h + +# We have to build libgold.a before we run the tests. +check: libgold.a + +.PHONY: install-exec-local + +install-exec-local: ld-new$(EXEEXT) + $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(tooldir)/bin + n=`echo ld | sed '$(transform)'; \ + $(INSTALL_PROGRAM) ld-new$(EXEEXT) $(DESTDIR)$(bindir)/$${n}$(EXEEXT); \ + if test "$(bindir)" != "$(tooldir)/bin"; then \ + rm -f $(DESTDIR)$(tooldir)/bin/ld$(EXEEXT); \ + ln $(DESTDIR)$(bindir)/$${n}$(EXEEXT) $(DESTDIR)$(tooldir)/bin/ld$(EXEEXT) >/dev/null 2>/dev/null \ + || $(INSTALL_PROGRAM) ld-new$(EXEEXT) $(DESTDIR)$(tooldir)/bin/ld$(EXEEXT); \ + fi + +# We want install to imply install-info as per GNU standards, despite +# the cygnus option. +install-data-local: install-info + +POTFILES= $(CCFILES) $(HFILES) $(TARGETFILES) + +po/POTFILES.in: @MAINT@ Makefile + for f in $(POTFILES); do echo $$f; done | LC_COLLATE= sort > tmp \ + && mv tmp $(srcdir)/po/POTFILES.in diff --git a/gold/Makefile.in b/gold/Makefile.in new file mode 100644 index 000000000000..884b1a52c888 --- /dev/null +++ b/gold/Makefile.in @@ -0,0 +1,873 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Process this file with automake to generate Makefile.in + + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +noinst_PROGRAMS = ld-new$(EXEEXT) +DIST_COMMON = README $(am__configure_deps) $(srcdir)/../config.guess \ + $(srcdir)/../config.sub $(srcdir)/../depcomp \ + $(srcdir)/../install-sh $(srcdir)/../missing \ + $(srcdir)/../mkinstalldirs $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(srcdir)/config.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 \ + $(top_srcdir)/../config/lead-dot.m4 \ + $(top_srcdir)/../config/progtest.m4 \ + $(top_srcdir)/../config/po.m4 $(top_srcdir)/../config/nls.m4 \ + $(top_srcdir)/../config/gettext-sister.m4 \ + $(top_srcdir)/../bfd/warning.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno configure.status.lineno +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = po/Makefile.in +LIBRARIES = $(noinst_LIBRARIES) +AR = ar +ARFLAGS = cru +libgold_a_AR = $(AR) $(ARFLAGS) +libgold_a_LIBADD = +am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \ + dirsearch.$(OBJEXT) dynobj.$(OBJEXT) fileread.$(OBJEXT) \ + gold.$(OBJEXT) gold-threads.$(OBJEXT) layout.$(OBJEXT) \ + merge.$(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 = yyscript.$(OBJEXT) +am_libgold_a_OBJECTS = $(am__objects_1) $(am__objects_2) \ + $(am__objects_3) +libgold_a_OBJECTS = $(am_libgold_a_OBJECTS) +PROGRAMS = $(noinst_PROGRAMS) +am__objects_4 = i386.$(OBJEXT) +am_ld_new_OBJECTS = main.$(OBJEXT) $(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 $@ +YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS) +SOURCES = $(libgold_a_SOURCES) $(ld_new_SOURCES) +DIST_SOURCES = $(libgold_a_SOURCES) $(ld_new_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GENCAT = @GENCAT@ +GMSGFMT = @GMSGFMT@ +INCINTL = @INCINTL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +LDFLAGS = @LDFLAGS@ +LFS_CXXFLAGS = @LFS_CXXFLAGS@ +LIBINTL = @LIBINTL@ +LIBINTL_DEP = @LIBINTL_DEP@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +NO_WERROR = @NO_WERROR@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +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_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +AUTOMAKE_OPTIONS = +SUBDIRS = po testsuite +tooldir = $(exec_prefix)/$(target_alias) +ACLOCAL_AMFLAGS = -I ../bfd -I ../config +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CXXFLAGS) +INCLUDES = -D_GNU_SOURCE \ + -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../elfcpp \ + -DLOCALEDIR="\"$(datadir)/locale\"" \ + @INCINTL@ + +YFLAGS = -d +noinst_LIBRARIES = libgold.a +CCFILES = \ + archive.cc \ + common.cc \ + defstd.cc \ + dirsearch.cc \ + dynobj.cc \ + fileread.cc \ + gold.cc \ + gold-threads.cc \ + layout.cc \ + merge.cc \ + object.cc \ + options.cc \ + output.cc \ + readsyms.cc \ + reloc.cc \ + resolve.cc \ + script.cc \ + symtab.cc \ + stringpool.cc \ + target-select.cc \ + workqueue.cc + +HFILES = \ + archive.h \ + common.h \ + defstd.h \ + dirsearch.h \ + dynobj.h \ + fileread.h \ + gold.h \ + gold-threads.h \ + layout.h \ + merge.h \ + object.h \ + options.h \ + output.h \ + readsyms.h \ + reloc.h \ + reloc-types.h \ + script.h \ + script-c.h \ + stringpool.h \ + symtab.h \ + target.h \ + target-reloc.h \ + target-select.h \ + workqueue.h + +TARGETFILES = \ + i386.cc + +YFILES = \ + yyscript.y + +EXTRA_DIST = yyscript.c yyscript.h +libgold_a_SOURCES = $(CCFILES) $(HFILES) $(YFILES) +ld_new_SOURCES = main.cc $(TARGETFILES) +ld_new_DEPENDENCIES = libgold.a $(LIBINTL_DEP) +ld_new_LDADD = libgold.a $(LIBINTL) +POTFILES = $(CCFILES) $(HFILES) $(TARGETFILES) +all: config.h + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +.SUFFIXES: .c .cc .o .obj .y +am--refresh: + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \ + cd $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) + +config.h: stamp-h1 + @if test ! -f $@; then \ + rm -f stamp-h1; \ + $(MAKE) stamp-h1; \ + else :; fi + +stamp-h1: $(srcdir)/config.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status config.h +$(srcdir)/config.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_srcdir) && $(AUTOHEADER) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +po/Makefile.in: $(top_builddir)/config.status $(top_srcdir)/po/Make-in + cd $(top_builddir) && $(SHELL) ./config.status $@ + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +yyscript.h: yyscript.c + @if test ! -f $@; then \ + rm -f yyscript.c; \ + $(MAKE) yyscript.c; \ + else :; fi +libgold.a: $(libgold_a_OBJECTS) $(libgold_a_DEPENDENCIES) + -rm -f libgold.a + $(libgold_a_AR) libgold.a $(libgold_a_OBJECTS) $(libgold_a_LIBADD) + $(RANLIB) libgold.a + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) +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) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive.Po@am__quote@ +@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@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i386.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layout.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/merge.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/object.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@ +@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 $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@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 $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@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 +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) config.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) config.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.in $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) config.in $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + mkdir $(distdir) + $(mkdir_p) $(distdir)/.. $(distdir)/../bfd $(distdir)/../config $(distdir)/po + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && cd $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}' +distuninstallcheck: + @cd $(distuninstallcheck_dir) \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-recursive +all-am: Makefile $(LIBRARIES) $(PROGRAMS) config.h +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -rm -f yyscript.c + -rm -f yyscript.h +clean: clean-recursive + +clean-am: clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: install-data-local + +install-exec-am: install-exec-local + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \ + check-am clean clean-generic clean-noinstLIBRARIES \ + clean-noinstPROGRAMS clean-recursive ctags ctags-recursive \ + dist dist-all dist-bzip2 dist-gzip dist-shar dist-tarZ \ + dist-zip distcheck distclean distclean-compile \ + distclean-generic distclean-hdr distclean-recursive \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-data-local install-exec \ + install-exec-am install-exec-local install-info \ + install-info-am install-man install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ + uninstall uninstall-am uninstall-info-am + + +# Use an explicit dependency for the bison generated header file. +script.$(OBJEXT): yyscript.h + +# We have to build libgold.a before we run the tests. +check: libgold.a + +.PHONY: install-exec-local + +install-exec-local: ld-new$(EXEEXT) + $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(tooldir)/bin + n=`echo ld | sed '$(transform)'; \ + $(INSTALL_PROGRAM) ld-new$(EXEEXT) $(DESTDIR)$(bindir)/$${n}$(EXEEXT); \ + if test "$(bindir)" != "$(tooldir)/bin"; then \ + rm -f $(DESTDIR)$(tooldir)/bin/ld$(EXEEXT); \ + ln $(DESTDIR)$(bindir)/$${n}$(EXEEXT) $(DESTDIR)$(tooldir)/bin/ld$(EXEEXT) >/dev/null 2>/dev/null \ + || $(INSTALL_PROGRAM) ld-new$(EXEEXT) $(DESTDIR)$(tooldir)/bin/ld$(EXEEXT); \ + fi + +# We want install to imply install-info as per GNU standards, despite +# the cygnus option. +install-data-local: install-info + +po/POTFILES.in: @MAINT@ Makefile + for f in $(POTFILES); do echo $$f; done | LC_COLLATE= sort > tmp \ + && mv tmp $(srcdir)/po/POTFILES.in +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gold/README b/gold/README new file mode 100644 index 000000000000..aa318f2daa8e --- /dev/null +++ b/gold/README @@ -0,0 +1,18 @@ +gold is an ELF linker. It is intended to have complete support for +ELF and to run as fast as possible on modern systems. + +It is written in C++. It is (intended to be) a GNU program, and +therefore follows the GNU formatting standards as modified for C++. +Source documents in order of precedence: + http://www.gnu.org/prep/standards/ + http://gcc.gnu.org/onlinedocs/libstdc++/17_intro/C++STYLE + http://www.zembu.com/eng/procs/c++style.html + +The linker is intended to have complete support for cross-compilation, +which still supporting the normal case of native linking as fast as +possible. This makes the code more complex. + +Many functions are actually templates whose parameter is the ELF file +class (e.g., 32 bits or 64 bits). The code is the same, but we don't +want to pay the execution time cost of always using 64-bit integers if +the target is 32 bits. diff --git a/gold/aclocal.m4 b/gold/aclocal.m4 new file mode 100644 index 000000000000..4438a34308e6 --- /dev/null +++ b/gold/aclocal.m4 @@ -0,0 +1,877 @@ +# generated automatically by aclocal 1.9.6 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +# 2005 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.9.6])]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 7 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +#serial 3 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. +AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 12 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.58])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $1 | $1:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +AC_DEFUN([AM_MAINTAINER_MODE], +[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode is disabled by default + AC_ARG_ENABLE(maintainer-mode, +[ --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer], + USE_MAINTAINER_MODE=$enableval, + USE_MAINTAINER_MODE=no) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST(MAINT)dnl +] +) + +AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_MKDIR_P +# --------------- +# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise. +# +# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories +# created by `make install' are always world readable, even if the +# installer happens to have an overly restrictive umask (e.g. 077). +# This was a mistake. There are at least two reasons why we must not +# use `-m 0755': +# - it causes special bits like SGID to be ignored, +# - it may be too restrictive (some setups expect 775 directories). +# +# Do not use -m 0755 and let people choose whatever they expect by +# setting umask. +# +# We cannot accept any implementation of `mkdir' that recognizes `-p'. +# Some implementations (such as Solaris 8's) are not thread-safe: if a +# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c' +# concurrently, both version can detect that a/ is missing, but only +# one can create it and the other will error out. Consequently we +# restrict ourselves to GNU make (using the --version option ensures +# this.) +AC_DEFUN([AM_PROG_MKDIR_P], +[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi +AC_SUBST([mkdir_p])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar <conftest.tar]) + grep GrepMe conftest.dir/file >/dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([../config/depstand.m4]) +m4_include([../config/lead-dot.m4]) +m4_include([../config/progtest.m4]) +m4_include([../config/po.m4]) +m4_include([../config/nls.m4]) +m4_include([../config/gettext-sister.m4]) +m4_include([../bfd/warning.m4]) diff --git a/gold/archive.cc b/gold/archive.cc new file mode 100644 index 000000000000..d0854036a672 --- /dev/null +++ b/gold/archive.cc @@ -0,0 +1,368 @@ +// archive.cc -- archive support for gold + +#include "gold.h" + +#include <cerrno> +#include <cstring> +#include <climits> +#include <vector> + +#include "elfcpp.h" +#include "fileread.h" +#include "readsyms.h" +#include "symtab.h" +#include "object.h" +#include "archive.h" + +namespace gold +{ + +// The header of an entry in the archive. This is all readable text, +// padded with spaces where necesary. If the contents of an archive +// are all text file, the entire archive is readable. + +struct Archive::Archive_header +{ + // The entry name. + char ar_name[16]; + // The file modification time. + char ar_date[12]; + // The user's UID in decimal. + char ar_uid[6]; + // The user's GID in decimal. + char ar_gid[6]; + // The file mode in octal. + char ar_mode[8]; + // The file size in decimal. + char ar_size[10]; + // The final magic code. + char ar_fmag[2]; +}; + +// Archive methods. + +const char Archive::armag[sarmag] = +{ + '!', '<', 'a', 'r', 'c', 'h', '>', '\n' +}; + +const char Archive::arfmag[2] = { '`', '\n' }; + +// Set up the archive: read the symbol map and the extended name +// table. + +void +Archive::setup() +{ + // The first member of the archive should be the symbol table. + std::string armap_name; + off_t armap_size = this->read_header(sarmag, &armap_name); + if (!armap_name.empty()) + { + fprintf(stderr, _("%s: %s: no archive symbol table (run ranlib)\n"), + program_name, this->name().c_str()); + gold_exit(false); + } + + // Read in the entire armap. + const unsigned char* p = this->get_view(sarmag + sizeof(Archive_header), + armap_size); + + // Numbers in the armap are always big-endian. + const elfcpp::Elf_Word* pword = reinterpret_cast<const elfcpp::Elf_Word*>(p); + unsigned int nsyms = elfcpp::Swap<32, true>::readval(pword); + ++pword; + + // Note that the addition is in units of sizeof(elfcpp::Elf_Word). + const char* pnames = reinterpret_cast<const char*>(pword + nsyms); + + this->armap_.resize(nsyms); + + for (unsigned int i = 0; i < nsyms; ++i) + { + this->armap_[i].name = pnames; + this->armap_[i].offset = elfcpp::Swap<32, true>::readval(pword); + pnames += strlen(pnames) + 1; + ++pword; + } + + if (reinterpret_cast<const unsigned char*>(pnames) - p > armap_size) + { + fprintf(stderr, _("%s: %s: bad archive symbol table names\n"), + program_name, this->name().c_str()); + gold_exit(false); + } + + // See if there is an extended name table. + off_t off = sarmag + sizeof(Archive_header) + armap_size; + if ((off & 1) != 0) + ++off; + std::string xname; + off_t extended_size = this->read_header(off, &xname); + if (xname == "/") + { + p = this->get_view(off + sizeof(Archive_header), extended_size); + const char* px = reinterpret_cast<const char*>(p); + this->extended_names_.assign(px, extended_size); + } + + // This array keeps track of which symbols are for archive elements + // which we have already included in the link. + this->seen_.resize(nsyms); + + // Opening the file locked it. Unlock it now. + this->input_file_->file().unlock(); +} + +// Read the header of an archive member at OFF. Fail if something +// goes wrong. Return the size of the member. Set *PNAME to the name +// of the member. + +off_t +Archive::read_header(off_t off, std::string* pname) +{ + const unsigned char* p = this->get_view(off, sizeof(Archive_header)); + const Archive_header* hdr = reinterpret_cast<const Archive_header*>(p); + + if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0) + { + fprintf(stderr, _("%s; %s: malformed archive header at %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + const int size_string_size = sizeof hdr->ar_size; + char size_string[size_string_size + 1]; + memcpy(size_string, hdr->ar_size, size_string_size); + char* ps = size_string + size_string_size; + while (ps[-1] == ' ') + --ps; + *ps = '\0'; + + errno = 0; + char* end; + off_t member_size = strtol(size_string, &end, 10); + if (*end != '\0' + || member_size < 0 + || (member_size == LONG_MAX && errno == ERANGE)) + { + fprintf(stderr, _("%s: %s: malformed archive header size at %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + if (hdr->ar_name[0] != '/') + { + const char* name_end = strchr(hdr->ar_name, '/'); + if (name_end == NULL + || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name)) + { + fprintf(stderr, _("%s: %s: malformed archive header name at %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + pname->assign(hdr->ar_name, name_end - hdr->ar_name); + } + else if (hdr->ar_name[1] == ' ') + { + // This is the symbol table. + pname->clear(); + } + else if (hdr->ar_name[1] == '/') + { + // This is the extended name table. + pname->assign(1, '/'); + } + else + { + errno = 0; + long x = strtol(hdr->ar_name + 1, &end, 10); + if (*end != ' ' + || x < 0 + || (x == LONG_MAX && errno == ERANGE) + || static_cast<size_t>(x) >= this->extended_names_.size()) + { + fprintf(stderr, _("%s: %s: bad extended name index at %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + const char* name = this->extended_names_.data() + x; + const char* name_end = strchr(name, '/'); + if (static_cast<size_t>(name_end - name) > this->extended_names_.size() + || name_end[1] != '\n') + { + fprintf(stderr, _("%s: %s: bad extended name entry at header %ld\n"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + pname->assign(name, name_end - name); + } + + return member_size; +} + +// Select members from the archive and add them to the link. We walk +// through the elements in the archive map, and look each one up in +// the symbol table. If it exists as a strong undefined symbol, we +// pull in the corresponding element. We have to do this in a loop, +// since pulling in one element may create new undefined symbols which +// may be satisfied by other objects in the archive. + +void +Archive::add_symbols(const General_options& options, Symbol_table* symtab, + Layout* layout, Input_objects* input_objects) +{ + const size_t armap_size = this->armap_.size(); + + bool added_new_object; + do + { + added_new_object = false; + off_t last = -1; + for (size_t i = 0; i < armap_size; ++i) + { + if (this->seen_[i]) + continue; + if (this->armap_[i].offset == last) + { + this->seen_[i] = true; + continue; + } + + Symbol* sym = symtab->lookup(this->armap_[i].name); + if (sym == NULL) + continue; + else if (!sym->is_undefined()) + { + this->seen_[i] = true; + continue; + } + else if (sym->binding() == elfcpp::STB_WEAK) + continue; + + // We want to include this object in the link. + last = this->armap_[i].offset; + this->include_member(options, symtab, layout, input_objects, last); + this->seen_[i] = true; + added_new_object = true; + } + } + while (added_new_object); +} + +// Include an archive member in the link. OFF is the file offset of +// the member header. + +void +Archive::include_member(const General_options& options, Symbol_table* symtab, + Layout* layout, Input_objects* input_objects, + off_t off) +{ + std::string n; + this->read_header(off, &n); + + size_t memoff = off + sizeof(Archive_header); + + // Read enough of the file to pick up the entire ELF header. + int ehdr_size = elfcpp::Elf_sizes<64>::ehdr_size; + off_t bytes; + const unsigned char* p = this->input_file_->file().get_view(memoff, + ehdr_size, + &bytes); + if (bytes < 4) + { + fprintf(stderr, _("%s: %s: member at %ld is not an ELF object"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + static unsigned char elfmagic[4] = + { + elfcpp::ELFMAG0, elfcpp::ELFMAG1, + elfcpp::ELFMAG2, elfcpp::ELFMAG3 + }; + if (memcmp(p, elfmagic, 4) != 0) + { + fprintf(stderr, _("%s: %s: member at %ld is not an ELF object"), + program_name, this->name().c_str(), + static_cast<long>(off)); + gold_exit(false); + } + + Object* obj = make_elf_object((std::string(this->input_file_->filename()) + + "(" + n + ")"), + this->input_file_, memoff, p, bytes); + + input_objects->add_object(obj); + + Read_symbols_data sd; + obj->read_symbols(&sd); + obj->layout(options, symtab, layout, &sd); + obj->add_symbols(symtab, &sd); +} + +// Add_archive_symbols methods. + +Add_archive_symbols::~Add_archive_symbols() +{ + if (this->this_blocker_ != NULL) + delete this->this_blocker_; + // next_blocker_ is deleted by the task associated with the next + // input file. +} + +// Return whether we can add the archive symbols. We are blocked by +// this_blocker_. We block next_blocker_. We also lock the file. + +Task::Is_runnable_type +Add_archive_symbols::is_runnable(Workqueue*) +{ + if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) + return IS_BLOCKED; + return IS_RUNNABLE; +} + +class Add_archive_symbols::Add_archive_symbols_locker : public Task_locker +{ + public: + Add_archive_symbols_locker(Task_token& token, Workqueue* workqueue, + File_read& file) + : blocker_(token, workqueue), filelock_(file) + { } + + private: + Task_locker_block blocker_; + Task_locker_obj<File_read> filelock_; +}; + +Task_locker* +Add_archive_symbols::locks(Workqueue* workqueue) +{ + return new Add_archive_symbols_locker(*this->next_blocker_, + workqueue, + this->archive_->file()); +} + +void +Add_archive_symbols::run(Workqueue*) +{ + this->archive_->add_symbols(this->options_, this->symtab_, this->layout_, + this->input_objects_); + + if (this->input_group_ != NULL) + this->input_group_->add_archive(this->archive_); + else + { + // We no longer need to know about this archive. + delete this->archive_; + } +} + +} // End namespace gold. diff --git a/gold/archive.h b/gold/archive.h new file mode 100644 index 000000000000..193a9e2de820 --- /dev/null +++ b/gold/archive.h @@ -0,0 +1,162 @@ +// archive.h -- archive support for gold -*- C++ -*- + +#ifndef GOLD_ARCHIVE_H +#define GOLD_ARCHIVE_H + +#include <string> +#include <vector> + +#include "workqueue.h" + +namespace gold +{ + +class General_options; +class Input_file; +class Input_objects; +class Input_group; +class Layout; +class Symbol_table; + +// This class represents an archive--generally a libNAME.a file. +// Archives have a symbol table and a list of objects. + +class Archive +{ + public: + Archive(const std::string& name, Input_file* input_file) + : name_(name), input_file_(input_file), armap_(), extended_names_() + { } + + // The length of the magic string at the start of an archive. + static const int sarmag = 8; + + // The magic string at the start of an archive. + static const char armag[sarmag]; + + // The string expected at the end of an archive member header. + static const char arfmag[2]; + + // The name of the object. + const std::string& + name() const + { return this->name_; } + + // Set up the archive: read the symbol map. + void + setup(); + + // Get a reference to the underlying file. + File_read& + file() + { return this->input_file_->file(); } + + // Lock the underlying file. + void + lock() + { this->input_file_->file().lock(); } + + // Unlock the underlying file. + void + unlock() + { this->input_file_->file().unlock(); } + + // Return whether the underlying file is locked. + bool + is_locked() const + { return this->input_file_->file().is_locked(); } + + // Select members from the archive as needed and add them to the + // link. + void + add_symbols(const General_options&, Symbol_table*, Layout*, Input_objects*); + + private: + Archive(const Archive&); + Archive& operator=(const Archive&); + + struct Archive_header; + + // Get a view into the underlying file. + const unsigned char* + get_view(off_t start, off_t size) + { return this->input_file_->file().get_view(start, size); } + + // Read an archive member header at OFF. Return the size of the + // member, and set *PNAME to the name. + off_t + read_header(off_t off, std::string* pname); + + // Include an archive member in the link. + void + include_member(const General_options&, Symbol_table*, Layout*, + Input_objects*, off_t off); + + // An entry in the archive map of symbols to object files. + struct Armap_entry + { + // The symbol name. + const char* name; + // The offset to the file. + off_t offset; + }; + + // Name of object as printed to user. + std::string name_; + // For reading the file. + Input_file* input_file_; + // The archive map. + std::vector<Armap_entry> armap_; + // The extended name table. + std::string extended_names_; + // Track which symbols in the archive map are for elements which + // have already been included in the link. + std::vector<bool> seen_; +}; + +// This class is used to read an archive and pick out the desired +// elements and add them to the link. + +class Add_archive_symbols : public Task +{ + public: + Add_archive_symbols(const General_options& options, Symbol_table* symtab, + Layout* layout, Input_objects* input_objects, + Archive* archive, Input_group* input_group, + Task_token* this_blocker, + Task_token* next_blocker) + : options_(options), symtab_(symtab), layout_(layout), + input_objects_(input_objects), archive_(archive), + input_group_(input_group), this_blocker_(this_blocker), + next_blocker_(next_blocker) + { } + + ~Add_archive_symbols(); + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Add_archive_symbols_locker; + + const General_options& options_; + Symbol_table* symtab_; + Layout* layout_; + Input_objects* input_objects_; + Archive* archive_; + Input_group* input_group_; + Task_token* this_blocker_; + Task_token* next_blocker_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_ARCHIVE_H) diff --git a/gold/common.cc b/gold/common.cc new file mode 100644 index 000000000000..e83219c901f0 --- /dev/null +++ b/gold/common.cc @@ -0,0 +1,210 @@ +// common.cc -- handle common symbols for gold + +#include "gold.h" + +#include <algorithm> + +#include "workqueue.h" +#include "layout.h" +#include "output.h" +#include "symtab.h" +#include "common.h" + +namespace gold +{ + +// Allocate_commons_task methods. + +// This task allocates the common symbols. We need a lock on the +// symbol table. + +Task::Is_runnable_type +Allocate_commons_task::is_runnable(Workqueue*) +{ + if (!this->symtab_lock_->is_writable()) + return IS_LOCKED; + return IS_RUNNABLE; +} + +// Return the locks we hold: one on the symbol table, and one blocker. + +class Allocate_commons_task::Allocate_commons_locker : public Task_locker +{ + public: + Allocate_commons_locker(Task_token& symtab_lock, Task* task, + Task_token& blocker, Workqueue* workqueue) + : symtab_locker_(symtab_lock, task), + blocker_(blocker, workqueue) + { } + + private: + Task_locker_write symtab_locker_; + Task_locker_block blocker_; +}; + +Task_locker* +Allocate_commons_task::locks(Workqueue* workqueue) +{ + return new Allocate_commons_locker(*this->symtab_lock_, this, + *this->blocker_, workqueue); +} + +// Allocate the common symbols. + +void +Allocate_commons_task::run(Workqueue*) +{ + this->symtab_->allocate_commons(this->options_, this->layout_); +} + +// This class is used to sort the common symbol by size. We put the +// larger common symbols first. + +template<int size> +class Sort_commons +{ + public: + Sort_commons(const Symbol_table* symtab) + : symtab_(symtab) + { } + + bool operator()(const Symbol* a, const Symbol* b) const; + + private: + const Symbol_table* symtab_; +}; + +template<int size> +bool +Sort_commons<size>::operator()(const Symbol* pa, const Symbol* pb) const +{ + if (pa == NULL) + return false; + if (pb == NULL) + return true; + + const Symbol_table* symtab = this->symtab_; + const Sized_symbol<size>* psa; + psa = symtab->get_sized_symbol SELECT_SIZE_NAME(size) (pa + SELECT_SIZE(size)); + const Sized_symbol<size>* psb; + psb = symtab->get_sized_symbol SELECT_SIZE_NAME(size) (pb + SELECT_SIZE(size)); + + typename Sized_symbol<size>::Size_type sa = psa->symsize(); + typename Sized_symbol<size>::Size_type sb = psb->symsize(); + if (sa < sb) + return false; + else if (sb > sa) + return true; + + // When the symbols are the same size, we sort them by alignment. + typename Sized_symbol<size>::Value_type va = psa->value(); + typename Sized_symbol<size>::Value_type vb = psb->value(); + if (va < vb) + return false; + else if (vb > va) + return true; + + // Otherwise we stabilize the sort by sorting by name. + return strcmp(psa->name(), psb->name()) < 0; +} + +// Allocate the common symbols. + +void +Symbol_table::allocate_commons(const General_options& options, Layout* layout) +{ + if (this->get_size() == 32) + this->do_allocate_commons<32>(options, layout); + else if (this->get_size() == 64) + this->do_allocate_commons<64>(options, layout); + else + gold_unreachable(); +} + +// Allocated the common symbols, sized version. + +template<int size> +void +Symbol_table::do_allocate_commons(const General_options&, + Layout* layout) +{ + typedef typename Sized_symbol<size>::Value_type Value_type; + typedef typename Sized_symbol<size>::Size_type Size_type; + + // We've kept a list of all the common symbols. But the symbol may + // have been resolved to a defined symbol by now. And it may be a + // forwarder. First remove all non-common symbols. + bool any = false; + uint64_t addralign = 0; + for (Commons_type::iterator p = this->commons_.begin(); + p != this->commons_.end(); + ++p) + { + Symbol* sym = *p; + if (sym->is_forwarder()) + { + sym = this->resolve_forwards(sym); + *p = sym; + } + if (!sym->is_common()) + *p = NULL; + else + { + any = true; + Sized_symbol<size>* ssym; + ssym = this->get_sized_symbol SELECT_SIZE_NAME(size) ( + sym + SELECT_SIZE(size)); + if (ssym->value() > addralign) + addralign = ssym->value(); + } + } + if (!any) + return; + + // Sort the common symbols by size, so that they pack better into + // memory. + std::sort(this->commons_.begin(), this->commons_.end(), + Sort_commons<size>(this)); + + // Place them in a newly allocated .bss section. + + Output_data_space *poc = new Output_data_space(addralign); + + layout->add_output_section_data(".bss", elfcpp::SHT_NOBITS, + elfcpp::SHF_WRITE | elfcpp::SHF_ALLOC, + poc); + + // Allocate them all. + + off_t off = 0; + for (Commons_type::iterator p = this->commons_.begin(); + p != this->commons_.end(); + ++p) + { + Symbol* sym = *p; + if (sym == NULL) + break; + + Sized_symbol<size>* ssym; + ssym = this->get_sized_symbol SELECT_SIZE_NAME(size) (sym + SELECT_SIZE(size)); + + off = align_address(off, ssym->value()); + + Size_type symsize = ssym->symsize(); + ssym->init(ssym->name(), poc, off, symsize, ssym->type(), + ssym->binding(), ssym->visibility(), ssym->nonvis(), + false); + + off += symsize; + } + + poc->set_space_size(off); + + this->commons_.clear(); +} + +} // End namespace gold. diff --git a/gold/common.h b/gold/common.h new file mode 100644 index 000000000000..75237a6a5c52 --- /dev/null +++ b/gold/common.h @@ -0,0 +1,49 @@ +// common.h -- handle common symbols for gold -*- C++ -*- + +#ifndef GOLD_COMMON_H +#define GOLD_COMMON_H + +#include "workqueue.h" + +namespace gold +{ + +class General_options; +class Symbol_table; + +// This task is used to allocate the common symbols. + +class Allocate_commons_task : public Task +{ + public: + Allocate_commons_task(const General_options& options, Symbol_table* symtab, + Layout* layout, Task_token* symtab_lock, + Task_token* blocker) + : options_(options), symtab_(symtab), layout_(layout), + symtab_lock_(symtab_lock), blocker_(blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Allocate_commons_locker; + + const General_options& options_; + Symbol_table* symtab_; + Layout* layout_; + Task_token* symtab_lock_; + Task_token* blocker_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_COMMON_H) diff --git a/gold/config.in b/gold/config.in new file mode 100644 index 000000000000..1d40c66bac36 --- /dev/null +++ b/gold/config.in @@ -0,0 +1,75 @@ +/* config.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#undef ENABLE_NLS + +/* Define to 1 if you have the <ext/hash_map> header file. */ +#undef HAVE_EXT_HASH_MAP + +/* Define to 1 if you have the <ext/hash_set> header file. */ +#undef HAVE_EXT_HASH_SET + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Whether the C++ compiler can call a template member with no arguments */ +#undef HAVE_MEMBER_TEMPLATE_SPECIFICATIONS + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <tr1/unordered_map> header file. */ +#undef HAVE_TR1_UNORDERED_MAP + +/* Define to 1 if you have the <tr1/unordered_set> header file. */ +#undef HAVE_TR1_UNORDERED_SET + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN diff --git a/gold/configure b/gold/configure new file mode 100755 index 000000000000..5d69594d33fb --- /dev/null +++ b/gold/configure @@ -0,0 +1,6715 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="gold.cc" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# 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 YACC RANLIB ac_ct_RANLIB 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. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CXX_set=${CXX+set} +ac_env_CXX_value=$CXX +ac_cv_env_CXX_set=${CXX+set} +ac_cv_env_CXX_value=$CXX +ac_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_env_CXXFLAGS_value=$CXXFLAGS +ac_cv_env_CXXFLAGS_set=${CXXFLAGS+set} +ac_cv_env_CXXFLAGS_value=$CXXFLAGS +ac_env_CXXCPP_set=${CXXCPP+set} +ac_env_CXXCPP_value=$CXXCPP +ac_cv_env_CXXCPP_set=${CXXCPP+set} +ac_cv_env_CXXCPP_value=$CXXCPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --disable-nls do not use Native Language Support + --enable-werror treat compile warnings as errors + --enable-build-warnings enable build-time compiler warnings + --enable-maintainer-mode enable make rules and dependencies not useful + (and sometimes confusing) to the casual installer + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have + headers in a nonstandard directory <include dir> + CXX C++ compiler command + CXXFLAGS C++ compiler flags + CXXCPP C++ preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Make sure we can run config.sub. +$ac_config_sub sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5 +echo "$as_me: error: cannot run $ac_config_sub" >&2;} + { (exit 1); exit 1; }; } + +echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6 +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_build_alias=$build_alias +test -z "$ac_cv_build_alias" && + ac_cv_build_alias=`$ac_config_guess` +test -z "$ac_cv_build_alias" && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$ac_config_sub $ac_cv_build_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6 +build=$ac_cv_build +build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6 +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_host_alias=$host_alias +test -z "$ac_cv_host_alias" && + ac_cv_host_alias=$ac_cv_build_alias +ac_cv_host=`$ac_config_sub $ac_cv_host_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6 +host=$ac_cv_host +host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +echo "$as_me:$LINENO: checking target system type" >&5 +echo $ECHO_N "checking target system type... $ECHO_C" >&6 +if test "${ac_cv_target+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_target_alias=$target_alias +test "x$ac_cv_target_alias" = "x" && + ac_cv_target_alias=$ac_cv_host_alias +ac_cv_target=`$ac_config_sub $ac_cv_target_alias` || + { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_target_alias failed" >&5 +echo "$as_me: error: $ac_config_sub $ac_cv_target_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +echo "$as_me:$LINENO: result: $ac_cv_target" >&5 +echo "${ECHO_T}$ac_cv_target" >&6 +target=$ac_cv_target +target_cpu=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +target_vendor=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +target_os=`echo $ac_cv_target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +am__api_version="1.9" +# 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: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6 +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +test "$program_prefix" != NONE && + program_transform_name="s,^,$program_prefix,;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$,$program_suffix,;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm conftest.sed + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi + +for ac_prog in gawk mawk nawk awk +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_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # 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_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$AWK" && break +done + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE=gold + VERSION=0.1 + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +install_sh=${install_sh-"$am_aux_dir/install-sh"} + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; 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_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # 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_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; 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_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # 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_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":" +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + STRIP=$ac_ct_STRIP +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' + + + + + + + ac_config_headers="$ac_config_headers config.h:config.in" + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; 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_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # 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_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; 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_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # 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_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; 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_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # 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_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; 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_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # 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_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; 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_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +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 + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$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_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # 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_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +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_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # 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_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 + (eval $ac_compiler --version </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5 + (eval $ac_compiler -v </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5 + (eval $ac_compiler -V </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include <stdlib.h> +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" + + ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6 +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi + + +echo "$as_me:$LINENO: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6 +rm -f confinc confmf + +# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval="$enable_dependency_tracking" + +fi; +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + + +depcc="$CC" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6 +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -n "$ac_tool_prefix"; then + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$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_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # 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_CXX="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in $CCC g++ c++ gpp aCC CC cxx cc++ cl FCC KCC RCC xlC_r xlC +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_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # 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_ac_ct_CXX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CXX" && break +done +test -n "$ac_ct_CXX" || ac_ct_CXX="g++" + + CXX=$ac_ct_CXX +fi + + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 + (eval $ac_compiler --version </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5 + (eval $ac_compiler -v </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5 + (eval $ac_compiler -V </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6 +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6 +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +CXXFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cxx_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6 +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include <stdlib.h> +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CXX" am_compiler_list= + +echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6 +if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6 +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +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" + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; 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_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # 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_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; 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_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # 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_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +# 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: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# If we haven't got the data from the intl directory, +# assume NLS is disabled. +USE_NLS=no +LIBINTL= +LIBINTL_DEP= +INCINTL= +XGETTEXT= +GMSGFMT= +POSUB= + +if test -f ../intl/config.intl; then + . ../intl/config.intl +fi +echo "$as_me:$LINENO: checking whether NLS is requested" >&5 +echo $ECHO_N "checking whether NLS is requested... $ECHO_C" >&6 +if test x"$USE_NLS" != xyes; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +cat >>confdefs.h <<\_ACEOF +#define ENABLE_NLS 1 +_ACEOF + + + echo "$as_me:$LINENO: checking for catalogs to be installed" >&5 +echo $ECHO_N "checking for catalogs to be installed... $ECHO_C" >&6 + # Look for .po and .gmo files in the source directory. + CATALOGS= + XLINGUAS= + for cat in $srcdir/po/*.gmo $srcdir/po/*.po; do + # If there aren't any .gmo files the shell will give us the + # literal string "../path/to/srcdir/po/*.gmo" which has to be + # weeded out. + case "$cat" in *\**) + continue;; + esac + # The quadruple backslash is collapsed to a double backslash + # by the backticks, then collapsed again by the double quotes, + # leaving us with one backslash in the sed expression (right + # before the dot that mustn't act as a wildcard). + cat=`echo $cat | sed -e "s!$srcdir/po/!!" -e "s!\\\\.po!.gmo!"` + lang=`echo $cat | sed -e "s!\\\\.gmo!!"` + # The user is allowed to set LINGUAS to a list of languages to + # install catalogs for. If it's empty that means "all of them." + if test "x$LINGUAS" = x; then + CATALOGS="$CATALOGS $cat" + XLINGUAS="$XLINGUAS $lang" + else + case "$LINGUAS" in *$lang*) + CATALOGS="$CATALOGS $cat" + XLINGUAS="$XLINGUAS $lang" + ;; + esac + fi + done + LINGUAS="$XLINGUAS" + echo "$as_me:$LINENO: result: $LINGUAS" >&5 +echo "${ECHO_T}$LINGUAS" >&6 + + + DATADIRNAME=share + + INSTOBJEXT=.mo + + GENCAT=gencat + + CATOBJEXT=.gmo + +fi + + MKINSTALLDIRS= + if test -n "$ac_aux_dir"; then + case "$ac_aux_dir" in + /*) MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" ;; + *) MKINSTALLDIRS="\$(top_builddir)/$ac_aux_dir/mkinstalldirs" ;; + esac + fi + if test -z "$MKINSTALLDIRS"; then + MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" + fi + + + + echo "$as_me:$LINENO: checking whether NLS is requested" >&5 +echo $ECHO_N "checking whether NLS is requested... $ECHO_C" >&6 + # Check whether --enable-nls or --disable-nls was given. +if test "${enable_nls+set}" = set; then + enableval="$enable_nls" + USE_NLS=$enableval +else + USE_NLS=yes +fi; + echo "$as_me:$LINENO: result: $USE_NLS" >&5 +echo "${ECHO_T}$USE_NLS" >&6 + + + + + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "msgfmt", so it can be a program name with args. +set dummy msgfmt; 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_path_MSGFMT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case "$MSGFMT" in + [\\/]* | ?:[\\/]*) + ac_cv_path_MSGFMT="$MSGFMT" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + if $ac_dir/$ac_word --statistics /dev/null >/dev/null 2>&1 && + (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + ac_cv_path_MSGFMT="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_MSGFMT" && ac_cv_path_MSGFMT=":" + ;; +esac +fi +MSGFMT="$ac_cv_path_MSGFMT" +if test "$MSGFMT" != ":"; then + echo "$as_me:$LINENO: result: $MSGFMT" >&5 +echo "${ECHO_T}$MSGFMT" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + # Extract the first word of "gmsgfmt", so it can be a program name with args. +set dummy gmsgfmt; 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_path_GMSGFMT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $GMSGFMT in + [\\/]* | ?:[\\/]*) + ac_cv_path_GMSGFMT="$GMSGFMT" # Let the user override the test with a path. + ;; + *) + 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_path_GMSGFMT="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_path_GMSGFMT" && ac_cv_path_GMSGFMT="$MSGFMT" + ;; +esac +fi +GMSGFMT=$ac_cv_path_GMSGFMT + +if test -n "$GMSGFMT"; then + echo "$as_me:$LINENO: result: $GMSGFMT" >&5 +echo "${ECHO_T}$GMSGFMT" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "xgettext", so it can be a program name with args. +set dummy xgettext; 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_path_XGETTEXT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case "$XGETTEXT" in + [\\/]* | ?:[\\/]*) + ac_cv_path_XGETTEXT="$XGETTEXT" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >/dev/null 2>&1 && + (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + ac_cv_path_XGETTEXT="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_XGETTEXT" && ac_cv_path_XGETTEXT=":" + ;; +esac +fi +XGETTEXT="$ac_cv_path_XGETTEXT" +if test "$XGETTEXT" != ":"; then + echo "$as_me:$LINENO: result: $XGETTEXT" >&5 +echo "${ECHO_T}$XGETTEXT" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + rm -f messages.po + + +# Prepare PATH_SEPARATOR. +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Find out how to test for executable files. Don't use a zero-byte file, +# as systems may use methods other than mode bits to determine executability. +cat >conf$$.file <<_ASEOF +#! /bin/sh +exit 0 +_ASEOF +chmod +x conf$$.file +if test -x conf$$.file >/dev/null 2>&1; then + ac_executable_p="test -x" +else + ac_executable_p="test -f" +fi +rm -f conf$$.file + +# Extract the first word of "msgmerge", so it can be a program name with args. +set dummy msgmerge; 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_path_MSGMERGE+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case "$MSGMERGE" in + [\\/]* | ?:[\\/]*) + ac_cv_path_MSGMERGE="$MSGMERGE" # Let the user override the test with a path. + ;; + *) + ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$ac_save_IFS" + test -z "$ac_dir" && ac_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then + if $ac_dir/$ac_word --update -q /dev/null /dev/null >/dev/null 2>&1; then + ac_cv_path_MSGMERGE="$ac_dir/$ac_word$ac_exec_ext" + break 2 + fi + fi + done + done + IFS="$ac_save_IFS" + test -z "$ac_cv_path_MSGMERGE" && ac_cv_path_MSGMERGE=":" + ;; +esac +fi +MSGMERGE="$ac_cv_path_MSGMERGE" +if test "$MSGMERGE" != ":"; then + echo "$as_me:$LINENO: result: $MSGMERGE" >&5 +echo "${ECHO_T}$MSGMERGE" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + if test "$GMSGFMT" != ":"; then + if $GMSGFMT --statistics /dev/null >/dev/null 2>&1 && + (if $GMSGFMT --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + : ; + else + GMSGFMT=`echo "$GMSGFMT" | sed -e 's,^.*/,,'` + echo "$as_me:$LINENO: result: found $GMSGFMT program is not GNU msgfmt; ignore it" >&5 +echo "${ECHO_T}found $GMSGFMT program is not GNU msgfmt; ignore it" >&6 + GMSGFMT=":" + fi + fi + + if test "$XGETTEXT" != ":"; then + if $XGETTEXT --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >/dev/null 2>&1 && + (if $XGETTEXT --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then + : ; + else + echo "$as_me:$LINENO: result: found xgettext program is not GNU xgettext; ignore it" >&5 +echo "${ECHO_T}found xgettext program is not GNU xgettext; ignore it" >&6 + XGETTEXT=":" + fi + rm -f messages.po + fi + + ac_config_commands="$ac_config_commands default-1" + + + + +echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/param.h> + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/param.h> + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_bigendian=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +# It does not; compile a test program. +if test "$cross_compiling" = yes; then + # try to guess the endianness by grepping values into an object file + ac_cv_c_bigendian=unknown + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; +short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; +void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } +short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; +short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; +void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } +int +main () +{ + _ascii (); _ebcdic (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then + ac_cv_c_bigendian=yes +fi +if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi +fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +case $ac_cv_c_bigendian in + yes) + +cat >>confdefs.h <<\_ACEOF +#define WORDS_BIGENDIAN 1 +_ACEOF + ;; + no) + ;; + *) + { { echo "$as_me:$LINENO: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&5 +echo "$as_me: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} + { (exit 1); exit 1; }; } ;; +esac + + + + + +GCC_WARN_CFLAGS="-W -Wall -Wstrict-prototypes -Wmissing-prototypes" + +# Check whether --enable-werror or --disable-werror was given. +if test "${enable_werror+set}" = set; then + enableval="$enable_werror" + case "${enableval}" in + yes | y) ERROR_ON_WARNING="yes" ;; + no | n) ERROR_ON_WARNING="no" ;; + *) { { echo "$as_me:$LINENO: error: bad value ${enableval} for --enable-werror" >&5 +echo "$as_me: error: bad value ${enableval} for --enable-werror" >&2;} + { (exit 1); exit 1; }; } ;; + esac +fi; + +# Enable -Werror by default when using gcc +if test "${GCC}" = yes -a -z "${ERROR_ON_WARNING}" ; then + ERROR_ON_WARNING=yes +fi + +NO_WERROR= +if test "${ERROR_ON_WARNING}" = yes ; then + GCC_WARN_CFLAGS="$GCC_WARN_CFLAGS -Werror" + NO_WERROR="-Wno-error" +fi + +if test "${GCC}" = yes ; then + WARN_CFLAGS="${GCC_WARN_CFLAGS}" +fi + +# Check whether --enable-build-warnings or --disable-build-warnings was given. +if test "${enable_build_warnings+set}" = set; then + enableval="$enable_build_warnings" + case "${enableval}" in + yes) WARN_CFLAGS="${GCC_WARN_CFLAGS}";; + no) if test "${GCC}" = yes ; then + WARN_CFLAGS="-w" + fi;; + ,*) t=`echo "${enableval}" | sed -e "s/,/ /g"` + WARN_CFLAGS="${GCC_WARN_CFLAGS} ${t}";; + *,) t=`echo "${enableval}" | sed -e "s/,/ /g"` + WARN_CFLAGS="${t} ${GCC_WARN_CFLAGS}";; + *) WARN_CFLAGS=`echo "${enableval}" | sed -e "s/,/ /g"`;; +esac +fi; + +if test x"$silent" != x"yes" && test x"$WARN_CFLAGS" != x""; then + echo "Setting warning flags = $WARN_CFLAGS" 6>&1 +fi + + + + + +WARN_CXXFLAGS=`echo ${WARN_CFLAGS} | sed -e 's/-Wstrict-prototypes//' -e 's/-Wmissing-prototypes//'` + + +LFS_CXXFLAGS="-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" + + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 +echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6 +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CXXCPP=$CXXCPP + +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +echo "$as_me:$LINENO: result: $CXXCPP" >&5 +echo "${ECHO_T}$CXXCPP" >&6 +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=cc +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + +for ac_header in tr1/unordered_set tr1/unordered_map +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_header in ext/hash_map ext/hash_set +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_cxx_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_cxx_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +cat >conftest.$ac_ext <<_ACEOF + +class c { public: template<int i> void fn(); }; +template<int i> void foo(c cv) { cv.fn<i>(); } +template void foo<1>(c cv); +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_cxx_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MEMBER_TEMPLATE_SPECIFICATIONS +_ACEOF + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5 +echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6 + # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then + enableval="$enable_maintainer_mode" + USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi; + echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5 +echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6 + + +if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + + ac_config_files="$ac_config_files Makefile testsuite/Makefile po/Makefile.in:po/Make-in" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to <bug-autoconf@gnu.org>." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS section. +# + +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" +# Capture the value of obsolete ALL_LINGUAS because we need it to compute + # POFILES, GMOFILES, UPDATEPOFILES, DUMMYPOFILES, CATALOGS. But hide it + # from automake. + eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"' + # Capture the value of LINGUAS because we need it to compute CATALOGS. + LINGUAS="${LINGUAS-%UNSET%}" + + +_ACEOF + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "testsuite/Makefile" ) CONFIG_FILES="$CONFIG_FILES testsuite/Makefile" ;; + "po/Makefile.in" ) CONFIG_FILES="$CONFIG_FILES po/Makefile.in:po/Make-in" ;; + "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "default-1" ) CONFIG_COMMANDS="$CONFIG_COMMANDS default-1" ;; + "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h:config.in" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@build@,$build,;t t +s,@build_cpu@,$build_cpu,;t t +s,@build_vendor@,$build_vendor,;t t +s,@build_os@,$build_os,;t t +s,@host@,$host,;t t +s,@host_cpu@,$host_cpu,;t t +s,@host_vendor@,$host_vendor,;t t +s,@host_os@,$host_os,;t t +s,@target@,$target,;t t +s,@target_cpu@,$target_cpu,;t t +s,@target_vendor@,$target_vendor,;t t +s,@target_os@,$target_os,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@CYGPATH_W@,$CYGPATH_W,;t t +s,@PACKAGE@,$PACKAGE,;t t +s,@VERSION@,$VERSION,;t t +s,@ACLOCAL@,$ACLOCAL,;t t +s,@AUTOCONF@,$AUTOCONF,;t t +s,@AUTOMAKE@,$AUTOMAKE,;t t +s,@AUTOHEADER@,$AUTOHEADER,;t t +s,@MAKEINFO@,$MAKEINFO,;t t +s,@install_sh@,$install_sh,;t t +s,@STRIP@,$STRIP,;t t +s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t +s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t +s,@mkdir_p@,$mkdir_p,;t t +s,@AWK@,$AWK,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@am__leading_dot@,$am__leading_dot,;t t +s,@AMTAR@,$AMTAR,;t t +s,@am__tar@,$am__tar,;t t +s,@am__untar@,$am__untar,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@DEPDIR@,$DEPDIR,;t t +s,@am__include@,$am__include,;t t +s,@am__quote@,$am__quote,;t t +s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t +s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t +s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t +s,@CCDEPMODE@,$CCDEPMODE,;t t +s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t +s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t +s,@CXX@,$CXX,;t t +s,@CXXFLAGS@,$CXXFLAGS,;t t +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,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@USE_NLS@,$USE_NLS,;t t +s,@LIBINTL@,$LIBINTL,;t t +s,@LIBINTL_DEP@,$LIBINTL_DEP,;t t +s,@INCINTL@,$INCINTL,;t t +s,@XGETTEXT@,$XGETTEXT,;t t +s,@GMSGFMT@,$GMSGFMT,;t t +s,@POSUB@,$POSUB,;t t +s,@CATALOGS@,$CATALOGS,;t t +s,@DATADIRNAME@,$DATADIRNAME,;t t +s,@INSTOBJEXT@,$INSTOBJEXT,;t t +s,@GENCAT@,$GENCAT,;t t +s,@CATOBJEXT@,$CATOBJEXT,;t t +s,@MKINSTALLDIRS@,$MKINSTALLDIRS,;t t +s,@MSGFMT@,$MSGFMT,;t t +s,@MSGMERGE@,$MSGMERGE,;t t +s,@WARN_CFLAGS@,$WARN_CFLAGS,;t t +s,@NO_WERROR@,$NO_WERROR,;t t +s,@WARN_CXXFLAGS@,$WARN_CXXFLAGS,;t t +s,@LFS_CXXFLAGS@,$LFS_CXXFLAGS,;t t +s,@CXXCPP@,$CXXCPP,;t t +s,@EGREP@,$EGREP,;t t +s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t +s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t +s,@MAINT@,$MAINT,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +# Compute $ac_file's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $ac_file | $ac_file:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null || +$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X$ac_file : 'X\(//\)[^/]' \| \ + X$ac_file : 'X\(//\)$' \| \ + X$ac_file : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X$ac_file | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'`/stamp-h$_am_stamp_count +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_COMMANDS section. +# +for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue + ac_dest=`echo "$ac_file" | sed 's,:.*,,'` + ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_dir=`(dirname "$ac_dest") 2>/dev/null || +$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_dest" : 'X\(//\)[^/]' \| \ + X"$ac_dest" : 'X\(//\)$' \| \ + X"$ac_dest" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_dest" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + { echo "$as_me:$LINENO: executing $ac_dest commands" >&5 +echo "$as_me: executing $ac_dest commands" >&6;} + case $ac_dest in + depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`(dirname "$mf") 2>/dev/null || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`(dirname "$file") 2>/dev/null || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p $dirpart/$fdir + else + as_dir=$dirpart/$fdir + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5 +echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;} + { (exit 1); exit 1; }; }; } + + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + default-1 ) + for ac_file in $CONFIG_FILES; do + # Support "outfile[:infile[:infile...]]" + case "$ac_file" in + *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + esac + # PO directories have a Makefile.in generated from Makefile.in.in. + case "$ac_file" in */Makefile.in) + # Adjust a relative srcdir. + ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` + ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`" + ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` + # In autoconf-2.13 it is called $ac_given_srcdir. + # In autoconf-2.50 it is called $srcdir. + test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" + case "$ac_given_srcdir" in + .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; + /*) top_srcdir="$ac_given_srcdir" ;; + *) top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then + rm -f "$ac_dir/POTFILES" + test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES" + cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES" + POMAKEFILEDEPS="POTFILES.in" + # ALL_LINGUAS, POFILES, GMOFILES, UPDATEPOFILES, DUMMYPOFILES depend + # on $ac_dir but don't depend on user-specified configuration + # parameters. + if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then + # The LINGUAS file contains the set of available languages. + if test -n "$OBSOLETE_ALL_LINGUAS"; then + test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete" + fi + ALL_LINGUAS_=`sed -e "/^#/d" "$ac_given_srcdir/$ac_dir/LINGUAS"` + # Hide the ALL_LINGUAS assigment from automake. + eval 'ALL_LINGUAS''=$ALL_LINGUAS_' + POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS" + else + # The set of available languages was given in configure.in. + eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS' + fi + case "$ac_given_srcdir" in + .) srcdirpre= ;; + *) srcdirpre='$(srcdir)/' ;; + esac + POFILES= + GMOFILES= + UPDATEPOFILES= + DUMMYPOFILES= + for lang in $ALL_LINGUAS; do + POFILES="$POFILES $srcdirpre$lang.po" + GMOFILES="$GMOFILES $srcdirpre$lang.gmo" + UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" + DUMMYPOFILES="$DUMMYPOFILES $lang.nop" + done + # CATALOGS depends on both $ac_dir and the user's LINGUAS + # environment variable. + INST_LINGUAS= + if test -n "$ALL_LINGUAS"; then + for presentlang in $ALL_LINGUAS; do + useit=no + if test "%UNSET%" != "$LINGUAS"; then + desiredlanguages="$LINGUAS" + else + desiredlanguages="$ALL_LINGUAS" + fi + for desiredlang in $desiredlanguages; do + # Use the presentlang catalog if desiredlang is + # a. equal to presentlang, or + # b. a variant of presentlang (because in this case, + # presentlang can be used as a fallback for messages + # which are not translated in the desiredlang catalog). + case "$desiredlang" in + "$presentlang"*) useit=yes;; + esac + done + if test $useit = yes; then + INST_LINGUAS="$INST_LINGUAS $presentlang" + fi + done + fi + CATALOGS= + if test -n "$INST_LINGUAS"; then + for lang in $INST_LINGUAS; do + CATALOGS="$CATALOGS $lang.gmo" + done + fi + test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile" + sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile" + for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do + if test -f "$f"; then + case "$f" in + *.orig | *.bak | *~) ;; + *) cat "$f" >> "$ac_dir/Makefile" ;; + esac + fi + done + fi + ;; + esac + done ;; + esac +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/gold/configure.ac b/gold/configure.ac new file mode 100644 index 000000000000..5cbaf2c455d9 --- /dev/null +++ b/gold/configure.ac @@ -0,0 +1,52 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT +AC_CONFIG_SRCDIR([gold.cc]) + +AC_CANONICAL_TARGET + +AM_INIT_AUTOMAKE(gold, 0.1) + +AM_CONFIG_HEADER(config.h:config.in) + +AC_PROG_CC +AC_PROG_CXX +AC_PROG_YACC +AC_PROG_RANLIB +AC_PROG_INSTALL +ZW_GNU_GETTEXT_SISTER_DIR +AM_PO_SUBDIRS + +AC_C_BIGENDIAN + +AC_EXEEXT + +AM_BINUTILS_WARNINGS + +WARN_CXXFLAGS=`echo ${WARN_CFLAGS} | sed -e 's/-Wstrict-prototypes//' -e 's/-Wmissing-prototypes//'` +AC_SUBST(WARN_CXXFLAGS) + +dnl Force support for large files by default. This may need to be +dnl host dependent. If build == host, we can check getconf LFS_CFLAGS. +LFS_CXXFLAGS="-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" +AC_SUBST(LFS_CXXFLAGS) + +AC_LANG_PUSH(C++) + +AC_CHECK_HEADERS(tr1/unordered_set tr1/unordered_map) +AC_CHECK_HEADERS(ext/hash_map ext/hash_set) + +dnl Test whether the compiler can specify a member templates to call. +AC_COMPILE_IFELSE([ +class c { public: template<int i> void fn(); }; +template<int i> void foo(c cv) { cv.fn<i>(); } +template void foo<1>(c cv);], +[AC_DEFINE(HAVE_MEMBER_TEMPLATE_SPECIFICATIONS, [], + [Whether the C++ compiler can call a template member with no arguments])]) + +AC_LANG_POP(C++) + +AM_MAINTAINER_MODE + +AC_OUTPUT(Makefile testsuite/Makefile po/Makefile.in:po/Make-in) diff --git a/gold/defstd.cc b/gold/defstd.cc new file mode 100644 index 000000000000..50a977a3b907 --- /dev/null +++ b/gold/defstd.cc @@ -0,0 +1,239 @@ +// defstd.cc -- define standard symbols for gold. + +#include "gold.h" + +#include "symtab.h" +#include "defstd.h" + +// This is a simple file which defines the standard symbols like +// "_end". + +namespace +{ + +using namespace gold; + +const Define_symbol_in_section in_section[] = +{ + { + "__preinit_array_start", // name + ".preinit_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + false, // offset_is_from_end + true // only_if_ref + }, + { + "__preinit_array_end", // name + ".preinit_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + true, // offset_is_from_end + true // only_if_ref + }, + { + "__init_array_start", // name + ".init_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + false, // offset_is_from_end + true // only_if_ref + }, + { + "__init_array_end", // name + ".init_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + true, // offset_is_from_end + true // only_if_ref + }, + { + "__fini_array_start", // name + ".fini_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + false, // offset_is_from_end + true // only_if_ref + }, + { + "__fini_array_end", // name + ".fini_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + true, // offset_is_from_end + true // only_if_ref + } +}; + +const int in_section_count = sizeof in_section / sizeof in_section[0]; + +const Define_symbol_in_segment in_segment[] = +{ + { + "__executable_start", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF(0), // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_START, // offset_from_base + true // only_if_ref + }, + { + "etext", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_X, // segment_flags_set + elfcpp::PF_W, // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_END, // offset_from_base + true // only_if_ref + }, + { + "_etext", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_X, // segment_flags_set + elfcpp::PF_W, // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_END, // offset_from_base + true // only_if_ref + }, + { + "__etext", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_X, // segment_flags_set + elfcpp::PF_W, // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_END, // offset_from_base + true // only_if_ref + }, + { + "_edata", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_X, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_BSS, // offset_from_base + false // only_if_ref + }, + { + "edata", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_X, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_BSS, // offset_from_base + true // only_if_ref + }, + { + "__bss_start", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_X, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_BSS, // offset_from_base + false // only_if_ref + }, + { + "_end", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_X, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_START, // offset_from_base + false // only_if_ref + }, + { + "end", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_X, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_START, // offset_from_base + false // only_if_ref + } +}; + +const int in_segment_count = sizeof in_segment / sizeof in_segment[0]; + +} // End anonymous namespace. + +namespace gold +{ + +void +define_standard_symbols(Symbol_table* symtab, const Layout* layout, + Target* target) +{ + symtab->define_symbols(layout, target, in_section_count, in_section); + symtab->define_symbols(layout, target, in_segment_count, in_segment); +} + +} // End namespace gold. diff --git a/gold/defstd.h b/gold/defstd.h new file mode 100644 index 000000000000..f578b49d76f2 --- /dev/null +++ b/gold/defstd.h @@ -0,0 +1,16 @@ +// defstd.h -- define standard symbols for gold -*- C++ -*- + +#ifndef GOLD_DEFSTD_H +#define GOLD_DEFSTD_H + +#include "symtab.h" + +namespace gold +{ + +extern void +define_standard_symbols(Symbol_table*, const Layout*, Target*); + +} // End namespace gold. + +#endif // !defined(GOLD_DEFSTD_H) diff --git a/gold/dirsearch.cc b/gold/dirsearch.cc new file mode 100644 index 000000000000..d1298d81757d --- /dev/null +++ b/gold/dirsearch.cc @@ -0,0 +1,236 @@ +// dirsearch.cc -- directory searching for gold + +#include "gold.h" + +#include <cerrno> +#include <sys/types.h> +#include <dirent.h> + +#include "gold-threads.h" +#include "dirsearch.h" + +namespace +{ + +// Read all the files in a directory. + +class Dir_cache +{ + public: + Dir_cache(const char* dirname) + : dirname_(dirname), files_() + { } + + // Read the files in the directory. + void read_files(); + + // Return whether a file (a base name) is present in the directory. + bool find(const std::string&) const; + + private: + // We can not copy this class. + Dir_cache(const Dir_cache&); + Dir_cache& operator=(const Dir_cache&); + + const char* dirname_; + Unordered_set<std::string> files_; +}; + +void +Dir_cache::read_files() +{ + DIR* d = opendir(this->dirname_); + if (d == NULL) + { + // We ignore directories which do not exist. + if (errno == ENOENT) + return; + + char *s = NULL; + if (asprintf(&s, _("can not read directory %s"), this->dirname_) < 0) + gold::gold_nomem(); + gold::gold_fatal(s, true); + } + + dirent* de; + while ((de = readdir(d)) != NULL) + this->files_.insert(std::string(de->d_name)); + + if (closedir(d) != 0) + gold::gold_fatal("closedir failed", true); +} + +bool +Dir_cache::find(const std::string& basename) const +{ + return this->files_.find(basename) != this->files_.end(); +} + +// A mapping from directory names to caches. A lock permits +// concurrent update. There is no lock for read operations--some +// other mechanism must be used to prevent reads from conflicting with +// writes. + +class Dir_caches +{ + public: + Dir_caches() + : lock_(), caches_() + { } + + ~Dir_caches(); + + // Add a cache for a directory. + void add(const char*); + + // Look up a directory in the cache. This much be locked against + // calls to Add. + Dir_cache* lookup(const char*) const; + + private: + // We can not copy this class. + Dir_caches(const Dir_caches&); + Dir_caches& operator=(const Dir_caches&); + + typedef Unordered_map<const char*, Dir_cache*> Cache_hash; + + gold::Lock lock_; + Cache_hash caches_; +}; + +Dir_caches::~Dir_caches() +{ + for (Cache_hash::iterator p = this->caches_.begin(); + p != this->caches_.end(); + ++p) + delete p->second; +} + +void +Dir_caches::add(const char* dirname) +{ + { + gold::Hold_lock hl(this->lock_); + if (this->lookup(dirname) != NULL) + return; + } + + Dir_cache* cache = new Dir_cache(dirname); + + cache->read_files(); + + { + gold::Hold_lock hl(this->lock_); + + std::pair<const char*, Dir_cache*> v(dirname, cache); + std::pair<Cache_hash::iterator, bool> p = this->caches_.insert(v); + gold_assert(p.second); + } +} + +Dir_cache* +Dir_caches::lookup(const char* dirname) const +{ + Cache_hash::const_iterator p = this->caches_.find(dirname); + if (p == this->caches_.end()) + return NULL; + return p->second; +} + +// The caches. + +Dir_caches caches; + +// A Task to read the directory. + +class Dir_cache_task : public gold::Task +{ + public: + Dir_cache_task(const char* dir, gold::Task_token& token) + : dir_(dir), token_(token) + { } + + Is_runnable_type is_runnable(gold::Workqueue*); + + gold::Task_locker* locks(gold::Workqueue*); + + void run(gold::Workqueue*); + + private: + const char* dir_; + gold::Task_token& token_; +}; + +// We can always run the task to read the directory. + +gold::Task::Is_runnable_type +Dir_cache_task::is_runnable(gold::Workqueue*) +{ + return IS_RUNNABLE; +} + +// Return the locks to hold. We use a blocker lock to prevent file +// lookups from starting until the directory contents have been read. + +gold::Task_locker* +Dir_cache_task::locks(gold::Workqueue* workqueue) +{ + return new gold::Task_locker_block(this->token_, workqueue); +} + +// Run the task--read the directory contents. + +void +Dir_cache_task::run(gold::Workqueue*) +{ + caches.add(this->dir_); +} + +} + +namespace gold +{ + +Dirsearch::Dirsearch() + : directories_(), token_() +{ +} + +void +Dirsearch::add(Workqueue* workqueue, const char* d) +{ + this->directories_.push_back(d); + this->token_.add_blocker(); + workqueue->queue(new Dir_cache_task(d, this->token_)); +} + +void +Dirsearch::add(Workqueue* workqueue, const General_options::Dir_list& list) +{ + for (General_options::Dir_list::const_iterator p = list.begin(); + p != list.end(); + ++p) + this->add(workqueue, *p); +} + +std::string +Dirsearch::find(const std::string& n1, const std::string& n2) const +{ + gold_assert(!this->token_.is_blocked()); + + for (std::list<const char*>::const_iterator p = this->directories_.begin(); + p != this->directories_.end(); + ++p) + { + Dir_cache* pdc = caches.lookup(*p); + gold_assert(pdc != NULL); + if (pdc->find(n1)) + return std::string(*p) + '/' + n1; + if (!n2.empty() && pdc->find(n2)) + return std::string(*p) + '/' + n2; + } + + return std::string(); +} + +} // End namespace gold. diff --git a/gold/dirsearch.h b/gold/dirsearch.h new file mode 100644 index 000000000000..8b6ba59bf583 --- /dev/null +++ b/gold/dirsearch.h @@ -0,0 +1,57 @@ +// dirsearch.h -- directory searching for gold -*- C++ -*- + +#ifndef GOLD_DIRSEARCH_H +#define GOLD_DIRSEARCH_H + +#include <string> +#include <list> + +#include "workqueue.h" + +namespace gold +{ + +class General_options; + +// A simple interface to manage directories to be searched for +// libraries. + +class Dirsearch +{ + public: + Dirsearch(); + + // Add a directory to the search path. + void + add(Workqueue*, const char*); + + // Add a list of directories to the search path. + void + add(Workqueue*, const General_options::Dir_list&); + + // Search for a file, giving one or two names to search for (the + // second one may be empty). Return a full path name for the file, + // or the empty string if it could not be found. This may only be + // called if the token is not blocked. + std::string + find(const std::string&, const std::string& n2 = std::string()) const; + + // Return a reference to the blocker token which controls access. + const Task_token& + token() const + { return this->token_; } + + private: + // We can not copy this class. + Dirsearch(const Dirsearch&); + Dirsearch& operator=(const Dirsearch&); + + // Directories to search. + std::list<const char*> directories_; + // Blocker token to control access from tasks. + Task_token token_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_DIRSEARCH_H) diff --git a/gold/dynobj.cc b/gold/dynobj.cc new file mode 100644 index 000000000000..1bd5a85e4392 --- /dev/null +++ b/gold/dynobj.cc @@ -0,0 +1,1647 @@ +// dynobj.cc -- dynamic object support for gold + +#include "gold.h" + +#include <vector> +#include <cstring> + +#include "elfcpp.h" +#include "symtab.h" +#include "dynobj.h" + +namespace gold +{ + +// Class Dynobj. + +// Return the string to use in a DT_NEEDED entry. + +const char* +Dynobj::soname() const +{ + if (!this->soname_.empty()) + return this->soname_.c_str(); + return this->name().c_str(); +} + +// 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) +{ +} + +// 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); + + gold_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 the soname field 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); + gold_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->set_soname_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); + gold_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 +{ + if (ndx >= version_map->size()) + version_map->resize(ndx + 1); + 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; +} + +// Add mappings for the version definitions to VERSION_MAP. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::make_verdef_map( + Read_symbols_data* sd, + Version_map* version_map) const +{ + if (sd->verdef == NULL) + return; + + const char* names = reinterpret_cast<const char*>(sd->symbol_names->data()); + off_t names_size = sd->symbol_names_size; + + 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); + + if (verdef.get_vd_version() != elfcpp::VER_DEF_CURRENT) + { + fprintf(stderr, _("%s: %s: unexpected verdef version %u\n"), + program_name, this->name().c_str(), verdef.get_vd_version()); + gold_exit(false); + } + + const unsigned int vd_ndx = verdef.get_vd_ndx(); + + // The GNU linker clears the VERSYM_HIDDEN bit. I'm not + // sure why. + + // The first Verdaux holds the name of this version. Subsequent + // ones are versions that this one depends upon, which we don't + // care about here. + 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, 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; + } +} + +// Add mappings for the required versions to VERSION_MAP. + +template<int size, bool big_endian> +void +Sized_dynobj<size, big_endian>::make_verneed_map( + Read_symbols_data* sd, + Version_map* version_map) const +{ + if (sd->verneed == NULL) + return; + + const char* names = reinterpret_cast<const char*>(sd->symbol_names->data()); + off_t names_size = sd->symbol_names_size; + + const unsigned char* pverneed = sd->verneed->data(); + const 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); + + if (verneed.get_vn_version() != elfcpp::VER_NEED_CURRENT) + { + fprintf(stderr, _("%s: %s: unexpected verneed version %u\n"), + program_name, this->name().c_str(), + verneed.get_vn_version()); + gold_exit(false); + } + + 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_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); + + 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; + } +} + +// 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; + + // A guess at the maximum version number we will see. If this is + // wrong we will be less efficient but still correct. + version_map->reserve(sd->verdef_info + sd->verneed_info * 10); + + this->make_verdef_map(sd, version_map); + this->make_verneed_map(sd, version_map); +} + +// 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) + { + gold_assert(sd->symbol_names == NULL); + gold_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; + } +} + +// Given a vector of hash codes, compute the number of hash buckets to +// use. + +unsigned int +Dynobj::compute_bucket_count(const std::vector<uint32_t>& hashcodes, + bool for_gnu_hash_table) +{ + // FIXME: Implement optional hash table optimization. + + // Array used to determine the number of hash table buckets to use + // based on the number of symbols there are. If there are fewer + // than 3 symbols we use 1 bucket, fewer than 17 symbols we use 3 + // buckets, fewer than 37 we use 17 buckets, and so forth. We never + // use more than 32771 buckets. This is straight from the old GNU + // linker. + static const unsigned int buckets[] = + { + 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209, + 16411, 32771 + }; + const int buckets_count = sizeof buckets / sizeof buckets[0]; + + unsigned int symcount = hashcodes.size(); + unsigned int ret = 1; + for (int i = 0; i < buckets_count; ++i) + { + if (symcount < buckets[i]) + break; + ret = buckets[i]; + } + + if (for_gnu_hash_table && ret < 2) + ret = 2; + + return ret; +} + +// The standard ELF hash function. This hash function must not +// change, as the dynamic linker uses it also. + +uint32_t +Dynobj::elf_hash(const char* name) +{ + const unsigned char* nameu = reinterpret_cast<const unsigned char*>(name); + uint32_t h = 0; + unsigned char c; + while ((c = *nameu++) != '\0') + { + h = (h << 4) + c; + uint32_t g = h & 0xf0000000; + if (g != 0) + { + h ^= g >> 24; + // The ELF ABI says h &= ~g, but using xor is equivalent in + // this case (since g was set from h) and may save one + // instruction. + h ^= g; + } + } + return h; +} + +// Create a standard ELF hash table, setting *PPHASH and *PHASHLEN. +// DYNSYMS is a vector with all the global dynamic symbols. +// LOCAL_DYNSYM_COUNT is the number of local symbols in the dynamic +// symbol table. + +void +Dynobj::create_elf_hash_table(const Target* target, + const std::vector<Symbol*>& dynsyms, + unsigned int local_dynsym_count, + unsigned char** pphash, + unsigned int* phashlen) +{ + unsigned int dynsym_count = dynsyms.size(); + + // Get the hash values for all the symbols. + std::vector<uint32_t> dynsym_hashvals(dynsym_count); + for (unsigned int i = 0; i < dynsym_count; ++i) + dynsym_hashvals[i] = Dynobj::elf_hash(dynsyms[i]->name()); + + const unsigned int bucketcount = + Dynobj::compute_bucket_count(dynsym_hashvals, false); + + std::vector<uint32_t> bucket(bucketcount); + std::vector<uint32_t> chain(local_dynsym_count + dynsym_count); + + for (unsigned int i = 0; i < dynsym_count; ++i) + { + unsigned int dynsym_index = dynsyms[i]->dynsym_index(); + unsigned int bucketpos = dynsym_hashvals[i] % bucketcount; + chain[dynsym_index] = bucket[bucketpos]; + bucket[bucketpos] = dynsym_index; + } + + unsigned int hashlen = ((2 + + bucketcount + + local_dynsym_count + + dynsym_count) + * 4); + unsigned char* phash = new unsigned char[hashlen]; + + if (target->is_big_endian()) + Dynobj::sized_create_elf_hash_table<true>(bucket, chain, phash, hashlen); + else + Dynobj::sized_create_elf_hash_table<false>(bucket, chain, phash, hashlen); + + *pphash = phash; + *phashlen = hashlen; +} + +// Fill in an ELF hash table. + +template<bool big_endian> +void +Dynobj::sized_create_elf_hash_table(const std::vector<uint32_t>& bucket, + const std::vector<uint32_t>& chain, + unsigned char* phash, + unsigned int hashlen) +{ + unsigned char* p = phash; + + const unsigned int bucketcount = bucket.size(); + const unsigned int chaincount = chain.size(); + + elfcpp::Swap<32, big_endian>::writeval(p, bucketcount); + p += 4; + elfcpp::Swap<32, big_endian>::writeval(p, chaincount); + p += 4; + + for (unsigned int i = 0; i < bucketcount; ++i) + { + elfcpp::Swap<32, big_endian>::writeval(p, bucket[i]); + p += 4; + } + + for (unsigned int i = 0; i < chaincount; ++i) + { + elfcpp::Swap<32, big_endian>::writeval(p, chain[i]); + p += 4; + } + + gold_assert(static_cast<unsigned int>(p - phash) == hashlen); +} + +// The hash function used for the GNU hash table. This hash function +// must not change, as the dynamic linker uses it also. + +uint32_t +Dynobj::gnu_hash(const char* name) +{ + const unsigned char* nameu = reinterpret_cast<const unsigned char*>(name); + uint32_t h = 5381; + unsigned char c; + while ((c = *nameu++) != '\0') + h = (h << 5) + h + c; + return h; +} + +// Create a GNU hash table, setting *PPHASH and *PHASHLEN. GNU hash +// tables are an extension to ELF which are recognized by the GNU +// dynamic linker. They are referenced using dynamic tag DT_GNU_HASH. +// TARGET is the target. DYNSYMS is a vector with all the global +// symbols which will be going into the dynamic symbol table. +// LOCAL_DYNSYM_COUNT is the number of local symbols in the dynamic +// symbol table. + +void +Dynobj::create_gnu_hash_table(const Target* target, + const std::vector<Symbol*>& dynsyms, + unsigned int local_dynsym_count, + unsigned char** pphash, + unsigned int* phashlen) +{ + const unsigned int count = dynsyms.size(); + + // Sort the dynamic symbols into two vectors. Symbols which we do + // not want to put into the hash table we store into + // UNHASHED_DYNSYMS. Symbols which we do want to store we put into + // HASHED_DYNSYMS. DYNSYM_HASHVALS is parallel to HASHED_DYNSYMS, + // and records the hash codes. + + std::vector<Symbol*> unhashed_dynsyms; + unhashed_dynsyms.reserve(count); + + std::vector<Symbol*> hashed_dynsyms; + hashed_dynsyms.reserve(count); + + std::vector<uint32_t> dynsym_hashvals; + dynsym_hashvals.reserve(count); + + for (unsigned int i = 0; i < count; ++i) + { + Symbol* sym = dynsyms[i]; + + // FIXME: Should put on unhashed_dynsyms if the symbol is + // hidden. + if (sym->is_undefined()) + unhashed_dynsyms.push_back(sym); + else + { + hashed_dynsyms.push_back(sym); + dynsym_hashvals.push_back(Dynobj::gnu_hash(sym->name())); + } + } + + // Put the unhashed symbols at the start of the global portion of + // the dynamic symbol table. + const unsigned int unhashed_count = unhashed_dynsyms.size(); + unsigned int unhashed_dynsym_index = local_dynsym_count; + for (unsigned int i = 0; i < unhashed_count; ++i) + { + unhashed_dynsyms[i]->set_dynsym_index(unhashed_dynsym_index); + ++unhashed_dynsym_index; + } + + // For the actual data generation we call out to a templatized + // function. + int size = target->get_size(); + bool big_endian = target->is_big_endian(); + if (size == 32) + { + if (big_endian) + Dynobj::sized_create_gnu_hash_table<32, true>(hashed_dynsyms, + dynsym_hashvals, + unhashed_dynsym_index, + pphash, + phashlen); + else + Dynobj::sized_create_gnu_hash_table<32, false>(hashed_dynsyms, + dynsym_hashvals, + unhashed_dynsym_index, + pphash, + phashlen); + } + else if (size == 64) + { + if (big_endian) + Dynobj::sized_create_gnu_hash_table<64, true>(hashed_dynsyms, + dynsym_hashvals, + unhashed_dynsym_index, + pphash, + phashlen); + else + Dynobj::sized_create_gnu_hash_table<64, false>(hashed_dynsyms, + dynsym_hashvals, + unhashed_dynsym_index, + pphash, + phashlen); + } + else + gold_unreachable(); +} + +// Create the actual data for a GNU hash table. This is just a copy +// of the code from the old GNU linker. + +template<int size, bool big_endian> +void +Dynobj::sized_create_gnu_hash_table( + const std::vector<Symbol*>& hashed_dynsyms, + const std::vector<uint32_t>& dynsym_hashvals, + unsigned int unhashed_dynsym_count, + unsigned char** pphash, + unsigned int* phashlen) +{ + if (hashed_dynsyms.empty()) + { + // Special case for the empty hash table. + unsigned int hashlen = 5 * 4 + size / 8; + unsigned char* phash = new unsigned char[hashlen]; + // One empty bucket. + elfcpp::Swap<32, big_endian>::writeval(phash, 1); + // Symbol index above unhashed symbols. + elfcpp::Swap<32, big_endian>::writeval(phash + 4, unhashed_dynsym_count); + // One word for bitmask. + elfcpp::Swap<32, big_endian>::writeval(phash + 8, 1); + // Only bloom filter. + elfcpp::Swap<32, big_endian>::writeval(phash + 12, 0); + // No valid hashes. + elfcpp::Swap<size, big_endian>::writeval(phash + 16, 0); + // No hashes in only bucket. + elfcpp::Swap<32, big_endian>::writeval(phash + 16 + size / 8, 0); + + *phashlen = hashlen; + *pphash = phash; + + return; + } + + const unsigned int bucketcount = + Dynobj::compute_bucket_count(dynsym_hashvals, true); + + const unsigned int nsyms = hashed_dynsyms.size(); + + uint32_t maskbitslog2 = 1; + uint32_t x = nsyms >> 1; + while (x != 0) + { + ++maskbitslog2; + x >>= 1; + } + if (maskbitslog2 < 3) + maskbitslog2 = 5; + else if (((1U << (maskbitslog2 - 2)) & nsyms) != 0) + maskbitslog2 += 3; + else + maskbitslog2 += 2; + + uint32_t shift1; + if (size == 32) + shift1 = 5; + else + { + if (maskbitslog2 == 5) + maskbitslog2 = 6; + shift1 = 6; + } + uint32_t mask = (1U << shift1) - 1U; + uint32_t shift2 = maskbitslog2; + uint32_t maskbits = 1U << maskbitslog2; + uint32_t maskwords = 1U << (maskbitslog2 - shift1); + + typedef typename elfcpp::Elf_types<size>::Elf_WXword Word; + std::vector<Word> bitmask(maskwords); + std::vector<uint32_t> counts(bucketcount); + std::vector<uint32_t> indx(bucketcount); + uint32_t symindx = unhashed_dynsym_count; + + // Count the number of times each hash bucket is used. + for (unsigned int i = 0; i < nsyms; ++i) + ++counts[dynsym_hashvals[i] % bucketcount]; + + unsigned int cnt = symindx; + for (unsigned int i = 0; i < bucketcount; ++i) + { + indx[i] = cnt; + cnt += counts[i]; + } + + unsigned int hashlen = (4 + bucketcount + nsyms) * 4; + hashlen += maskbits / 8; + unsigned char* phash = new unsigned char[hashlen]; + + elfcpp::Swap<32, big_endian>::writeval(phash, bucketcount); + elfcpp::Swap<32, big_endian>::writeval(phash + 4, symindx); + elfcpp::Swap<32, big_endian>::writeval(phash + 8, maskwords); + elfcpp::Swap<32, big_endian>::writeval(phash + 12, shift2); + + unsigned char* p = phash + 16 + maskbits / 8; + for (unsigned int i = 0; i < bucketcount; ++i) + { + if (counts[i] == 0) + elfcpp::Swap<32, big_endian>::writeval(p, 0); + else + elfcpp::Swap<32, big_endian>::writeval(p, indx[i]); + p += 4; + } + + for (unsigned int i = 0; i < nsyms; ++i) + { + Symbol* sym = hashed_dynsyms[i]; + uint32_t hashval = dynsym_hashvals[i]; + + unsigned int bucket = hashval % bucketcount; + unsigned int val = ((hashval >> shift1) + & ((maskbits >> shift1) - 1)); + bitmask[val] |= (static_cast<Word>(1U)) << (hashval & mask); + bitmask[val] |= (static_cast<Word>(1U)) << ((hashval >> shift2) & mask); + val = hashval & ~ 1U; + if (counts[bucket] == 1) + { + // Last element terminates the chain. + val |= 1; + } + elfcpp::Swap<32, big_endian>::writeval(p + (indx[bucket] - symindx) * 4, + val); + --counts[bucket]; + + sym->set_dynsym_index(indx[bucket]); + ++indx[bucket]; + } + + p = phash + 16; + for (unsigned int i = 0; i < maskwords; ++i) + { + elfcpp::Swap<size, big_endian>::writeval(p, bitmask[i]); + p += size / 8; + } + + *phashlen = hashlen; + *pphash = phash; +} + +// Verdef methods. + +// Write this definition to a buffer for the output section. + +template<int size, bool big_endian> +unsigned char* +Verdef::write(const Stringpool* dynpool, bool is_last, unsigned char* pb + ACCEPT_SIZE_ENDIAN) const +{ + const int verdef_size = elfcpp::Elf_sizes<size>::verdef_size; + const int verdaux_size = elfcpp::Elf_sizes<size>::verdaux_size; + + elfcpp::Verdef_write<size, big_endian> vd(pb); + vd.set_vd_version(elfcpp::VER_DEF_CURRENT); + vd.set_vd_flags((this->is_base_ ? elfcpp::VER_FLG_BASE : 0) + | (this->is_weak_ ? elfcpp::VER_FLG_WEAK : 0)); + vd.set_vd_ndx(this->index()); + vd.set_vd_cnt(1 + this->deps_.size()); + vd.set_vd_hash(Dynobj::elf_hash(this->name())); + vd.set_vd_aux(verdef_size); + vd.set_vd_next(is_last + ? 0 + : verdef_size + (1 + this->deps_.size()) * verdaux_size); + pb += verdef_size; + + elfcpp::Verdaux_write<size, big_endian> vda(pb); + vda.set_vda_name(dynpool->get_offset(this->name())); + vda.set_vda_next(this->deps_.empty() ? 0 : verdaux_size); + pb += verdaux_size; + + Deps::const_iterator p; + unsigned int i; + for (p = this->deps_.begin(), i = 0; + p != this->deps_.end(); + ++p, ++i) + { + elfcpp::Verdaux_write<size, big_endian> vda(pb); + vda.set_vda_name(dynpool->get_offset(*p)); + vda.set_vda_next(i + 1 >= this->deps_.size() ? 0 : verdaux_size); + pb += verdaux_size; + } + + return pb; +} + +// Verneed methods. + +Verneed::~Verneed() +{ + for (Need_versions::iterator p = this->need_versions_.begin(); + p != this->need_versions_.end(); + ++p) + delete *p; +} + +// Add a new version to this file reference. + +Verneed_version* +Verneed::add_name(const char* name) +{ + Verneed_version* vv = new Verneed_version(name); + this->need_versions_.push_back(vv); + return vv; +} + +// Set the version indexes starting at INDEX. + +unsigned int +Verneed::finalize(unsigned int index) +{ + for (Need_versions::iterator p = this->need_versions_.begin(); + p != this->need_versions_.end(); + ++p) + { + (*p)->set_index(index); + ++index; + } + return index; +} + +// Write this list of referenced versions to a buffer for the output +// section. + +template<int size, bool big_endian> +unsigned char* +Verneed::write(const Stringpool* dynpool, bool is_last, + unsigned char* pb ACCEPT_SIZE_ENDIAN) const +{ + const int verneed_size = elfcpp::Elf_sizes<size>::verneed_size; + const int vernaux_size = elfcpp::Elf_sizes<size>::vernaux_size; + + elfcpp::Verneed_write<size, big_endian> vn(pb); + vn.set_vn_version(elfcpp::VER_NEED_CURRENT); + vn.set_vn_cnt(this->need_versions_.size()); + vn.set_vn_file(dynpool->get_offset(this->filename())); + vn.set_vn_aux(verneed_size); + vn.set_vn_next(is_last + ? 0 + : verneed_size + this->need_versions_.size() * vernaux_size); + pb += verneed_size; + + Need_versions::const_iterator p; + unsigned int i; + for (p = this->need_versions_.begin(), i = 0; + p != this->need_versions_.end(); + ++p, ++i) + { + elfcpp::Vernaux_write<size, big_endian> vna(pb); + vna.set_vna_hash(Dynobj::elf_hash((*p)->version())); + // FIXME: We need to sometimes set VER_FLG_WEAK here. + vna.set_vna_flags(0); + vna.set_vna_other((*p)->index()); + vna.set_vna_name(dynpool->get_offset((*p)->version())); + vna.set_vna_next(i + 1 >= this->need_versions_.size() + ? 0 + : vernaux_size); + pb += vernaux_size; + } + + return pb; +} + +// Versions methods. + +Versions::~Versions() +{ + for (Defs::iterator p = this->defs_.begin(); + p != this->defs_.end(); + ++p) + delete *p; + + for (Needs::iterator p = this->needs_.begin(); + p != this->needs_.end(); + ++p) + delete *p; +} + +// Record version information for a symbol going into the dynamic +// symbol table. + +void +Versions::record_version(const General_options* options, + Stringpool* dynpool, const Symbol* sym) +{ + gold_assert(!this->is_finalized_); + gold_assert(sym->version() != NULL); + + Stringpool::Key version_key; + const char* version = dynpool->add(sym->version(), &version_key); + + if (!sym->is_from_dynobj()) + this->add_def(options, sym, version, version_key); + else + { + // This is a version reference. + + Object* object = sym->object(); + gold_assert(object->is_dynamic()); + Dynobj* dynobj = static_cast<Dynobj*>(object); + + this->add_need(dynpool, dynobj->soname(), version, version_key); + } +} + +// We've found a symbol SYM defined in version VERSION. + +void +Versions::add_def(const General_options* options, const Symbol* sym, + const char* version, Stringpool::Key version_key) +{ + Key k(version_key, 0); + Version_base* const vbnull = NULL; + std::pair<Version_table::iterator, bool> ins = + this->version_table_.insert(std::make_pair(k, vbnull)); + + if (!ins.second) + { + // We already have an entry for this version. + Version_base* vb = ins.first->second; + + // We have now seen a symbol in this version, so it is not + // weak. + vb->clear_weak(); + + // FIXME: When we support version scripts, we will need to + // check whether this symbol should be forced local. + } + else + { + // If we are creating a shared object, it is an error to + // find a definition of a symbol with a version which is not + // in the version script. + if (options->is_shared()) + { + fprintf(stderr, _("%s: symbol %s has undefined version %s\n"), + program_name, sym->name(), version); + gold_exit(false); + } + + // If this is the first version we are defining, first define + // the base version. FIXME: Should use soname here when + // creating a shared object. + Verdef* vdbase = new Verdef(options->output_file_name(), true, false, + true); + this->defs_.push_back(vdbase); + + // When creating a regular executable, automatically define + // a new version. + Verdef* vd = new Verdef(version, false, false, false); + this->defs_.push_back(vd); + ins.first->second = vd; + } +} + +// Add a reference to version NAME in file FILENAME. + +void +Versions::add_need(Stringpool* dynpool, const char* filename, const char* name, + Stringpool::Key name_key) +{ + Stringpool::Key filename_key; + filename = dynpool->add(filename, &filename_key); + + Key k(name_key, filename_key); + Version_base* const vbnull = NULL; + std::pair<Version_table::iterator, bool> ins = + this->version_table_.insert(std::make_pair(k, vbnull)); + + if (!ins.second) + { + // We already have an entry for this filename/version. + return; + } + + // See whether we already have this filename. We don't expect many + // version references, so we just do a linear search. This could be + // replaced by a hash table. + Verneed* vn = NULL; + for (Needs::iterator p = this->needs_.begin(); + p != this->needs_.end(); + ++p) + { + if ((*p)->filename() == filename) + { + vn = *p; + break; + } + } + + if (vn == NULL) + { + // We have a new filename. + vn = new Verneed(filename); + this->needs_.push_back(vn); + } + + ins.first->second = vn->add_name(name); +} + +// Set the version indexes. Create a new dynamic version symbol for +// each new version definition. + +unsigned int +Versions::finalize(const Target* target, Symbol_table* symtab, + unsigned int dynsym_index, std::vector<Symbol*>* syms) +{ + gold_assert(!this->is_finalized_); + + unsigned int vi = 1; + + for (Defs::iterator p = this->defs_.begin(); + p != this->defs_.end(); + ++p) + { + (*p)->set_index(vi); + ++vi; + + // Create a version symbol if necessary. + if (!(*p)->is_symbol_created()) + { + Symbol* vsym = symtab->define_as_constant(target, (*p)->name(), + (*p)->name(), 0, 0, + elfcpp::STT_OBJECT, + elfcpp::STB_GLOBAL, + elfcpp::STV_DEFAULT, 0, + false); + vsym->set_needs_dynsym_entry(); + ++dynsym_index; + syms->push_back(vsym); + // The name is already in the dynamic pool. + } + } + + // Index 1 is used for global symbols. + if (vi == 1) + { + gold_assert(this->defs_.empty()); + vi = 2; + } + + for (Needs::iterator p = this->needs_.begin(); + p != this->needs_.end(); + ++p) + vi = (*p)->finalize(vi); + + this->is_finalized_ = true; + + return dynsym_index; +} + +// Return the version index to use for a symbol. This does two hash +// table lookups: one in DYNPOOL and one in this->version_table_. +// Another approach alternative would be store a pointer in SYM, which +// would increase the size of the symbol table. Or perhaps we could +// use a hash table from dynamic symbol pointer values to Version_base +// pointers. + +unsigned int +Versions::version_index(const Stringpool* dynpool, const Symbol* sym) const +{ + Stringpool::Key version_key; + const char* version = dynpool->find(sym->version(), &version_key); + gold_assert(version != NULL); + + Key k; + if (!sym->is_from_dynobj()) + k = Key(version_key, 0); + else + { + Object* object = sym->object(); + gold_assert(object->is_dynamic()); + Dynobj* dynobj = static_cast<Dynobj*>(object); + + Stringpool::Key filename_key; + const char* filename = dynpool->find(dynobj->soname(), &filename_key); + gold_assert(filename != NULL); + + k = Key(version_key, filename_key); + } + + Version_table::const_iterator p = this->version_table_.find(k); + gold_assert(p != this->version_table_.end()); + + return p->second->index(); +} + +// Return an allocated buffer holding the contents of the symbol +// version section. + +template<int size, bool big_endian> +void +Versions::symbol_section_contents(const Stringpool* dynpool, + unsigned int local_symcount, + const std::vector<Symbol*>& syms, + unsigned char** pp, + unsigned int* psize + ACCEPT_SIZE_ENDIAN) const +{ + gold_assert(this->is_finalized_); + + unsigned int sz = (local_symcount + syms.size()) * 2; + unsigned char* pbuf = new unsigned char[sz]; + + for (unsigned int i = 0; i < local_symcount; ++i) + elfcpp::Swap<16, big_endian>::writeval(pbuf + i * 2, + elfcpp::VER_NDX_LOCAL); + + for (std::vector<Symbol*>::const_iterator p = syms.begin(); + p != syms.end(); + ++p) + { + unsigned int version_index; + const char* version = (*p)->version(); + if (version == NULL) + version_index = elfcpp::VER_NDX_GLOBAL; + else + version_index = this->version_index(dynpool, *p); + elfcpp::Swap<16, big_endian>::writeval(pbuf + (*p)->dynsym_index() * 2, + version_index); + } + + *pp = pbuf; + *psize = sz; +} + +// Return an allocated buffer holding the contents of the version +// definition section. + +template<int size, bool big_endian> +void +Versions::def_section_contents(const Stringpool* dynpool, + unsigned char** pp, unsigned int* psize, + unsigned int* pentries + ACCEPT_SIZE_ENDIAN) const +{ + gold_assert(this->is_finalized_); + gold_assert(!this->defs_.empty()); + + const int verdef_size = elfcpp::Elf_sizes<size>::verdef_size; + const int verdaux_size = elfcpp::Elf_sizes<size>::verdaux_size; + + unsigned int sz = 0; + for (Defs::const_iterator p = this->defs_.begin(); + p != this->defs_.end(); + ++p) + { + sz += verdef_size + verdaux_size; + sz += (*p)->count_dependencies() * verdaux_size; + } + + unsigned char* pbuf = new unsigned char[sz]; + + unsigned char* pb = pbuf; + Defs::const_iterator p; + unsigned int i; + for (p = this->defs_.begin(), i = 0; + p != this->defs_.end(); + ++p, ++i) + pb = (*p)->write SELECT_SIZE_ENDIAN_NAME(size, big_endian)( + dynpool, i + 1 >= this->defs_.size(), pb + SELECT_SIZE_ENDIAN(size, big_endian)); + + gold_assert(static_cast<unsigned int>(pb - pbuf) == sz); + + *pp = pbuf; + *psize = sz; + *pentries = this->defs_.size(); +} + +// Return an allocated buffer holding the contents of the version +// reference section. + +template<int size, bool big_endian> +void +Versions::need_section_contents(const Stringpool* dynpool, + unsigned char** pp, unsigned int *psize, + unsigned int *pentries + ACCEPT_SIZE_ENDIAN) const +{ + gold_assert(this->is_finalized_); + gold_assert(!this->needs_.empty()); + + const int verneed_size = elfcpp::Elf_sizes<size>::verneed_size; + const int vernaux_size = elfcpp::Elf_sizes<size>::vernaux_size; + + unsigned int sz = 0; + for (Needs::const_iterator p = this->needs_.begin(); + p != this->needs_.end(); + ++p) + { + sz += verneed_size; + sz += (*p)->count_versions() * vernaux_size; + } + + unsigned char* pbuf = new unsigned char[sz]; + + unsigned char* pb = pbuf; + Needs::const_iterator p; + unsigned int i; + for (p = this->needs_.begin(), i = 0; + p != this->needs_.end(); + ++p, ++i) + pb = (*p)->write SELECT_SIZE_ENDIAN_NAME(size, big_endian)( + dynpool, i + 1 >= this->needs_.size(), pb + SELECT_SIZE_ENDIAN(size, big_endian)); + + gold_assert(static_cast<unsigned int>(pb - pbuf) == sz); + + *pp = pbuf; + *psize = sz; + *pentries = this->needs_.size(); +} + +// 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>; + +template +void +Versions::symbol_section_contents<32, false>( + const Stringpool*, + unsigned int, + const std::vector<Symbol*>&, + unsigned char**, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(32, false)) const; + +template +void +Versions::symbol_section_contents<32, true>( + const Stringpool*, + unsigned int, + const std::vector<Symbol*>&, + unsigned char**, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(32, true)) const; + +template +void +Versions::symbol_section_contents<64, false>( + const Stringpool*, + unsigned int, + const std::vector<Symbol*>&, + unsigned char**, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(64, false)) const; + +template +void +Versions::symbol_section_contents<64, true>( + const Stringpool*, + unsigned int, + const std::vector<Symbol*>&, + unsigned char**, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(64, true)) const; + +template +void +Versions::def_section_contents<32, false>( + const Stringpool*, + unsigned char**, + unsigned int*, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(32, false)) const; + +template +void +Versions::def_section_contents<32, true>( + const Stringpool*, + unsigned char**, + unsigned int*, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(32, true)) const; + +template +void +Versions::def_section_contents<64, false>( + const Stringpool*, + unsigned char**, + unsigned int*, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(64, false)) const; + +template +void +Versions::def_section_contents<64, true>( + const Stringpool*, + unsigned char**, + unsigned int*, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(64, true)) const; + +template +void +Versions::need_section_contents<32, false>( + const Stringpool*, + unsigned char**, + unsigned int*, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(32, false)) const; + +template +void +Versions::need_section_contents<32, true>( + const Stringpool*, + unsigned char**, + unsigned int*, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(32, true)) const; + +template +void +Versions::need_section_contents<64, false>( + const Stringpool*, + unsigned char**, + unsigned int*, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(64, false)) const; + +template +void +Versions::need_section_contents<64, true>( + const Stringpool*, + unsigned char**, + unsigned int*, + unsigned int* + ACCEPT_SIZE_ENDIAN_EXPLICIT(64, true)) const; + +} // End namespace gold. diff --git a/gold/dynobj.h b/gold/dynobj.h new file mode 100644 index 000000000000..d63aa6af0b21 --- /dev/null +++ b/gold/dynobj.h @@ -0,0 +1,494 @@ +// dynobj.h -- dynamic object support for gold -*- C++ -*- + +#ifndef GOLD_DYNOBJ_H +#define GOLD_DYNOBJ_H + +#include <vector> + +#include "stringpool.h" +#include "object.h" + +namespace gold +{ + +class General_options; + +// A dynamic object (ET_DYN). This is an abstract base class itself. +// The implementations is the template class Sized_dynobj. + +class Dynobj : public Object +{ + public: + Dynobj(const std::string& name, Input_file* input_file, off_t offset = 0) + : Object(name, input_file, true, offset), soname_() + { } + + // Return the name to use in a DT_NEEDED entry for this object. + const char* + soname() const; + + // Compute the ELF hash code for a string. + static uint32_t + elf_hash(const char*); + + // Create a standard ELF hash table, setting *PPHASH and *PHASHLEN. + // DYNSYMS is the global dynamic symbols. LOCAL_DYNSYM_COUNT is the + // number of local dynamic symbols, which is the index of the first + // dynamic gobal symbol. + static void + create_elf_hash_table(const Target*, const std::vector<Symbol*>& dynsyms, + unsigned int local_dynsym_count, + unsigned char** pphash, + unsigned int* phashlen); + + // Create a GNU hash table, setting *PPHASH and *PHASHLEN. DYNSYMS + // is the global dynamic symbols. LOCAL_DYNSYM_COUNT is the number + // of local dynamic symbols, which is the index of the first dynamic + // gobal symbol. + static void + create_gnu_hash_table(const Target*, const std::vector<Symbol*>& dynsyms, + unsigned int local_dynsym_count, + unsigned char** pphash, unsigned int* phashlen); + + protected: + // Set the DT_SONAME string. + void + set_soname_string(const char* s) + { this->soname_.assign(s); } + + private: + // Compute the GNU hash code for a string. + static uint32_t + gnu_hash(const char*); + + // Compute the number of hash buckets to use. + static unsigned int + compute_bucket_count(const std::vector<uint32_t>& hashcodes, + bool for_gnu_hash_table); + + // Sized version of create_elf_hash_table. + template<bool big_endian> + static void + sized_create_elf_hash_table(const std::vector<uint32_t>& bucket, + const std::vector<uint32_t>& chain, + unsigned char* phash, + unsigned int hashlen); + + // Sized version of create_gnu_hash_table. + template<int size, bool big_endian> + static void + sized_create_gnu_hash_table(const std::vector<Symbol*>& hashed_dynsyms, + const std::vector<uint32_t>& dynsym_hashvals, + unsigned int unhashed_dynsym_count, + unsigned char** pphash, + unsigned int* phashlen); + + // The DT_SONAME name, if any. + std::string soname_; +}; + +// A dynamic object, size and endian specific version. + +template<int size, bool big_endian> +class Sized_dynobj : public Dynobj +{ + public: + 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*); + + // 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*); + + // 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. + Object::Location + do_section_contents(unsigned int shndx) + { return this->elf_file_.section_contents(shndx); } + + // Return section flags. + uint64_t + do_section_flags(unsigned int shndx) + { return this->elf_file_.section_flags(shndx); } + + 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 version definitions to the version map. + void + make_verdef_map(Read_symbols_data* sd, Version_map*) const; + + // Add version references to the version map. + void + make_verneed_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_; +}; + +// A base class for Verdef and Verneed_version which just handles the +// version index which will be stored in the SHT_GNU_versym section. + +class Version_base +{ + public: + Version_base() + : index_(-1U) + { } + + virtual + ~Version_base() + { } + + // Return the version index. + unsigned int + index() const + { + gold_assert(this->index_ != -1U); + return this->index_; + } + + // Set the version index. + void + set_index(unsigned int index) + { + gold_assert(this->index_ == -1U); + this->index_ = index; + } + + // Clear the weak flag in a version definition. + virtual void + clear_weak() = 0; + + private: + Version_base(const Version_base&); + Version_base& operator=(const Version_base&); + + // The index of the version definition or reference. + unsigned int index_; +}; + +// This class handles a version being defined in the file we are +// generating. + +class Verdef : public Version_base +{ + public: + Verdef(const char* name, bool is_base, bool is_weak, bool is_symbol_created) + : name_(name), deps_(), is_base_(is_base), is_weak_(is_weak), + is_symbol_created_(is_symbol_created) + { } + + // Return the version name. + const char* + name() const + { return this->name_; } + + // Return the number of dependencies. + unsigned int + count_dependencies() const + { return this->deps_.size(); } + + // Add a dependency to this version. The NAME should be + // canonicalized in the dynamic Stringpool. + void + add_dependency(const char* name) + { this->deps_.push_back(name); } + + // Return whether this definition is weak. + bool + is_weak() const + { return this->is_weak_; } + + // Clear the weak flag. + void + clear_weak() + { this->is_weak_ = false; } + + // Return whether a version symbol has been created for this + // definition. + bool + is_symbol_created() const + { return this->is_symbol_created_; } + + // Write contents to buffer. + template<int size, bool big_endian> + unsigned char* + write(const Stringpool*, bool is_last, unsigned char* + ACCEPT_SIZE_ENDIAN) const; + + private: + Verdef(const Verdef&); + Verdef& operator=(const Verdef&); + + // The type of the list of version dependencies. Each dependency + // should be canonicalized in the dynamic Stringpool. + typedef std::vector<const char*> Deps; + + // The name of this version. This should be canonicalized in the + // dynamic Stringpool. + const char* name_; + // A list of other versions which this version depends upon. + Deps deps_; + // Whether this is the base version. + bool is_base_; + // Whether this version is weak. + bool is_weak_; + // Whether a version symbol has been created. + bool is_symbol_created_; +}; + +// A referened version. This will be associated with a filename by +// Verneed. + +class Verneed_version : public Version_base +{ + public: + Verneed_version(const char* version) + : version_(version) + { } + + // Return the version name. + const char* + version() const + { return this->version_; } + + // Clear the weak flag. This is invalid for a reference. + void + clear_weak() + { gold_unreachable(); } + + private: + Verneed_version(const Verneed_version&); + Verneed_version& operator=(const Verneed_version&); + + const char* version_; +}; + +// Version references in a single dynamic object. + +class Verneed +{ + public: + Verneed(const char* filename) + : filename_(filename), need_versions_() + { } + + ~Verneed(); + + // Return the file name. + const char* + filename() const + { return this->filename_; } + + // Return the number of versions. + unsigned int + count_versions() const + { return this->need_versions_.size(); } + + // Add a version name. The name should be canonicalized in the + // dynamic Stringpool. If the name is already present, this does + // nothing. + Verneed_version* + add_name(const char* name); + + // Set the version indexes, starting at INDEX. Return the updated + // INDEX. + unsigned int + finalize(unsigned int index); + + // Write contents to buffer. + template<int size, bool big_endian> + unsigned char* + write(const Stringpool*, bool is_last, unsigned char* + ACCEPT_SIZE_ENDIAN) const; + + private: + Verneed(const Verneed&); + Verneed& operator=(const Verneed&); + + // The type of the list of version names. Each name should be + // canonicalized in the dynamic Stringpool. + typedef std::vector<Verneed_version*> Need_versions; + + // The filename of the dynamic object. This should be + // canonicalized in the dynamic Stringpool. + const char* filename_; + // The list of version names. + Need_versions need_versions_; +}; + +// This class handles version definitions and references which go into +// the output file. + +class Versions +{ + public: + Versions() + : defs_(), needs_(), version_table_(), is_finalized_(false) + { } + + ~Versions(); + + // SYM is going into the dynamic symbol table and has a version. + // Record the appropriate version information. + void + record_version(const General_options*, Stringpool*, const Symbol* sym); + + // Set the version indexes. DYNSYM_INDEX is the index we should use + // for the next dynamic symbol. We add new dynamic symbols to SYMS + // and return an updated DYNSYM_INDEX. + unsigned int + finalize(const Target*, Symbol_table* symtab, unsigned int dynsym_index, + std::vector<Symbol*>* syms); + + // Return whether there are any version definitions. + bool + any_defs() const + { return !this->defs_.empty(); } + + // Return whether there are any version references. + bool + any_needs() const + { return !this->needs_.empty(); } + + // Build an allocated buffer holding the contents of the symbol + // version section (.gnu.version). + template<int size, bool big_endian> + void + symbol_section_contents(const Stringpool*, unsigned int local_symcount, + const std::vector<Symbol*>& syms, + unsigned char**, unsigned int* + ACCEPT_SIZE_ENDIAN) const; + + // Build an allocated buffer holding the contents of the version + // definition section (.gnu.version_d). + template<int size, bool big_endian> + void + def_section_contents(const Stringpool*, unsigned char**, + unsigned int* psize, unsigned int* pentries + ACCEPT_SIZE_ENDIAN) const; + + // Build an allocated buffer holding the contents of the version + // reference section (.gnu.version_r). + template<int size, bool big_endian> + void + need_section_contents(const Stringpool*, unsigned char**, + unsigned int* psize, unsigned int* pentries + ACCEPT_SIZE_ENDIAN) const; + + private: + // The type of the list of version definitions. + typedef std::vector<Verdef*> Defs; + + // The type of the list of version references. + typedef std::vector<Verneed*> Needs; + + // Handle a symbol SYM defined with version VERSION. + void + add_def(const General_options*, const Symbol* sym, const char* version, + Stringpool::Key); + + // Add a reference to version NAME in file FILENAME. + void + add_need(Stringpool*, const char* filename, const char* name, + Stringpool::Key); + + // Return the version index to use for SYM. + unsigned int + version_index(const Stringpool*, const Symbol* sym) const; + + // We keep a hash table mapping canonicalized name/version pairs to + // a version base. + typedef std::pair<Stringpool::Key, Stringpool::Key> Key; + + struct Version_table_hash + { + size_t + operator()(const Key& k) const + { return k.first + k.second; } + }; + + struct Version_table_eq + { + bool + operator()(const Key& k1, const Key& k2) const + { return k1.first == k2.first && k1.second == k2.second; } + }; + + typedef Unordered_map<Key, Version_base*, Version_table_hash, + Version_table_eq> Version_table; + + // The version definitions. + Defs defs_; + // The version references. + Needs needs_; + // The mapping from a canonicalized version/filename pair to a + // version index. The filename may be NULL. + Version_table version_table_; + // Whether the version indexes have been set. + bool is_finalized_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_DYNOBJ_H) diff --git a/gold/fileread.cc b/gold/fileread.cc new file mode 100644 index 000000000000..1a142022569a --- /dev/null +++ b/gold/fileread.cc @@ -0,0 +1,379 @@ +// fileread.cc -- read files for gold + +#include "gold.h" + +#include <cstring> +#include <cerrno> +#include <fcntl.h> +#include <unistd.h> + +#include "options.h" +#include "dirsearch.h" +#include "fileread.h" + +namespace gold +{ + +// Class File_read::View. + +File_read::View::~View() +{ + gold_assert(!this->is_locked()); + delete[] this->data_; +} + +void +File_read::View::lock() +{ + ++this->lock_count_; +} + +void +File_read::View::unlock() +{ + gold_assert(this->lock_count_ > 0); + --this->lock_count_; +} + +bool +File_read::View::is_locked() +{ + return this->lock_count_ > 0; +} + +// Class File_read. + +// The File_read class is designed to support file descriptor caching, +// but this is not currently implemented. + +File_read::~File_read() +{ + gold_assert(this->lock_count_ == 0); + if (this->descriptor_ >= 0) + { + if (close(this->descriptor_) < 0) + fprintf(stderr, _("%s: warning: close(%s) failed: %s"), + program_name, this->name_.c_str(), strerror(errno)); + this->descriptor_ = -1; + } + this->name_.clear(); + this->clear_views(true); +} + +// Open the file. + +bool +File_read::open(const std::string& name) +{ + gold_assert(this->lock_count_ == 0 + && this->descriptor_ < 0 + && this->name_.empty()); + this->name_ = name; + this->descriptor_ = ::open(this->name_.c_str(), O_RDONLY); + ++this->lock_count_; + return this->descriptor_ >= 0; +} + +// Open the file for testing purposes. + +bool +File_read::open(const std::string& name, const unsigned char* contents, + off_t contents_size) +{ + gold_assert(this->lock_count_ == 0 + && this->descriptor_ < 0 + && this->name_.empty()); + this->name_ = name; + this->contents_ = contents; + this->contents_size_ = contents_size; + ++this->lock_count_; + return true; +} + +void +File_read::lock() +{ + ++this->lock_count_; +} + +void +File_read::unlock() +{ + gold_assert(this->lock_count_ > 0); + --this->lock_count_; +} + +bool +File_read::is_locked() +{ + return this->lock_count_ > 0; +} + +// See if we have a view which covers the file starting at START for +// SIZE bytes. Return a pointer to the View if found, NULL if not. + +inline File_read::View* +File_read::find_view(off_t start, off_t size) +{ + off_t page = File_read::page_offset(start); + Views::iterator p = this->views_.find(page); + if (p == this->views_.end()) + return NULL; + if (p->second->size() - (start - page) < size) + return NULL; + return p->second; +} + +// Read data from the file. Return the number of bytes read. If +// PBYTES is not NULL, store the number of bytes in *PBYTES, otherwise +// require that we read exactly the number of bytes requested. + +off_t +File_read::do_read(off_t start, off_t size, void* p, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + + off_t bytes; + if (this->contents_ == NULL) + { + int o = this->descriptor_; + + if (lseek(o, start, SEEK_SET) < 0) + { + fprintf(stderr, _("%s: %s: lseek to %lld failed: %s"), + program_name, this->filename().c_str(), + static_cast<long long>(start), + strerror(errno)); + gold_exit(false); + } + + bytes = ::read(o, p, size); + if (bytes < 0) + { + fprintf(stderr, _("%s: %s: read failed: %s\n"), + program_name, this->filename().c_str(), strerror(errno)); + gold_exit(false); + } + } + else + { + bytes = this->contents_size_ - start; + if (bytes < 0) + bytes = 0; + else if (bytes > size) + bytes = size; + memcpy(p, this->contents_ + start, bytes); + } + + if (pbytes != NULL) + *pbytes = bytes; + else if (bytes != size) + { + fprintf(stderr, + _("%s: %s: file too short: read only %lld of %lld " + "bytes at %lld\n"), + program_name, this->filename().c_str(), + static_cast<long long>(bytes), + static_cast<long long>(size), + static_cast<long long>(start)); + gold_exit(false); + } + + return bytes; +} + +void +File_read::read(off_t start, off_t size, void* p, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + + File_read::View* pv = this->find_view(start, size); + if (pv != NULL) + { + memcpy(p, pv->data() + (start - pv->start()), size); + if (pbytes != NULL) + *pbytes = size; + return; + } + + this->do_read(start, size, p, pbytes); +} + +// Find an existing view or make a new one. + +File_read::View* +File_read::find_or_make_view(off_t start, off_t size, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + + off_t poff = File_read::page_offset(start); + + File_read::View* const vnull = NULL; + std::pair<Views::iterator, bool> ins = + this->views_.insert(std::make_pair(poff, vnull)); + + if (!ins.second) + { + // There was an existing view at this offset. + File_read::View* v = ins.first->second; + if (v->size() - (start - v->start()) >= size) + { + if (pbytes != NULL) + *pbytes = size; + return v; + } + + // This view is not large enough. + this->saved_views_.push_back(v); + } + + // We need to read data from the file. + + off_t psize = File_read::pages(size + (start - poff)); + unsigned char* p = new unsigned char[psize]; + + off_t got_bytes; + off_t bytes = this->do_read(poff, psize, p, &got_bytes); + + File_read::View* v = new File_read::View(poff, bytes, p); + + ins.first->second = v; + + if (bytes - (start - poff) >= size) + { + if (pbytes != NULL) + *pbytes = size; + return v; + } + + if (pbytes != NULL) + { + *pbytes = bytes - (start - poff); + return v; + } + + fprintf(stderr, + _("%s: %s: file too short: read only %lld of %lld bytes at %lld\n"), + program_name, this->filename().c_str(), + static_cast<long long>(bytes - (start - poff)), + static_cast<long long>(size), + static_cast<long long>(start)); + gold_exit(false); +} + +// This implementation of get_view just reads into a memory buffer, +// which we store on view_list_. At some point we should support +// mmap. + +const unsigned char* +File_read::get_view(off_t start, off_t size, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + File_read::View* pv = this->find_or_make_view(start, size, pbytes); + return pv->data() + (start - pv->start()); +} + +File_view* +File_read::get_lasting_view(off_t start, off_t size, off_t* pbytes) +{ + gold_assert(this->lock_count_ > 0); + File_read::View* pv = this->find_or_make_view(start, size, pbytes); + pv->lock(); + return new File_view(*this, pv, pv->data() + (start - pv->start())); +} + +// Remove all the file views. + +void +File_read::clear_views(bool destroying) +{ + for (Views::iterator p = this->views_.begin(); + p != this->views_.end(); + ++p) + { + if (!p->second->is_locked()) + delete p->second; + else + { + gold_assert(!destroying); + this->saved_views_.push_back(p->second); + } + } + this->views_.clear(); + + Saved_views::iterator p = this->saved_views_.begin(); + while (p != this->saved_views_.end()) + { + if (!(*p)->is_locked()) + { + delete *p; + p = this->saved_views_.erase(p); + } + else + { + gold_assert(!destroying); + ++p; + } + } +} + +// Class File_view. + +File_view::~File_view() +{ + gold_assert(this->file_.is_locked()); + this->view_->unlock(); +} + +// Class Input_file. + +// Create a file for testing. + +Input_file::Input_file(const char* name, const unsigned char* contents, + off_t size) + : file_() +{ + this->input_argument_ = + new Input_file_argument(name, false, Position_dependent_options()); + bool ok = file_.open(name, contents, size); + gold_assert(ok); +} + +// Open the file. + +void +Input_file::open(const General_options& options, const Dirsearch& dirpath) +{ + std::string name; + if (!this->input_argument_->is_lib()) + name = this->input_argument_->name(); + else + { + std::string n1("lib"); + n1 += this->input_argument_->name(); + std::string n2; + if (options.is_static()) + n1 += ".a"; + else + { + n2 = n1 + ".a"; + n1 += ".so"; + } + name = dirpath.find(n1, n2); + if (name.empty()) + { + fprintf(stderr, _("%s: cannot find %s\n"), program_name, + this->input_argument_->name()); + gold_exit(false); + } + } + + if (!this->file_.open(name)) + { + fprintf(stderr, _("%s: cannot open %s: %s\n"), program_name, + name.c_str(), strerror(errno)); + gold_exit(false); + } +} + +} // End namespace gold. diff --git a/gold/fileread.h b/gold/fileread.h new file mode 100644 index 000000000000..178e7f355e44 --- /dev/null +++ b/gold/fileread.h @@ -0,0 +1,257 @@ +// fileread.h -- read files for gold -*- C++ -*- + +// Classes used to read data from binary input files. + +#ifndef GOLD_FILEREAD_H +#define GOLD_FILEREAD_H + +#include <list> +#include <map> +#include <string> + +#include "options.h" + +namespace gold +{ + +class Dirsearch; +class File_view; + +// File_read manages a file descriptor for a file we are reading. We +// close file descriptors if we run out of them, so this class reopens +// the file as needed. + +class File_read +{ + public: + File_read() + : name_(), descriptor_(-1), lock_count_(0), views_(), + saved_views_(), contents_(NULL), contents_size_(0) + { } + + ~File_read(); + + // Open a file. + bool + open(const std::string& name); + + // Pretend to open the file, but provide the file contents. No + // actual file system activity will occur. This is used for + // testing. + bool + open(const std::string& name, const unsigned char* contents, off_t size); + + // Return the file name. + const std::string& + filename() const + { return this->name_; } + + // Lock the file for access within a particular Task::run execution. + // This means that the descriptor can not be closed. This routine + // may only be called from the main thread. + void + lock(); + + // Unlock the descriptor, permitting it to be closed if necessary. + void + unlock(); + + // Test whether the object is locked. + bool + is_locked(); + + // Return a view into the file. The pointer will remain valid until + // the File_read is unlocked. If PBYTES is NULL, it is an error if + // we can not read enough data. Otherwise *PBYTES is set to the + // number of bytes read. + const unsigned char* + get_view(off_t start, off_t size, off_t *pbytes = NULL); + + // Read data from the file into the buffer P. PBYTES is as in + // get_view. + void + read(off_t start, off_t size, void* p, off_t *pbytes = NULL); + + // Return a lasting view into the file. This is allocated with new, + // and the caller is responsible for deleting it when done. The + // data associated with this view will remain valid until the view + // is deleted. PBYTES is handled as with get_view. + File_view* + get_lasting_view(off_t start, off_t size, off_t *pbytes = NULL); + + private: + // This class may not be copied. + File_read(const File_read&); + File_read& operator=(const File_read&); + + // A view into the file when not using mmap. + class View + { + public: + View(off_t start, off_t size, unsigned char* data) + : start_(start), size_(size), data_(data), lock_count_(0) + { } + + ~View(); + + off_t + start() const + { return this->start_; } + + off_t + size() const + { return this->size_; } + + unsigned char* + data() const + { return this->data_; } + + void + lock(); + + void + unlock(); + + bool + is_locked(); + + private: + View(const View&); + View& operator=(const View&); + + off_t start_; + off_t size_; + unsigned char* data_; + int lock_count_; + }; + + friend class File_view; + + // Find a view into the file. + View* + find_view(off_t start, off_t size); + + // Read data from the file into a buffer. + off_t + do_read(off_t start, off_t size, void* p, off_t* pbytes); + + // Find or make a view into the file. + View* + find_or_make_view(off_t start, off_t size, off_t* pbytes); + + // Clear the file views. + void + clear_views(bool); + + // The size of a file page for buffering data. + static const off_t page_size = 8192; + + // Given a file offset, return the page offset. + static off_t + page_offset(off_t file_offset) + { return file_offset & ~ (page_size - 1); } + + // Given a file size, return the size to read integral pages. + static off_t + pages(off_t file_size) + { return (file_size + (page_size - 1)) & ~ (page_size - 1); } + + // The type of a mapping from page start to views. + typedef std::map<off_t, View*> Views; + + // A simple list of Views. + typedef std::list<View*> Saved_views; + + // File name. + std::string name_; + // File descriptor. + int descriptor_; + // Number of locks on the file. + int lock_count_; + // Buffered views into the file. + Views views_; + // List of views which were locked but had to be removed from views_ + // because they were not large enough. + Saved_views saved_views_; + // Specified file contents. Used only for testing purposes. + const unsigned char* contents_; + // Specified file size. Used only for testing purposes. + off_t contents_size_; +}; + +// A view of file data that persists even when the file is unlocked. +// Callers should destroy these when no longer required. These are +// obtained form File_read::get_lasting_view. They may only be +// destroyed when the underlying File_read is locked. + +class File_view +{ + public: + // This may only be called when the underlying File_read is locked. + ~File_view(); + + // Return a pointer to the data associated with this view. + const unsigned char* + data() const + { return this->data_; } + + private: + File_view(const File_view&); + File_view& operator=(const File_view&); + + friend class File_read; + + // Callers have to get these via File_read::get_lasting_view. + File_view(File_read& file, File_read::View* view, const unsigned char* data) + : file_(file), view_(view), data_(data) + { } + + File_read& file_; + File_read::View* view_; + const unsigned char* data_; +}; + +// All the information we hold for a single input file. This can be +// an object file, a shared library, or an archive. + +class Input_file +{ + public: + Input_file(const Input_file_argument* input_argument) + : input_argument_(input_argument), file_() + { } + + // Create an input file with the contents already provided. This is + // only used for testing. With this path, don't call the open + // method. + Input_file(const char* name, const unsigned char* contents, off_t size); + + // Open the file. + void + open(const General_options&, const Dirsearch&); + + // Return the name given by the user. + const char* + name() const + { return this->input_argument_->name(); } + + // Return the file name. + const std::string& + filename() const + { return this->file_.filename(); } + + File_read& + file() + { return this->file_; } + + private: + Input_file(const Input_file&); + Input_file& operator=(const Input_file&); + + const Input_file_argument* input_argument_; + File_read file_; +}; + +} // end namespace gold + +#endif // !defined(GOLD_FILEREAD_H) diff --git a/gold/gold-threads.cc b/gold/gold-threads.cc new file mode 100644 index 000000000000..537373969ada --- /dev/null +++ b/gold/gold-threads.cc @@ -0,0 +1,228 @@ +// gold-threads.cc -- thread support for gold + +#include "gold.h" + +#ifdef ENABLE_THREADS +#include <pthread.h> +#endif + +#include "gold-threads.h" + +namespace gold +{ + +// Class Lock_impl. + +class Lock_impl +{ + public: + Lock_impl(); + ~Lock_impl(); + + void acquire(); + + void release(); + +private: + // This class can not be copied. + Lock_impl(const Lock_impl&); + Lock_impl& operator=(const Lock_impl&); + + friend class Condvar_impl; + +#ifdef ENABLE_THREADS + pthread_mutex_t mutex_; +#else + bool acquired_; +#endif +}; + +#ifdef ENABLE_THREADS + +Lock_impl::Lock_impl() +{ + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr) != 0) + gold_fatal(_("pthead_mutextattr_init failed"), true); +#ifdef PTHREAD_MUTEXT_ADAPTIVE_NP + if (pthread_mutextattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP) != 0) + gold_fatal(_("pthread_mutextattr_settype failed"), true); +#endif + + if (pthread_mutex_init (&this->mutex_, &attr) != 0) + gold_fatal(_("pthread_mutex_init failed"), true); + + if (pthread_mutexattr_destroy(&attr) != 0) + gold_fatal(_("pthread_mutexattr_destroy failed"), true); +} + +Lock_impl::~Lock_impl() +{ + if (pthread_mutex_destroy(&this->mutex_) != 0) + gold_fatal(_("pthread_mutex_destroy failed"), true); +} + +void +Lock_impl::acquire() +{ + if (pthread_mutex_lock(&this->mutex_) != 0) + gold_fatal(_("pthread_mutex_lock failed"), true); +} + +void +Lock_impl::release() +{ + if (pthread_mutex_unlock(&this->mutex_) != 0) + gold_fatal(_("pthread_mutex_unlock failed"), true); +} + +#else // !defined(ENABLE_THREADS) + +Lock_impl::Lock_impl() + : acquired_(false) +{ +} + +Lock_impl::~Lock_impl() +{ + gold_assert(!this->acquired_); +} + +void +Lock_impl::acquire() +{ + gold_assert(!this->acquired_); + this->acquired_ = true; +} + +void +Lock_impl::release() +{ + gold_assert(this->acquired_); + this->acquired_ = false; +} + +#endif // !defined(ENABLE_THREADS) + +// Methods for Lock class. + +Lock::Lock() +{ + this->lock_ = new Lock_impl; +} + +Lock::~Lock() +{ + delete this->lock_; +} + +void +Lock::acquire() +{ + this->lock_->acquire(); +} + +void +Lock::release() +{ + this->lock_->release(); +} + +// Class Condvar_impl. + +class Condvar_impl +{ + public: + Condvar_impl(); + ~Condvar_impl(); + + void wait(Lock_impl*); + void signal(); + + private: + // This class can not be copied. + Condvar_impl(const Condvar_impl&); + Condvar_impl& operator=(const Condvar_impl&); + +#ifdef ENABLE_THREADS + pthread_cond_t cond_; +#endif +}; + +#ifdef ENABLE_THREADS + +Condvar_impl::Condvar_impl() +{ + if (pthread_cond_init(&this->cond_, NULL) != 0) + gold_fatal(_("pthread_cond_init failed"), true); +} + +Condvar_impl::~Condvar_impl() +{ + if (pthread_cond_destroy(&this->cond_) != 0) + gold_fatal(_("pthread_cond_destroy failed"), true); +} + +void +Condvar_impl::wait(Lock_impl* li) +{ + if (pthread_cond_wait(&this->cond_, &li->mutex_) != 0) + gold_fatal(_("pthread_cond_wait failed"), true); +} + +void +Condvar_impl::signal() +{ + if (pthread_cond_signal(&this->cond_) != 0) + gold_fatal(_("pthread_cond_signal failed"), true); +} + +#else // !defined(ENABLE_THREADS) + +Condvar_impl::Condvar_impl() +{ +} + +Condvar_impl::~Condvar_impl() +{ +} + +void +Condvar_impl::wait(Lock_impl* li) +{ + gold_assert(li->acquired_); +} + +void +Condvar_impl::signal() +{ +} + +#endif // !defined(ENABLE_THREADS) + +// Methods for Condvar class. + +Condvar::Condvar(Lock& lock) + : lock_(lock) +{ + this->condvar_ = new Condvar_impl; +} + +Condvar::~Condvar() +{ + delete this->condvar_; +} + +void +Condvar::wait() +{ + this->condvar_->wait(this->lock_.get_impl()); +} + +void +Condvar::signal() +{ + this->condvar_->signal(); +} + +} // End namespace gold. diff --git a/gold/gold-threads.h b/gold/gold-threads.h new file mode 100644 index 000000000000..e2a8388ac136 --- /dev/null +++ b/gold/gold-threads.h @@ -0,0 +1,101 @@ +// gold-threads.h -- thread support for gold -*- C++ -*- + +// gold can be configured to support threads. If threads are +// supported, the user can specify at runtime whether or not to +// support them. This provides an interface to manage locking +// accordingly. + +// Lock +// A simple lock class. + +#ifndef GOLD_THREADS_H +#define GOLD_THREADS_H + +namespace gold +{ + +class Lock_impl; +class Condvar; + +// A simple lock class. + +class Lock +{ + public: + Lock(); + ~Lock(); + + // Acquire the lock. + void + acquire(); + + // Release the lock. + void + release(); + + private: + // This class can not be copied. + Lock(const Lock&); + Lock& operator=(const Lock&); + + friend class Condvar; + Lock_impl* + get_impl() const + { return this->lock_; } + + Lock_impl* lock_; +}; + +// RAII for Lock. + +class Hold_lock +{ + public: + Hold_lock(Lock& lock) + : lock_(lock) + { this->lock_.acquire(); } + + ~Hold_lock() + { this->lock_.release(); } + + private: + // This class can not be copied. + Hold_lock(const Hold_lock&); + Hold_lock& operator=(const Hold_lock&); + + Lock& lock_; +}; + +class Condvar_impl; + +// A simple condition variable class. It is always associated with a +// specific lock. + +class Condvar +{ + public: + Condvar(Lock& lock); + ~Condvar(); + + // Wait for the condition variable to be signalled. This should + // only be called when the lock is held. + void + wait(); + + // Signal the condition variable. This should only be called when + // the lock is held. + void + signal(); + + private: + // This class can not be copied. + Condvar(const Condvar&); + Condvar& operator=(const Condvar&); + + Lock& lock_; + Condvar_impl* condvar_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_THREADS_H) diff --git a/gold/gold.cc b/gold/gold.cc new file mode 100644 index 000000000000..e7b7ae2939d7 --- /dev/null +++ b/gold/gold.cc @@ -0,0 +1,239 @@ +// gold.cc -- main linker functions + +#include "gold.h" + +#include <cstdlib> +#include <cstdio> +#include <cstring> +#include <unistd.h> + +#include "options.h" +#include "workqueue.h" +#include "dirsearch.h" +#include "readsyms.h" +#include "symtab.h" +#include "common.h" +#include "object.h" +#include "layout.h" +#include "reloc.h" +#include "defstd.h" + +namespace gold +{ + +const char* program_name; + +void +gold_exit(bool status) +{ + exit(status ? EXIT_SUCCESS : EXIT_FAILURE); +} + +void +gold_fatal(const char* msg, bool perrno) +{ + fprintf(stderr, "%s: ", program_name); + if (perrno) + perror(msg); + else + fprintf(stderr, "%s\n", msg); + gold_exit(false); +} + +void +gold_nomem() +{ + // We are out of memory, so try hard to print a reasonable message. + // Note that we don't try to translate this message, since the + // translation process itself will require memory. + write(2, program_name, strlen(program_name)); + const char* const s = ": out of memory\n"; + write(2, s, strlen(s)); + gold_exit(false); +} + +// Handle an unreachable case. + +void +do_gold_unreachable(const char* filename, int lineno, const char* function) +{ + fprintf(stderr, "%s: internal error in %s, at %s:%d\n", + program_name, function, filename, lineno); + gold_exit(false); +} + +// This class arranges to run the functions done in the middle of the +// link. It is just a closure. + +class Middle_runner : public Task_function_runner +{ + public: + Middle_runner(const General_options& options, + const Input_objects* input_objects, + Symbol_table* symtab, + Layout* layout) + : options_(options), input_objects_(input_objects), symtab_(symtab), + layout_(layout) + { } + + void + run(Workqueue*); + + private: + const General_options& options_; + const Input_objects* input_objects_; + Symbol_table* symtab_; + Layout* layout_; +}; + +void +Middle_runner::run(Workqueue* workqueue) +{ + queue_middle_tasks(this->options_, this->input_objects_, this->symtab_, + this->layout_, workqueue); +} + +// Queue up the initial set of tasks for this link job. + +void +queue_initial_tasks(const General_options& options, + const Dirsearch& search_path, + const Command_line& cmdline, + Workqueue* workqueue, Input_objects* input_objects, + Symbol_table* symtab, Layout* layout) +{ + if (cmdline.begin() == cmdline.end()) + gold_fatal(_("no input files"), false); + + // Read the input files. We have to add the symbols to the symbol + // table in order. We do this by creating a separate blocker for + // each input file. We associate the blocker with the following + // input file, to give us a convenient place to delete it. + Task_token* this_blocker = NULL; + for (Command_line::const_iterator p = cmdline.begin(); + p != cmdline.end(); + ++p) + { + 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, + next_blocker)); + this_blocker = next_blocker; + } + + workqueue->queue(new Task_function(new Middle_runner(options, + input_objects, + symtab, + layout), + this_blocker)); +} + +// Queue up the middle set of tasks. These are the tasks which run +// after all the input objects have been found and all the symbols +// have been read, but before we lay out the output file. + +void +queue_middle_tasks(const General_options& options, + const Input_objects* input_objects, + Symbol_table* symtab, + Layout* layout, + Workqueue* workqueue) +{ + // Define some sections and symbols needed for a dynamic link. This + // handles some cases we want to see before we read the relocs. + layout->create_initial_dynamic_sections(input_objects, symtab); + + // Predefine standard symbols. This should be fast, so we don't + // bother to create a task for it. + define_standard_symbols(symtab, layout, input_objects->target()); + + // Read the relocations of the input files. We do this to find + // which symbols are used by relocations which require a GOT and/or + // a PLT entry, or a COPY reloc. When we implement garbage + // collection we will do it here by reading the relocations in a + // breadth first search by references. + // + // We could also read the relocations during the first pass, and + // mark symbols at that time. That is how the old GNU linker works. + // Doing that is more complex, since we may later decide to discard + // some of the sections, and thus change our minds about the types + // of references made to the symbols. + Task_token* blocker = new Task_token(); + Task_token* symtab_lock = new Task_token(); + for (Input_objects::Relobj_iterator p = input_objects->relobj_begin(); + p != input_objects->relobj_end(); + ++p) + { + // We can read and process the relocations in any order. But we + // only want one task to write to the symbol table at a time. + // So we queue up a task for each object to read the + // relocations. That task will in turn queue a task to wait + // until it can write to the symbol table. + blocker->add_blocker(); + workqueue->queue(new Read_relocs(options, symtab, layout, *p, + symtab_lock, blocker)); + } + + // Allocate common symbols. This requires write access to the + // symbol table, but is independent of the relocation processing. + blocker->add_blocker(); + workqueue->queue(new Allocate_commons_task(options, symtab, layout, + symtab_lock, blocker)); + + // When all those tasks are complete, we can start laying out the + // output file. + workqueue->queue(new Task_function(new Layout_task_runner(options, + input_objects, + symtab, + layout), + blocker)); +} + +// Queue up the final set of tasks. This is called at the end of +// Layout_task. + +void +queue_final_tasks(const General_options& options, + const Input_objects* input_objects, + const Symbol_table* symtab, + const Layout* layout, + Workqueue* workqueue, + Output_file* of) +{ + // Use a blocker to block the final cleanup task. + Task_token* final_blocker = new Task_token(); + + // Queue a task for each input object to relocate the sections and + // write out the local symbols. + for (Input_objects::Relobj_iterator p = input_objects->relobj_begin(); + p != input_objects->relobj_end(); + ++p) + { + final_blocker->add_blocker(); + workqueue->queue(new Relocate_task(options, symtab, layout, *p, of, + final_blocker)); + } + + // Queue a task to write out the symbol table. + final_blocker->add_blocker(); + workqueue->queue(new Write_symbols_task(symtab, + input_objects->target(), + layout->sympool(), + layout->dynpool(), + of, + final_blocker)); + + // Queue a task to write out everything else. + final_blocker->add_blocker(); + workqueue->queue(new Write_data_task(layout, symtab, + input_objects->target(), + of, final_blocker)); + + // Queue a task to close the output file. This will be blocked by + // FINAL_BLOCKER. + workqueue->queue(new Task_function(new Close_task_runner(of), + final_blocker)); +} + +} // End namespace gold. diff --git a/gold/gold.h b/gold/gold.h new file mode 100644 index 000000000000..288d9eb937bf --- /dev/null +++ b/gold/gold.h @@ -0,0 +1,214 @@ +// gold.h -- general definitions for gold -*- C++ -*- + +#ifndef GOLD_GOLD_H + +#include "config.h" +#include "ansidecl.h" + +#ifdef ENABLE_NLS +# include <libintl.h> +# define _(String) gettext (String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define gettext(Msgid) (Msgid) +# define dgettext(Domainname, Msgid) (Msgid) +# define dcgettext(Domainname, Msgid, Category) (Msgid) +# define textdomain(Domainname) while (0) /* nothing */ +# define bindtextdomain(Domainname, Dirname) while (0) /* nothing */ +# define _(String) (String) +# define N_(String) (String) +#endif + +// Figure out how to get a hash set and a hash map. + +#if defined(HAVE_TR1_UNORDERED_SET) && defined(HAVE_TR1_UNORDERED_MAP) + +#include <tr1/unordered_set> +#include <tr1/unordered_map> + +// We need a template typedef here. + +#define Unordered_set std::tr1::unordered_set +#define Unordered_map std::tr1::unordered_map + +#elif defined(HAVE_EXT_HASH_MAP) && defined(HAVE_EXT_HASH_SET) + +#include <ext/hash_map> +#include <ext/hash_set> +#include <string> + +#define Unordered_set __gnu_cxx::hash_set +#define Unordered_map __gnu_cxx::hash_map + +namespace __gnu_cxx +{ + +template<> +struct hash<std::string> +{ + size_t + operator()(std::string s) const + { return __stl_hash_string(s.c_str()); } +}; + +template<typename T> +struct hash<T*> +{ + size_t + operator()(T* p) const + { return reinterpret_cast<size_t>(p); } +}; + +} + +#else + +// The fallback is to just use set and map. + +#include <set> +#include <map> + +#define Unordered_set std::set +#define Unordered_map std::map + +#endif + +namespace gold +{ +// This is a hack to work around a problem with older versions of g++. +// The problem is that they don't support calling a member template by +// specifying the template parameters. It works to pass in an +// argument for argument dependent lookup. + +// To use this, the member template method declaration should put +// ACCEPT_SIZE or ACCEPT_SIZE_ENDIAN after the last parameter. If the +// method takes no parameters, use ACCEPT_SIZE_ONLY or +// ACCEPT_SIZE_ENDIAN_ONLY. + +// When calling the method, instead of using fn<size>, use fn +// SELECT_SIZE_NAME or SELECT_SIZE_ENDIAN_NAME. And after the last +// argument, put SELECT_SIZE(size) or SELECT_SIZE_ENDIAN(size, +// big_endian). If there is only one argment, use the _ONLY variants. + +#ifdef HAVE_MEMBER_TEMPLATE_SPECIFICATIONS + +#define SELECT_SIZE_NAME(size) <size> +#define SELECT_SIZE(size) +#define SELECT_SIZE_ONLY(size) +#define ACCEPT_SIZE +#define ACCEPT_SIZE_ONLY +#define ACCEPT_SIZE_EXPLICIT(size) + +#define SELECT_SIZE_ENDIAN_NAME(size, big_endian) <size, big_endian> +#define SELECT_SIZE_ENDIAN(size, big_endian) +#define SELECT_SIZE_ENDIAN_ONLY(size, big_endian) +#define ACCEPT_SIZE_ENDIAN +#define ACCEPT_SIZE_ENDIAN_ONLY +#define ACCEPT_SIZE_ENDIAN_EXPLICIT(size, big_endian) + +#else // !defined(HAVE_MEMBER_TEMPLATE_SPECIFICATIONS) + +template<int size> +class Select_size { }; +template<int size, bool big_endian> +class Select_size_endian { }; + +#define SELECT_SIZE_NAME(size) +#define SELECT_SIZE(size) , Select_size<size>() +#define SELECT_SIZE_ONLY(size) Select_size<size>() +#define ACCEPT_SIZE , Select_size<size> +#define ACCEPT_SIZE_ONLY Select_size<size> +#define ACCEPT_SIZE_EXPLICIT(size) , Select_size<size> + +#define SELECT_SIZE_ENDIAN_NAME(size, big_endian) +#define SELECT_SIZE_ENDIAN(size, big_endian) \ + , Select_size_endian<size, big_endian>() +#define SELECT_SIZE_ENDIAN_ONLY(size, big_endian) \ + Select_size_endian<size, big_endian>() +#define ACCEPT_SIZE_ENDIAN , Select_size_endian<size, big_endian> +#define ACCEPT_SIZE_ENDIAN_ONLY Select_size_endian<size, big_endian> +#define ACCEPT_SIZE_ENDIAN_EXPLICIT(size, big_endian) \ + , Select_size_endian<size, big_endian> + +#endif // !defined(HAVE_MEMBER_TEMPLATE_SPECIFICATIONS) + +} // End namespace gold. + +namespace gold +{ + +class General_options; +class Command_line; +class Input_argument_list; +class Dirsearch; +class Input_objects; +class Symbol_table; +class Layout; +class Workqueue; +class Output_file; + +// The name of the program as used in error messages. +extern const char* program_name; + +// This function is called to exit the program. Status is true to +// exit success (0) and false to exit failure (1). +extern void +gold_exit(bool status) ATTRIBUTE_NORETURN; + +// This function is called to emit an unexpected error message and a +// newline, and then exit with failure. If PERRNO is true, it reports +// the error in errno. +extern void +gold_fatal(const char* msg, bool perrno) ATTRIBUTE_NORETURN; + +// This is function is called in some cases if we run out of memory. +extern void +gold_nomem() ATTRIBUTE_NORETURN; + +// This macro and function are used in cases which can not arise if +// the code is written correctly. + +#define gold_unreachable() \ + (gold::do_gold_unreachable(__FILE__, __LINE__, __FUNCTION__)) + +extern void do_gold_unreachable(const char*, int, const char*) + ATTRIBUTE_NORETURN; + +// Assertion check. + +#define gold_assert(expr) ((void)(!(expr) ? gold_unreachable(), 0 : 0)) + +// Queue up the first set of tasks. +extern void +queue_initial_tasks(const General_options&, + const Dirsearch&, + const Command_line&, + Workqueue*, + Input_objects*, + Symbol_table*, + Layout*); + +// Queue up the middle set of tasks. +extern void +queue_middle_tasks(const General_options&, + const Input_objects*, + Symbol_table*, + Layout*, + Workqueue*); + +// Queue up the final set of tasks. +extern void +queue_final_tasks(const General_options&, + const Input_objects*, + const Symbol_table*, + const Layout*, + Workqueue*, + Output_file* of); + +} // End namespace gold. + +#endif // !defined(GOLD_GOLD_H) diff --git a/gold/i386.cc b/gold/i386.cc new file mode 100644 index 000000000000..448453aa3495 --- /dev/null +++ b/gold/i386.cc @@ -0,0 +1,1517 @@ +// i386.cc -- i386 target support for gold. + +#include "gold.h" + +#include <cstring> + +#include "elfcpp.h" +#include "reloc.h" +#include "i386.h" +#include "object.h" +#include "symtab.h" +#include "layout.h" +#include "output.h" +#include "target.h" +#include "target-reloc.h" +#include "target-select.h" + +namespace +{ + +using namespace gold; + +class Output_data_plt_i386; + +// The i386 target class. + +class Target_i386 : public Sized_target<32, false> +{ + public: + typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, false> Reloc_section; + + Target_i386() + : Sized_target<32, false>(&i386_info), + got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL), + copy_relocs_(NULL), dynbss_(NULL) + { } + + // Scan the relocations to look for symbol adjustments. + void + scan_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<32, false>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_symbol_count, + const unsigned char* plocal_symbols, + Symbol** global_symbols); + + // Finalize the sections. + void + do_finalize_sections(const General_options*, Layout*); + + // Relocate a section. + void + relocate_section(const Relocate_info<32, false>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr view_address, + off_t view_size); + + private: + // The class which scans relocations. + struct Scan + { + inline void + local(const General_options& options, Symbol_table* symtab, + Layout* layout, Target_i386* target, + Sized_relobj<32, false>* object, + unsigned int data_shndx, + const elfcpp::Rel<32, false>& reloc, unsigned int r_type, + const elfcpp::Sym<32, false>& lsym); + + inline void + global(const General_options& options, Symbol_table* symtab, + Layout* layout, Target_i386* target, + Sized_relobj<32, false>* object, + unsigned int data_shndx, + const elfcpp::Rel<32, false>& reloc, unsigned int r_type, + Symbol* gsym); + }; + + // The class which implements relocation. + class Relocate + { + public: + Relocate() + : skip_call_tls_get_addr_(false) + { } + + ~Relocate() + { + if (this->skip_call_tls_get_addr_) + { + // FIXME: This needs to specify the location somehow. + fprintf(stderr, _("%s: missing expected TLS relocation\n"), + program_name); + gold_exit(false); + } + } + + // Do a relocation. Return false if the caller should not issue + // any warnings about this relocation. + inline bool + relocate(const Relocate_info<32, false>*, Target_i386*, size_t relnum, + const elfcpp::Rel<32, false>&, + unsigned int r_type, const Sized_symbol<32>*, + const Symbol_value<32>*, + unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, + off_t); + + private: + // Do a TLS relocation. + inline void + relocate_tls(const Relocate_info<32, false>*, size_t relnum, + const elfcpp::Rel<32, false>&, + unsigned int r_type, const Sized_symbol<32>*, + const Symbol_value<32>*, + unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, off_t); + + // Do a TLS Initial-Exec to Local-Exec transition. + static inline void + tls_ie_to_le(const Relocate_info<32, false>*, size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>&, unsigned int r_type, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size); + + // Do a TLS Global-Dynamic to Local-Exec transition. + inline void + tls_gd_to_le(const Relocate_info<32, false>*, size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>&, unsigned int r_type, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size); + + // Check the range for a TLS relocation. + static inline void + check_range(const Relocate_info<32, false>*, size_t relnum, + const elfcpp::Rel<32, false>&, off_t, off_t); + + // Check the validity of a TLS relocation. This is like assert. + static inline void + check_tls(const Relocate_info<32, false>*, size_t relnum, + const elfcpp::Rel<32, false>&, bool); + + // This is set if we should skip the next reloc, which should be a + // PLT32 reloc against ___tls_get_addr. + bool skip_call_tls_get_addr_; + }; + + // Adjust TLS relocation type based on the options and whether this + // is a local symbol. + static unsigned int + optimize_tls_reloc(const General_options*, bool is_final, int r_type); + + // Get the GOT section, creating it if necessary. + Output_data_got<32, false>* + got_section(const General_options*, Symbol_table*, Layout*); + + // Create a PLT entry for a global symbol. + void + make_plt_entry(const General_options* options, Symbol_table*, + Layout*, Symbol*); + + // Get the PLT section. + Output_data_plt_i386* + plt_section() const + { + gold_assert(this->plt_ != NULL); + return this->plt_; + } + + // Get the dynamic reloc section, creating it if necessary. + Reloc_section* + rel_dyn_section(Layout*); + + // Copy a relocation against a global symbol. + void + copy_reloc(const General_options*, Symbol_table*, Layout*, + Sized_relobj<32, false>*, unsigned int, + Symbol*, const elfcpp::Rel<32, false>&); + + // Information about this specific target which we pass to the + // general Target structure. + static const Target::Target_info i386_info; + + // The GOT section. + Output_data_got<32, false>* got_; + // The PLT section. + Output_data_plt_i386* plt_; + // The GOT PLT section. + Output_data_space* got_plt_; + // The dynamic reloc section. + Reloc_section* rel_dyn_; + // Relocs saved to avoid a COPY reloc. + Copy_relocs<32, false>* copy_relocs_; + // Space for variables copied with a COPY reloc. + Output_data_space* dynbss_; +}; + +const Target::Target_info Target_i386::i386_info = +{ + 32, // size + false, // is_big_endian + elfcpp::EM_386, // machine_code + false, // has_make_symbol + 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_data_got<32, false>* +Target_i386::got_section(const General_options* options, Symbol_table* symtab, + Layout* layout) +{ + if (this->got_ == NULL) + { + gold_assert(options != NULL && symtab != NULL && layout != NULL); + + this->got_ = new Output_data_got<32, false>(options); + + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, + this->got_); + + // The old GNU linker creates a .got.plt section. We just + // create another set of data in the .got section. Note that we + // always create a PLT if we create a GOT, although the PLT + // might be empty. + this->got_plt_ = new Output_data_space(4); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, + this->got_plt_); + + // The first three entries are reserved. + this->got_plt_->set_space_size(3 * 4); + + // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT. + symtab->define_in_output_data(this, "_GLOBAL_OFFSET_TABLE_", NULL, + this->got_plt_, + 0, 0, elfcpp::STT_OBJECT, + elfcpp::STB_LOCAL, + elfcpp::STV_HIDDEN, 0, + false, false); + } + + return this->got_; +} + +// Get the dynamic reloc section, creating it if necessary. + +Target_i386::Reloc_section* +Target_i386::rel_dyn_section(Layout* layout) +{ + if (this->rel_dyn_ == NULL) + { + gold_assert(layout != NULL); + this->rel_dyn_ = new Reloc_section(); + layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->rel_dyn_); + } + return this->rel_dyn_; +} + +// A class to handle the PLT data. + +class Output_data_plt_i386 : public Output_section_data +{ + public: + typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, false> Reloc_section; + + Output_data_plt_i386(Layout*, Output_data_space*, bool is_shared); + + // Add an entry to the PLT. + void + add_entry(Symbol* gsym); + + // Return the .rel.plt section data. + const Reloc_section* + rel_plt() const + { return this->rel_; } + + protected: + void + do_adjust_output_section(Output_section* os); + + private: + // The size of an entry in the PLT. + static const int plt_entry_size = 16; + + // The first entry in the PLT for an executable. + static unsigned char exec_first_plt_entry[plt_entry_size]; + + // The first entry in the PLT for a shared object. + static unsigned char dyn_first_plt_entry[plt_entry_size]; + + // Other entries in the PLT for an executable. + static unsigned char exec_plt_entry[plt_entry_size]; + + // Other entries in the PLT for a shared object. + static unsigned char dyn_plt_entry[plt_entry_size]; + + // Set the final size. + void + do_set_address(uint64_t, off_t) + { this->set_data_size((this->count_ + 1) * plt_entry_size); } + + // Write out the PLT data. + void + do_write(Output_file*); + + // The reloc section. + Reloc_section* rel_; + // The .got.plt section. + Output_data_space* got_plt_; + // The number of PLT entries. + unsigned int count_; + // Whether we are generated a shared object. + bool is_shared_; +}; + +// Create the PLT section. The ordinary .got section is an argument, +// since we need to refer to the start. We also create our own .got +// section just for PLT entries. + +Output_data_plt_i386::Output_data_plt_i386(Layout* layout, + Output_data_space* got_plt, + bool is_shared) + : Output_section_data(4), got_plt_(got_plt), is_shared_(is_shared) +{ + this->rel_ = new Reloc_section(); + layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->rel_); +} + +// For some reason + +void +Output_data_plt_i386::do_adjust_output_section(Output_section* os) +{ + // UnixWare sets the entsize of .plt to 4, and so does the old GNU + // linker, and so do we. + os->set_entsize(4); +} + +// Add an entry to the PLT. + +void +Output_data_plt_i386::add_entry(Symbol* gsym) +{ + gold_assert(!gsym->has_plt_offset()); + + // Note that when setting the PLT offset we skip the initial + // reserved PLT entry. + gsym->set_plt_offset((this->count_ + 1) * plt_entry_size); + + ++this->count_; + + off_t got_offset = this->got_plt_->data_size(); + + // Every PLT entry needs a GOT entry which points back to the PLT + // entry (this will be changed by the dynamic linker, normally + // lazily when the function is called). + this->got_plt_->set_space_size(got_offset + 4); + + // Every PLT entry needs a reloc. + gsym->set_needs_dynsym_entry(); + this->rel_->add_global(gsym, elfcpp::R_386_JUMP_SLOT, this->got_plt_, + got_offset); + + // Note that we don't need to save the symbol. The contents of the + // PLT are independent of which symbols are used. The symbols only + // appear in the relocations. +} + +// The first entry in the PLT for an executable. + +unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] = +{ + 0xff, 0x35, // pushl contents of memory address + 0, 0, 0, 0, // replaced with address of .got + 4 + 0xff, 0x25, // jmp indirect + 0, 0, 0, 0, // replaced with address of .got + 8 + 0, 0, 0, 0 // unused +}; + +// The first entry in the PLT for a shared object. + +unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] = +{ + 0xff, 0xb3, 4, 0, 0, 0, // pushl 4(%ebx) + 0xff, 0xa3, 8, 0, 0, 0, // jmp *8(%ebx) + 0, 0, 0, 0 // unused +}; + +// Subsequent entries in the PLT for an executable. + +unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] = +{ + 0xff, 0x25, // jmp indirect + 0, 0, 0, 0, // replaced with address of symbol in .got + 0x68, // pushl immediate + 0, 0, 0, 0, // replaced with offset into relocation table + 0xe9, // jmp relative + 0, 0, 0, 0 // replaced with offset to start of .plt +}; + +// Subsequent entries in the PLT for a shared object. + +unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] = +{ + 0xff, 0xa3, // jmp *offset(%ebx) + 0, 0, 0, 0, // replaced with offset of symbol in .got + 0x68, // pushl immediate + 0, 0, 0, 0, // replaced with offset into relocation table + 0xe9, // jmp relative + 0, 0, 0, 0 // replaced with offset to start of .plt +}; + +// Write out the PLT. This uses the hand-coded instructions above, +// and adjusts them as needed. This is all specified by the i386 ELF +// Processor Supplement. + +void +Output_data_plt_i386::do_write(Output_file* of) +{ + const off_t offset = this->offset(); + const off_t oview_size = this->data_size(); + unsigned char* const oview = of->get_output_view(offset, oview_size); + + const off_t got_file_offset = this->got_plt_->offset(); + const off_t got_size = this->got_plt_->data_size(); + unsigned char* const got_view = of->get_output_view(got_file_offset, + got_size); + + unsigned char* pov = oview; + + elfcpp::Elf_types<32>::Elf_Addr plt_address = this->address(); + elfcpp::Elf_types<32>::Elf_Addr got_address = this->got_plt_->address(); + + if (this->is_shared_) + memcpy(pov, dyn_first_plt_entry, plt_entry_size); + else + { + memcpy(pov, exec_first_plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_address + 4); + elfcpp::Swap<32, false>::writeval(pov + 8, got_address + 8); + } + pov += plt_entry_size; + + unsigned char* got_pov = got_view; + + memset(got_pov, 0, 12); + got_pov += 12; + + const int rel_size = elfcpp::Elf_sizes<32>::rel_size; + + unsigned int plt_offset = plt_entry_size; + unsigned int plt_rel_offset = 0; + unsigned int got_offset = 12; + const unsigned int count = this->count_; + for (unsigned int i = 0; + i < count; + ++i, + pov += plt_entry_size, + got_pov += 4, + plt_offset += plt_entry_size, + plt_rel_offset += rel_size, + got_offset += 4) + { + // Set and adjust the PLT entry itself. + + if (this->is_shared_) + { + memcpy(pov, dyn_plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_offset); + } + else + { + memcpy(pov, exec_plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, + (got_address + + got_offset)); + } + + elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_rel_offset); + elfcpp::Swap<32, false>::writeval(pov + 12, + - (plt_offset + plt_entry_size)); + + // Set the entry in the GOT. + elfcpp::Swap<32, false>::writeval(got_pov, plt_address + plt_offset + 6); + } + + gold_assert(pov - oview == oview_size); + gold_assert(got_pov - got_view == got_size); + + of->write_output_view(offset, oview_size, oview); + of->write_output_view(got_file_offset, got_size, got_view); +} + +// Create a PLT entry for a global symbol. + +void +Target_i386::make_plt_entry(const General_options* options, + Symbol_table* symtab, Layout* layout, Symbol* gsym) +{ + if (gsym->has_plt_offset()) + return; + + if (this->plt_ == NULL) + { + // Create the GOT sections first. + this->got_section(options, symtab, layout); + + this->plt_ = new Output_data_plt_i386(layout, this->got_plt_, + options->is_shared()); + layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_EXECINSTR), + this->plt_); + } + + this->plt_->add_entry(gsym); +} + +// Handle a relocation against a non-function symbol defined in a +// dynamic object. The traditional way to handle this is to generate +// a COPY relocation to copy the variable at runtime from the shared +// object into the executable's data segment. However, this is +// undesirable in general, as if the size of the object changes in the +// dynamic object, the executable will no longer work correctly. If +// this relocation is in a writable section, then we can create a +// dynamic reloc and the dynamic linker will resolve it to the correct +// address at runtime. However, we do not want do that if the +// relocation is in a read-only section, as it would prevent the +// readonly segment from being shared. And if we have to eventually +// generate a COPY reloc, then any dynamic relocations will be +// useless. So this means that if this is a writable section, we need +// to save the relocation until we see whether we have to create a +// COPY relocation for this symbol for any other relocation. + +void +Target_i386::copy_reloc(const General_options* options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<32, false>* object, + unsigned int data_shndx, Symbol* gsym, + const elfcpp::Rel<32, false>& rel) +{ + Sized_symbol<32>* ssym; + ssym = symtab->get_sized_symbol SELECT_SIZE_NAME(32) (gsym + SELECT_SIZE(32)); + + if (!Copy_relocs<32, false>::need_copy_reloc(options, object, + data_shndx, ssym)) + { + // So far we do not need a COPY reloc. Save this relocation. + // If it turns out that we never need a COPY reloc for this + // symbol, then we will emit the relocation. + if (this->copy_relocs_ == NULL) + this->copy_relocs_ = new Copy_relocs<32, false>(); + this->copy_relocs_->save(ssym, object, data_shndx, rel); + } + else + { + // Allocate space for this symbol in the .bss section. + + elfcpp::Elf_types<32>::Elf_WXword symsize = ssym->symsize(); + + // There is no defined way to determine the required alignment + // of the symbol. We pick the alignment based on the size. We + // set an arbitrary maximum of 256. + unsigned int align; + for (align = 1; align < 512; align <<= 1) + if ((symsize & align) != 0) + break; + + if (this->dynbss_ == NULL) + { + this->dynbss_ = new Output_data_space(align); + layout->add_output_section_data(".bss", + elfcpp::SHT_NOBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->dynbss_); + } + + Output_data_space* dynbss = this->dynbss_; + + if (align > dynbss->addralign()) + dynbss->set_space_alignment(align); + + off_t dynbss_size = dynbss->data_size(); + dynbss_size = align_address(dynbss_size, align); + off_t offset = dynbss_size; + dynbss->set_space_size(dynbss_size + symsize); + + // Define the symbol in the .dynbss section. + symtab->define_in_output_data(this, ssym->name(), ssym->version(), + dynbss, offset, symsize, ssym->type(), + ssym->binding(), ssym->visibility(), + ssym->nonvis(), false, false); + + // Add the COPY reloc. + ssym->set_needs_dynsym_entry(); + Reloc_section* rel_dyn = this->rel_dyn_section(layout); + rel_dyn->add_global(ssym, elfcpp::R_386_COPY, dynbss, offset); + } +} + +// Optimize the TLS relocation type based on what we know about the +// symbol. IS_FINAL is true if the final address of this symbol is +// known at link time. + +unsigned int +Target_i386::optimize_tls_reloc(const General_options* options, + bool is_final, + int r_type) +{ + // If we are generating a shared library, then we can't do anything + // in the linker. + if (options->is_shared()) + return r_type; + + switch (r_type) + { + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + // These are Global-Dynamic which permits fully general TLS + // access. Since we know that we are generating an executable, + // we can convert this to Initial-Exec. If we also know that + // this is a local symbol, we can further switch to Local-Exec. + if (is_final) + return elfcpp::R_386_TLS_LE_32; + return elfcpp::R_386_TLS_IE_32; + + case elfcpp::R_386_TLS_LDM: + // This is Local-Dynamic, which refers to a local symbol in the + // dynamic TLS block. Since we know that we generating an + // executable, we can switch to Local-Exec. + return elfcpp::R_386_TLS_LE_32; + + case elfcpp::R_386_TLS_LDO_32: + // Another type of Local-Dynamic relocation. + return elfcpp::R_386_TLS_LE; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_IE_32: + // These are Initial-Exec relocs which get the thread offset + // from the GOT. If we know that we are linking against the + // local symbol, we can switch to Local-Exec, which links the + // thread offset into the instruction. + if (is_final) + return elfcpp::R_386_TLS_LE_32; + return r_type; + + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_LE_32: + // When we already have Local-Exec, there is nothing further we + // can do. + return r_type; + + default: + gold_unreachable(); + } +} + +// Scan a relocation for a local symbol. + +inline void +Target_i386::Scan::local(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Target_i386* target, + Sized_relobj<32, false>* object, + unsigned int, + const elfcpp::Rel<32, false>&, + unsigned int r_type, + const elfcpp::Sym<32, false>&) +{ + switch (r_type) + { + case elfcpp::R_386_NONE: + case elfcpp::R_386_GNU_VTINHERIT: + case elfcpp::R_386_GNU_VTENTRY: + break; + + case elfcpp::R_386_32: + case elfcpp::R_386_16: + case elfcpp::R_386_8: + // FIXME: If we are generating a shared object we need to copy + // this relocation into the object. + gold_assert(!options.is_shared()); + break; + + case elfcpp::R_386_PC32: + case elfcpp::R_386_PC16: + case elfcpp::R_386_PC8: + break; + + case elfcpp::R_386_GOTOFF: + case elfcpp::R_386_GOTPC: + // We need a GOT section. + target->got_section(&options, symtab, layout); + break; + + case elfcpp::R_386_COPY: + case elfcpp::R_386_GLOB_DAT: + case elfcpp::R_386_JUMP_SLOT: + case elfcpp::R_386_RELATIVE: + case elfcpp::R_386_TLS_TPOFF: + case elfcpp::R_386_TLS_DTPMOD32: + case elfcpp::R_386_TLS_DTPOFF32: + case elfcpp::R_386_TLS_TPOFF32: + case elfcpp::R_386_TLS_DESC: + fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"), + program_name, object->name().c_str(), r_type); + gold_exit(false); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_LE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + r_type = Target_i386::optimize_tls_reloc(&options, + !options.is_shared(), + r_type); + switch (r_type) + { + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_LE_32: + // FIXME: If generating a shared object, we need to copy + // this relocation into the object. + gold_assert(!options.is_shared()); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + fprintf(stderr, + _("%s: %s: unsupported reloc %u against local symbol\n"), + program_name, object->name().c_str(), r_type); + break; + } + break; + + case elfcpp::R_386_GOT32: + case elfcpp::R_386_PLT32: + case elfcpp::R_386_32PLT: + case elfcpp::R_386_TLS_GD_32: + case elfcpp::R_386_TLS_GD_PUSH: + case elfcpp::R_386_TLS_GD_CALL: + case elfcpp::R_386_TLS_GD_POP: + case elfcpp::R_386_TLS_LDM_32: + case elfcpp::R_386_TLS_LDM_PUSH: + case elfcpp::R_386_TLS_LDM_CALL: + case elfcpp::R_386_TLS_LDM_POP: + case elfcpp::R_386_USED_BY_INTEL_200: + default: + fprintf(stderr, _("%s: %s: unsupported reloc %u against local symbol\n"), + program_name, object->name().c_str(), r_type); + break; + } +} + +// Scan a relocation for a global symbol. + +inline void +Target_i386::Scan::global(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Target_i386* target, + Sized_relobj<32, false>* object, + unsigned int data_shndx, + const elfcpp::Rel<32, false>& reloc, + unsigned int r_type, + Symbol* gsym) +{ + switch (r_type) + { + case elfcpp::R_386_NONE: + case elfcpp::R_386_GNU_VTINHERIT: + case elfcpp::R_386_GNU_VTENTRY: + break; + + case elfcpp::R_386_32: + case elfcpp::R_386_PC32: + case elfcpp::R_386_16: + case elfcpp::R_386_PC16: + case elfcpp::R_386_8: + case elfcpp::R_386_PC8: + // FIXME: If we are generating a shared object we may need to + // copy this relocation into the object. If this symbol is + // defined in a shared object, we may need to copy this + // relocation in order to avoid a COPY relocation. + gold_assert(!options.is_shared()); + + if (gsym->is_from_dynobj()) + { + // This symbol is defined in a dynamic object. If it is a + // function, we make a PLT entry. Otherwise we need to + // either generate a COPY reloc or copy this reloc. + if (gsym->type() == elfcpp::STT_FUNC) + target->make_plt_entry(&options, symtab, layout, gsym); + else + target->copy_reloc(&options, symtab, layout, object, data_shndx, + gsym, reloc); + } + + break; + + case elfcpp::R_386_GOT32: + // The symbol requires a GOT entry. + if (target->got_section(&options, symtab, layout)->add_global(gsym)) + { + // If this symbol is not fully resolved, we need to add a + // dynamic relocation for it. + if (!gsym->final_value_is_known(&options)) + gold_unreachable(); + } + break; + + case elfcpp::R_386_PLT32: + // If the symbol is fully resolved, this is just a PC32 reloc. + // Otherwise we need a PLT entry. + if (gsym->final_value_is_known(&options)) + break; + target->make_plt_entry(&options, symtab, layout, gsym); + break; + + case elfcpp::R_386_GOTOFF: + case elfcpp::R_386_GOTPC: + // We need a GOT section. + target->got_section(&options, symtab, layout); + break; + + case elfcpp::R_386_COPY: + case elfcpp::R_386_GLOB_DAT: + case elfcpp::R_386_JUMP_SLOT: + case elfcpp::R_386_RELATIVE: + case elfcpp::R_386_TLS_TPOFF: + case elfcpp::R_386_TLS_DTPMOD32: + case elfcpp::R_386_TLS_DTPOFF32: + case elfcpp::R_386_TLS_TPOFF32: + case elfcpp::R_386_TLS_DESC: + fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"), + program_name, object->name().c_str(), r_type); + gold_exit(false); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_LE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + { + const bool is_final = gsym->final_value_is_known(&options); + r_type = Target_i386::optimize_tls_reloc(&options, is_final, r_type); + switch (r_type) + { + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_LE_32: + // FIXME: If generating a shared object, we need to copy + // this relocation into the object. + gold_assert(!options.is_shared()); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + fprintf(stderr, + _("%s: %s: unsupported reloc %u " + "against global symbol %s\n"), + program_name, object->name().c_str(), r_type, + gsym->name()); + break; + } + } + break; + + case elfcpp::R_386_32PLT: + case elfcpp::R_386_TLS_GD_32: + case elfcpp::R_386_TLS_GD_PUSH: + case elfcpp::R_386_TLS_GD_CALL: + case elfcpp::R_386_TLS_GD_POP: + case elfcpp::R_386_TLS_LDM_32: + case elfcpp::R_386_TLS_LDM_PUSH: + case elfcpp::R_386_TLS_LDM_CALL: + case elfcpp::R_386_TLS_LDM_POP: + case elfcpp::R_386_USED_BY_INTEL_200: + default: + fprintf(stderr, + _("%s: %s: unsupported reloc %u against global symbol %s\n"), + program_name, object->name().c_str(), r_type, gsym->name()); + break; + } +} + +// Scan relocations for a section. + +void +Target_i386::scan_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<32, false>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_symbol_count, + const unsigned char* plocal_symbols, + Symbol** global_symbols) +{ + if (sh_type == elfcpp::SHT_RELA) + { + fprintf(stderr, _("%s: %s: unsupported RELA reloc section\n"), + program_name, object->name().c_str()); + gold_exit(false); + } + + gold::scan_relocs<32, false, Target_i386, elfcpp::SHT_REL, + Target_i386::Scan>( + options, + symtab, + layout, + this, + object, + data_shndx, + prelocs, + reloc_count, + local_symbol_count, + plocal_symbols, + global_symbols); +} + +// Finalize the sections. + +void +Target_i386::do_finalize_sections(const General_options* options, + Layout* layout) +{ + // Fill in some more dynamic tags. + Output_data_dynamic* const odyn = layout->dynamic_data(); + if (odyn != NULL) + { + if (this->got_plt_ != NULL) + odyn->add_section_address(elfcpp::DT_PLTGOT, this->got_plt_); + + if (this->plt_ != NULL) + { + const Output_data* od = this->plt_->rel_plt(); + odyn->add_section_size(elfcpp::DT_PLTRELSZ, od); + odyn->add_section_address(elfcpp::DT_JMPREL, od); + odyn->add_constant(elfcpp::DT_PLTREL, elfcpp::DT_REL); + } + + if (this->rel_dyn_ != NULL) + { + const Output_data* od = this->rel_dyn_; + odyn->add_section_address(elfcpp::DT_REL, od); + odyn->add_section_size(elfcpp::DT_RELSZ, od); + odyn->add_constant(elfcpp::DT_RELENT, + elfcpp::Elf_sizes<32>::rel_size); + } + + if (!options->is_shared()) + { + // The value of the DT_DEBUG tag is filled in by the dynamic + // linker at run time, and used by the debugger. + odyn->add_constant(elfcpp::DT_DEBUG, 0); + } + } + + // Emit any relocs we saved in an attempt to avoid generating COPY + // relocs. + if (this->copy_relocs_ == NULL) + return; + if (this->copy_relocs_->any_to_emit()) + { + Reloc_section* rel_dyn = this->rel_dyn_section(layout); + this->copy_relocs_->emit(rel_dyn); + } + delete this->copy_relocs_; + this->copy_relocs_ = NULL; +} + +// Perform a relocation. + +inline bool +Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, + Target_i386* target, + size_t relnum, + const elfcpp::Rel<32, false>& rel, + unsigned int r_type, + const Sized_symbol<32>* gsym, + const Symbol_value<32>* psymval, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr address, + off_t view_size) +{ + if (this->skip_call_tls_get_addr_) + { + if (r_type != elfcpp::R_386_PLT32 + || gsym == NULL + || strcmp(gsym->name(), "___tls_get_addr") != 0) + { + fprintf(stderr, _("%s: %s: missing expected TLS relocation\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } + + this->skip_call_tls_get_addr_ = false; + + return false; + } + + // Pick the value to use for symbols defined in shared objects. + Symbol_value<32> symval; + if (gsym != NULL && gsym->is_from_dynobj()) + { + if (!gsym->has_plt_offset()) + gold_unreachable(); + + symval.set_output_value(target->plt_section()->address() + + gsym->plt_offset()); + psymval = &symval; + } + + const Sized_relobj<32, false>* object = relinfo->object; + + switch (r_type) + { + case elfcpp::R_386_NONE: + case elfcpp::R_386_GNU_VTINHERIT: + case elfcpp::R_386_GNU_VTENTRY: + break; + + case elfcpp::R_386_32: + Relocate_functions<32, false>::rel32(view, object, psymval); + break; + + case elfcpp::R_386_PC32: + Relocate_functions<32, false>::pcrel32(view, object, psymval, address); + break; + + case elfcpp::R_386_16: + Relocate_functions<32, false>::rel16(view, object, psymval); + break; + + case elfcpp::R_386_PC16: + Relocate_functions<32, false>::pcrel16(view, object, psymval, address); + break; + + case elfcpp::R_386_8: + Relocate_functions<32, false>::rel8(view, object, psymval); + break; + + case elfcpp::R_386_PC8: + Relocate_functions<32, false>::pcrel8(view, object, psymval, address); + break; + + case elfcpp::R_386_PLT32: + gold_assert(gsym->has_plt_offset() + || gsym->final_value_is_known(relinfo->options)); + Relocate_functions<32, false>::pcrel32(view, object, psymval, address); + break; + + case elfcpp::R_386_GOT32: + // Local GOT offsets not yet supported. + gold_assert(gsym); + gold_assert(gsym->has_got_offset()); + Relocate_functions<32, false>::rel32(view, gsym->got_offset()); + break; + + case elfcpp::R_386_GOTOFF: + { + elfcpp::Elf_types<32>::Elf_Addr value; + value = (psymval->value(object, 0) + - target->got_section(NULL, NULL, NULL)->address()); + Relocate_functions<32, false>::rel32(view, value); + } + break; + + case elfcpp::R_386_GOTPC: + { + elfcpp::Elf_types<32>::Elf_Addr value; + value = target->got_section(NULL, NULL, NULL)->address(); + Relocate_functions<32, false>::pcrel32(view, value, address); + } + break; + + case elfcpp::R_386_COPY: + case elfcpp::R_386_GLOB_DAT: + case elfcpp::R_386_JUMP_SLOT: + case elfcpp::R_386_RELATIVE: + case elfcpp::R_386_TLS_TPOFF: + case elfcpp::R_386_TLS_DTPMOD32: + case elfcpp::R_386_TLS_DTPOFF32: + case elfcpp::R_386_TLS_TPOFF32: + case elfcpp::R_386_TLS_DESC: + fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + gold_exit(false); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_LE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + this->relocate_tls(relinfo, relnum, rel, r_type, gsym, psymval, view, + address, view_size); + break; + + case elfcpp::R_386_32PLT: + case elfcpp::R_386_TLS_GD_32: + case elfcpp::R_386_TLS_GD_PUSH: + case elfcpp::R_386_TLS_GD_CALL: + case elfcpp::R_386_TLS_GD_POP: + case elfcpp::R_386_TLS_LDM_32: + case elfcpp::R_386_TLS_LDM_PUSH: + case elfcpp::R_386_TLS_LDM_CALL: + case elfcpp::R_386_TLS_LDM_POP: + case elfcpp::R_386_USED_BY_INTEL_200: + default: + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + // gold_exit(false); + break; + } + + return true; +} + +// Perform a TLS relocation. + +inline void +Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, + size_t relnum, + const elfcpp::Rel<32, false>& rel, + unsigned int r_type, + const Sized_symbol<32>* gsym, + const Symbol_value<32>* psymval, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr, + off_t view_size) +{ + Output_segment* tls_segment = relinfo->layout->tls_segment(); + if (tls_segment == NULL) + { + fprintf(stderr, _("%s: %s: TLS reloc but no TLS segment\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } + + elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(relinfo->object, 0); + + const bool is_final = (gsym == NULL + ? !relinfo->options->is_shared() + : gsym->final_value_is_known(relinfo->options)); + const unsigned int opt_r_type = + Target_i386::optimize_tls_reloc(relinfo->options, is_final, r_type); + switch (r_type) + { + case elfcpp::R_386_TLS_LE_32: + value = tls_segment->vaddr() + tls_segment->memsz() - value; + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_TLS_LE: + value = value - (tls_segment->vaddr() + tls_segment->memsz()); + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_IE_32: + if (opt_r_type == elfcpp::R_386_TLS_LE_32) + { + Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment, + rel, r_type, value, view, + view_size); + break; + } + fprintf(stderr, _("%s: %s: unsupported reloc type %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + // gold_exit(false); + break; + + case elfcpp::R_386_TLS_GD: + if (opt_r_type == elfcpp::R_386_TLS_LE_32) + { + this->tls_gd_to_le(relinfo, relnum, tls_segment, + rel, r_type, value, view, + view_size); + break; + } + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + // gold_exit(false); + break; + + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + // gold_exit(false); + break; + } +} + +// Do a relocation in which we convert a TLS Initial-Exec to a +// Local-Exec. + +inline void +Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo, + size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>& rel, + unsigned int r_type, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size) +{ + // We have to actually change the instructions, which means that we + // need to examine the opcodes to figure out which instruction we + // are looking at. + if (r_type == elfcpp::R_386_TLS_IE) + { + // movl %gs:XX,%eax ==> movl $YY,%eax + // movl %gs:XX,%reg ==> movl $YY,%reg + // addl %gs:XX,%reg ==> addl $YY,%reg + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -1); + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 4); + + unsigned char op1 = view[-1]; + if (op1 == 0xa1) + { + // movl XX,%eax ==> movl $YY,%eax + view[-1] = 0xb8; + } + else + { + Target_i386::Relocate::check_range(relinfo, relnum, rel, + view_size, -2); + + unsigned char op2 = view[-2]; + if (op2 == 0x8b) + { + // movl XX,%reg ==> movl $YY,%reg + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xc7) == 0x05); + view[-2] = 0xc7; + view[-1] = 0xc0 | ((op1 >> 3) & 7); + } + else if (op2 == 0x03) + { + // addl XX,%reg ==> addl $YY,%reg + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xc7) == 0x05); + view[-2] = 0x81; + view[-1] = 0xc0 | ((op1 >> 3) & 7); + } + else + Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0); + } + } + else + { + // subl %gs:XX(%reg1),%reg2 ==> subl $YY,%reg2 + // movl %gs:XX(%reg1),%reg2 ==> movl $YY,%reg2 + // addl %gs:XX(%reg1),%reg2 ==> addl $YY,$reg2 + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2); + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 4); + + unsigned char op1 = view[-1]; + unsigned char op2 = view[-2]; + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xc0) == 0x80 && (op1 & 7) != 4); + if (op2 == 0x8b) + { + // movl %gs:XX(%reg1),%reg2 ==> movl $YY,%reg2 + view[-2] = 0xc7; + view[-1] = 0xc0 | ((op1 >> 3) & 7); + } + else if (op2 == 0x2b) + { + // subl %gs:XX(%reg1),%reg2 ==> subl $YY,%reg2 + view[-2] = 0x81; + view[-1] = 0xe8 | ((op1 >> 3) & 7); + } + else if (op2 == 0x03) + { + // addl %gs:XX(%reg1),%reg2 ==> addl $YY,$reg2 + view[-2] = 0x81; + view[-1] = 0xc0 | ((op1 >> 3) & 7); + } + else + Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0); + } + + value = tls_segment->vaddr() + tls_segment->memsz() - value; + if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE) + value = - value; + + Relocate_functions<32, false>::rel32(view, value); +} + +// Do a relocation in which we convert a TLS Global-Dynamic to a +// Local-Exec. + +inline void +Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, + size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>& rel, + unsigned int, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size) +{ + // leal foo(,%reg,1),%eax; call ___tls_get_addr + // ==> movl %gs,0,%eax; subl $foo@tpoff,%eax + // leal foo(%reg),%eax; call ___tls_get_addr + // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax + + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2); + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 9); + + unsigned char op1 = view[-1]; + unsigned char op2 = view[-2]; + + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + op2 == 0x8d || op2 == 0x04); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + view[4] == 0xe8); + + int roff = 5; + + if (op2 == 0x04) + { + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -3); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + view[-3] == 0x8d); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + ((op1 & 0xc7) == 0x05 + && op1 != (4 << 3))); + memcpy(view - 3, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); + } + else + { + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xf8) == 0x80 && (op1 & 7) != 4); + if (rel.get_r_offset() + 9 < view_size && view[9] == 0x90) + { + // There is a trailing nop. Use the size byte subl. + memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); + roff = 6; + } + else + { + // Use the five byte subl. + memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11); + } + } + + value = tls_segment->vaddr() + tls_segment->memsz() - value; + Relocate_functions<32, false>::rel32(view + roff, value); + + // The next reloc should be a PLT32 reloc against __tls_get_addr. + // We can skip it. + this->skip_call_tls_get_addr_ = true; +} + +// Check the range for a TLS relocation. + +inline void +Target_i386::Relocate::check_range(const Relocate_info<32, false>* relinfo, + size_t relnum, + const elfcpp::Rel<32, false>& rel, + off_t view_size, off_t off) +{ + off_t offset = rel.get_r_offset() + off; + if (offset < 0 || offset > view_size) + { + fprintf(stderr, _("%s: %s: TLS relocation out of range\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } +} + +// Check the validity of a TLS relocation. This is like assert. + +inline void +Target_i386::Relocate::check_tls(const Relocate_info<32, false>* relinfo, + size_t relnum, + const elfcpp::Rel<32, false>& rel, + bool valid) +{ + if (!valid) + { + fprintf(stderr, + _("%s: %s: TLS relocation against invalid instruction\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } +} + +// Relocate section data. + +void +Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr address, + off_t view_size) +{ + gold_assert(sh_type == elfcpp::SHT_REL); + + gold::relocate_section<32, false, Target_i386, elfcpp::SHT_REL, + Target_i386::Relocate>( + relinfo, + this, + prelocs, + reloc_count, + view, + address, + view_size); +} + +// The selector for i386 object files. + +class Target_selector_i386 : public Target_selector +{ +public: + Target_selector_i386() + : Target_selector(elfcpp::EM_386, 32, false) + { } + + Target* + recognize(int machine, int osabi, int abiversion); + + private: + Target_i386* target_; +}; + +// Recognize an i386 object file when we already know that the machine +// number is EM_386. + +Target* +Target_selector_i386::recognize(int, int, int) +{ + if (this->target_ == NULL) + this->target_ = new Target_i386(); + return this->target_; +} + +Target_selector_i386 target_selector_i386; + +} // End anonymous namespace. diff --git a/gold/layout.cc b/gold/layout.cc new file mode 100644 index 000000000000..62ba5f30dec9 --- /dev/null +++ b/gold/layout.cc @@ -0,0 +1,1440 @@ +// layout.cc -- lay out output file sections for gold + +#include "gold.h" + +#include <cstring> +#include <algorithm> +#include <iostream> +#include <utility> + +#include "output.h" +#include "symtab.h" +#include "dynobj.h" +#include "layout.h" + +namespace gold +{ + +// Layout_task_runner methods. + +// Lay out the sections. This is called after all the input objects +// have been read. + +void +Layout_task_runner::run(Workqueue* workqueue) +{ + off_t file_size = this->layout_->finalize(this->input_objects_, + this->symtab_); + + // Now we know the final size of the output file and we know where + // each piece of information goes. + Output_file* of = new Output_file(this->options_); + of->open(file_size); + + // Queue up the final set of tasks. + gold::queue_final_tasks(this->options_, this->input_objects_, + this->symtab_, this->layout_, workqueue, of); +} + +// Layout methods. + +Layout::Layout(const General_options& options) + : options_(options), namepool_(), sympool_(), dynpool_(), signatures_(), + section_name_map_(), segment_list_(), section_list_(), + unattached_section_list_(), special_output_list_(), + tls_segment_(NULL), symtab_section_(NULL), + dynsym_section_(NULL), dynamic_section_(NULL), dynamic_data_(NULL) +{ + // Make space for more than enough segments for a typical file. + // This is just for efficiency--it's OK if we wind up needing more. + this->segment_list_.reserve(12); + + // We expect three unattached Output_data objects: the file header, + // the segment headers, and the section headers. + this->special_output_list_.reserve(3); +} + +// Hash a key we use to look up an output section mapping. + +size_t +Layout::Hash_key::operator()(const Layout::Key& k) const +{ + return k.first + k.second.first + k.second.second; +} + +// Whether to include this section in the link. + +template<int size, bool big_endian> +bool +Layout::include_section(Object*, const char*, + const elfcpp::Shdr<size, big_endian>& shdr) +{ + // Some section types are never linked. Some are only linked when + // doing a relocateable link. + switch (shdr.get_sh_type()) + { + case elfcpp::SHT_NULL: + case elfcpp::SHT_SYMTAB: + case elfcpp::SHT_DYNSYM: + case elfcpp::SHT_STRTAB: + case elfcpp::SHT_HASH: + case elfcpp::SHT_DYNAMIC: + case elfcpp::SHT_SYMTAB_SHNDX: + return false; + + case elfcpp::SHT_RELA: + case elfcpp::SHT_REL: + case elfcpp::SHT_GROUP: + return this->options_.is_relocatable(); + + default: + // FIXME: Handle stripping debug sections here. + return true; + } +} + +// Return an output section named NAME, or NULL if there is none. + +Output_section* +Layout::find_output_section(const char* name) const +{ + for (Section_name_map::const_iterator p = this->section_name_map_.begin(); + p != this->section_name_map_.end(); + ++p) + if (strcmp(p->second->name(), name) == 0) + return p->second; + return NULL; +} + +// Return an output segment of type TYPE, with segment flags SET set +// and segment flags CLEAR clear. Return NULL if there is none. + +Output_segment* +Layout::find_output_segment(elfcpp::PT type, elfcpp::Elf_Word set, + elfcpp::Elf_Word clear) const +{ + for (Segment_list::const_iterator p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + if (static_cast<elfcpp::PT>((*p)->type()) == type + && ((*p)->flags() & set) == set + && ((*p)->flags() & clear) == 0) + return *p; + return NULL; +} + +// Return the output section to use for section NAME with type TYPE +// and section flags FLAGS. + +Output_section* +Layout::get_output_section(const char* name, Stringpool::Key name_key, + elfcpp::Elf_Word type, elfcpp::Elf_Xword flags) +{ + // We should ignore some flags. + flags &= ~ (elfcpp::SHF_INFO_LINK + | elfcpp::SHF_LINK_ORDER + | elfcpp::SHF_GROUP + | elfcpp::SHF_MERGE + | elfcpp::SHF_STRINGS); + + const Key key(name_key, std::make_pair(type, flags)); + const std::pair<Key, Output_section*> v(key, NULL); + std::pair<Section_name_map::iterator, bool> ins( + this->section_name_map_.insert(v)); + + if (!ins.second) + return ins.first->second; + else + { + // This is the first time we've seen this name/type/flags + // combination. + Output_section* os = this->make_output_section(name, type, flags); + ins.first->second = os; + return os; + } +} + +// Return the output section to use for input section SHNDX, with name +// NAME, with header HEADER, from object OBJECT. Set *OFF to the +// offset of this input section without the output section. + +template<int size, bool big_endian> +Output_section* +Layout::layout(Relobj* object, unsigned int shndx, const char* name, + const elfcpp::Shdr<size, big_endian>& shdr, off_t* off) +{ + if (!this->include_section(object, name, shdr)) + return NULL; + + // If we are not doing a relocateable link, choose the name to use + // for the output section. + size_t len = strlen(name); + if (!this->options_.is_relocatable()) + name = Layout::output_section_name(name, &len); + + // FIXME: Handle SHF_OS_NONCONFORMING here. + + // Canonicalize the section name. + Stringpool::Key name_key; + name = this->namepool_.add(name, len, &name_key); + + // Find the output section. The output section is selected based on + // the section name, type, and flags. + Output_section* os = this->get_output_section(name, name_key, + shdr.get_sh_type(), + shdr.get_sh_flags()); + + // FIXME: Handle SHF_LINK_ORDER somewhere. + + *off = os->add_input_section(object, shndx, name, shdr); + + return os; +} + +// Add POSD to an output section using NAME, TYPE, and FLAGS. + +void +Layout::add_output_section_data(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags, + Output_section_data* posd) +{ + // Canonicalize the name. + Stringpool::Key name_key; + name = this->namepool_.add(name, &name_key); + + Output_section* os = this->get_output_section(name, name_key, type, flags); + os->add_output_section_data(posd); +} + +// Map section flags to segment flags. + +elfcpp::Elf_Word +Layout::section_flags_to_segment(elfcpp::Elf_Xword flags) +{ + elfcpp::Elf_Word ret = elfcpp::PF_R; + if ((flags & elfcpp::SHF_WRITE) != 0) + ret |= elfcpp::PF_W; + if ((flags & elfcpp::SHF_EXECINSTR) != 0) + ret |= elfcpp::PF_X; + return ret; +} + +// Make a new Output_section, and attach it to segments as +// appropriate. + +Output_section* +Layout::make_output_section(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags) +{ + Output_section* os = new Output_section(name, type, flags); + this->section_list_.push_back(os); + + if ((flags & elfcpp::SHF_ALLOC) == 0) + this->unattached_section_list_.push_back(os); + else + { + // This output section goes into a PT_LOAD segment. + + elfcpp::Elf_Word seg_flags = Layout::section_flags_to_segment(flags); + + // The only thing we really care about for PT_LOAD segments is + // whether or not they are writable, so that is how we search + // for them. People who need segments sorted on some other + // basis will have to wait until we implement a mechanism for + // them to describe the segments they want. + + Segment_list::const_iterator p; + for (p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + { + if ((*p)->type() == elfcpp::PT_LOAD + && ((*p)->flags() & elfcpp::PF_W) == (seg_flags & elfcpp::PF_W)) + { + (*p)->add_output_section(os, seg_flags); + break; + } + } + + if (p == this->segment_list_.end()) + { + Output_segment* oseg = new Output_segment(elfcpp::PT_LOAD, + seg_flags); + this->segment_list_.push_back(oseg); + oseg->add_output_section(os, seg_flags); + } + + // If we see a loadable SHT_NOTE section, we create a PT_NOTE + // segment. + if (type == elfcpp::SHT_NOTE) + { + // See if we already have an equivalent PT_NOTE segment. + for (p = this->segment_list_.begin(); + p != segment_list_.end(); + ++p) + { + if ((*p)->type() == elfcpp::PT_NOTE + && (((*p)->flags() & elfcpp::PF_W) + == (seg_flags & elfcpp::PF_W))) + { + (*p)->add_output_section(os, seg_flags); + break; + } + } + + if (p == this->segment_list_.end()) + { + Output_segment* oseg = new Output_segment(elfcpp::PT_NOTE, + seg_flags); + this->segment_list_.push_back(oseg); + oseg->add_output_section(os, seg_flags); + } + } + + // If we see a loadable SHF_TLS section, we create a PT_TLS + // segment. There can only be one such segment. + if ((flags & elfcpp::SHF_TLS) != 0) + { + if (this->tls_segment_ == NULL) + { + this->tls_segment_ = new Output_segment(elfcpp::PT_TLS, + seg_flags); + this->segment_list_.push_back(this->tls_segment_); + } + this->tls_segment_->add_output_section(os, seg_flags); + } + } + + return os; +} + +// Create the dynamic sections which are needed before we read the +// relocs. + +void +Layout::create_initial_dynamic_sections(const Input_objects* input_objects, + Symbol_table* symtab) +{ + if (!input_objects->any_dynamic()) + return; + + const char* dynamic_name = this->namepool_.add(".dynamic", NULL); + this->dynamic_section_ = this->make_output_section(dynamic_name, + elfcpp::SHT_DYNAMIC, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE)); + + symtab->define_in_output_data(input_objects->target(), "_DYNAMIC", NULL, + this->dynamic_section_, 0, 0, + elfcpp::STT_OBJECT, elfcpp::STB_LOCAL, + elfcpp::STV_HIDDEN, 0, false, false); + + this->dynamic_data_ = new Output_data_dynamic(input_objects->target(), + &this->dynpool_); + + this->dynamic_section_->add_output_section_data(this->dynamic_data_); +} + +// Find the first read-only PT_LOAD segment, creating one if +// necessary. + +Output_segment* +Layout::find_first_load_seg() +{ + for (Segment_list::const_iterator p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + { + if ((*p)->type() == elfcpp::PT_LOAD + && ((*p)->flags() & elfcpp::PF_R) != 0 + && ((*p)->flags() & elfcpp::PF_W) == 0) + return *p; + } + + Output_segment* load_seg = new Output_segment(elfcpp::PT_LOAD, elfcpp::PF_R); + this->segment_list_.push_back(load_seg); + return load_seg; +} + +// Finalize the layout. When this is called, we have created all the +// output sections and all the output segments which are based on +// input sections. We have several things to do, and we have to do +// them in the right order, so that we get the right results correctly +// and efficiently. + +// 1) Finalize the list of output segments and create the segment +// table header. + +// 2) Finalize the dynamic symbol table and associated sections. + +// 3) Determine the final file offset of all the output segments. + +// 4) Determine the final file offset of all the SHF_ALLOC output +// sections. + +// 5) Create the symbol table sections and the section name table +// section. + +// 6) Finalize the symbol table: set symbol values to their final +// value and make a final determination of which symbols are going +// into the output symbol table. + +// 7) Create the section table header. + +// 8) Determine the final file offset of all the output sections which +// are not SHF_ALLOC, including the section table header. + +// 9) Finalize the ELF file header. + +// This function returns the size of the output file. + +off_t +Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) +{ + Target* const target = input_objects->target(); + const int size = target->get_size(); + + target->finalize_sections(&this->options_, this); + + Output_segment* phdr_seg = NULL; + if (input_objects->any_dynamic()) + { + // 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. + Output_section* dynstr; + std::vector<Symbol*> dynamic_symbols; + unsigned int local_dynamic_count; + Versions versions; + this->create_dynamic_symtab(target, symtab, &dynstr, + &local_dynamic_count, &dynamic_symbols, + &versions); + + // Create the .interp section to hold the name of the + // interpreter, and put it in a PT_INTERP segment. + this->create_interp(target); + + // Finish the .dynamic section to hold the dynamic data, and put + // it in a PT_DYNAMIC segment. + this->finish_dynamic_section(input_objects, symtab); + + // We should have added everything we need to the dynamic string + // table. + this->dynpool_.set_string_offsets(); + + // Create the version sections. We can't do this until the + // dynamic string table is complete. + this->create_version_sections(target, &versions, local_dynamic_count, + dynamic_symbols, dynstr); + } + + // FIXME: Handle PT_GNU_STACK. + + Output_segment* load_seg = this->find_first_load_seg(); + + // Lay out the segment headers. + bool big_endian = 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); + if (phdr_seg != NULL) + phdr_seg->add_initial_output_data(segment_headers); + + // Lay out the file header. + Output_file_header* file_header; + file_header = new Output_file_header(size, + big_endian, + this->options_, + target, + symtab, + segment_headers); + load_seg->add_initial_output_data(file_header); + this->special_output_list_.push_back(file_header); + + // We set the output section indexes in set_segment_offsets and + // set_section_offsets. + unsigned int shndx = 1; + + // Set the file offsets of all the segments, and all the sections + // they contain. + off_t off = this->set_segment_offsets(target, load_seg, &shndx); + + // Create the symbol table sections. + this->create_symtab_sections(size, input_objects, symtab, &off); + + // Create the .shstrtab section. + Output_section* shstrtab_section = this->create_shstrtab(); + + // Set the file offsets of all the sections not associated with + // segments. + off = this->set_section_offsets(off, &shndx); + + // Create the section table header. + Output_section_headers* oshdrs = this->create_shdrs(size, big_endian, &off); + + file_header->set_section_info(oshdrs, shstrtab_section); + + // Now we know exactly where everything goes in the output file. + Output_data::layout_complete(); + + return off; +} + +// Return whether SEG1 should be before SEG2 in the output file. This +// is based entirely on the segment type and flags. When this is +// called the segment addresses has normally not yet been set. + +bool +Layout::segment_precedes(const Output_segment* seg1, + const Output_segment* seg2) +{ + elfcpp::Elf_Word type1 = seg1->type(); + elfcpp::Elf_Word type2 = seg2->type(); + + // The single PT_PHDR segment is required to precede any loadable + // segment. We simply make it always first. + if (type1 == elfcpp::PT_PHDR) + { + gold_assert(type2 != elfcpp::PT_PHDR); + return true; + } + if (type2 == elfcpp::PT_PHDR) + return false; + + // The single PT_INTERP segment is required to precede any loadable + // segment. We simply make it always second. + if (type1 == elfcpp::PT_INTERP) + { + gold_assert(type2 != elfcpp::PT_INTERP); + return true; + } + if (type2 == elfcpp::PT_INTERP) + return false; + + // We then put PT_LOAD segments before any other segments. + if (type1 == elfcpp::PT_LOAD && type2 != elfcpp::PT_LOAD) + return true; + if (type2 == elfcpp::PT_LOAD && type1 != elfcpp::PT_LOAD) + return false; + + // We put the PT_TLS segment last, because that is where the dynamic + // linker expects to find it (this is just for efficiency; other + // positions would also work correctly). + if (type1 == elfcpp::PT_TLS && type2 != elfcpp::PT_TLS) + return false; + if (type2 == elfcpp::PT_TLS && type1 != elfcpp::PT_TLS) + return true; + + const elfcpp::Elf_Word flags1 = seg1->flags(); + const elfcpp::Elf_Word flags2 = seg2->flags(); + + // The order of non-PT_LOAD segments is unimportant. We simply sort + // by the numeric segment type and flags values. There should not + // be more than one segment with the same type and flags. + if (type1 != elfcpp::PT_LOAD) + { + if (type1 != type2) + return type1 < type2; + gold_assert(flags1 != flags2); + return flags1 < flags2; + } + + // We sort PT_LOAD segments based on the flags. Readonly segments + // come before writable segments. Then executable segments come + // before non-executable segments. Then the unlikely case of a + // non-readable segment comes before the normal case of a readable + // segment. If there are multiple segments with the same type and + // flags, we require that the address be set, and we sort by + // virtual address and then physical address. + if ((flags1 & elfcpp::PF_W) != (flags2 & elfcpp::PF_W)) + return (flags1 & elfcpp::PF_W) == 0; + if ((flags1 & elfcpp::PF_X) != (flags2 & elfcpp::PF_X)) + return (flags1 & elfcpp::PF_X) != 0; + if ((flags1 & elfcpp::PF_R) != (flags2 & elfcpp::PF_R)) + return (flags1 & elfcpp::PF_R) == 0; + + uint64_t vaddr1 = seg1->vaddr(); + uint64_t vaddr2 = seg2->vaddr(); + if (vaddr1 != vaddr2) + return vaddr1 < vaddr2; + + uint64_t paddr1 = seg1->paddr(); + uint64_t paddr2 = seg2->paddr(); + gold_assert(paddr1 != paddr2); + return paddr1 < paddr2; +} + +// Set the file offsets of all the segments, and all the sections they +// contain. They have all been created. LOAD_SEG must be be laid out +// first. Return the offset of the data to follow. + +off_t +Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, + unsigned int *pshndx) +{ + // Sort them into the final order. + std::sort(this->segment_list_.begin(), this->segment_list_.end(), + Layout::Compare_segments()); + + // Find the PT_LOAD segments, and set their addresses and offsets + // and their section's addresses and offsets. + uint64_t addr = target->text_segment_address(); + off_t off = 0; + bool was_readonly = false; + for (Segment_list::iterator p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + { + if ((*p)->type() == elfcpp::PT_LOAD) + { + if (load_seg != NULL && load_seg != *p) + gold_unreachable(); + load_seg = NULL; + + // If the last segment was readonly, and this one is not, + // then skip the address forward one page, maintaining the + // same position within the page. This lets us store both + // segments overlapping on a single page in the file, but + // the loader will put them on different pages in memory. + + uint64_t orig_addr = addr; + uint64_t orig_off = off; + + uint64_t aligned_addr = addr; + uint64_t abi_pagesize = target->abi_pagesize(); + if (was_readonly && ((*p)->flags() & elfcpp::PF_W) != 0) + { + uint64_t align = (*p)->addralign(); + + addr = align_address(addr, align); + aligned_addr = addr; + if ((addr & (abi_pagesize - 1)) != 0) + addr = addr + abi_pagesize; + } + + unsigned int shndx_hold = *pshndx; + off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1)); + uint64_t new_addr = (*p)->set_section_addresses(addr, &off, pshndx); + + // Now that we know the size of this segment, we may be able + // to save a page in memory, at the cost of wasting some + // file space, by instead aligning to the start of a new + // page. Here we use the real machine page size rather than + // the ABI mandated page size. + + if (aligned_addr != addr) + { + uint64_t common_pagesize = target->common_pagesize(); + uint64_t first_off = (common_pagesize + - (aligned_addr + & (common_pagesize - 1))); + uint64_t last_off = new_addr & (common_pagesize - 1); + if (first_off > 0 + && last_off > 0 + && ((aligned_addr & ~ (common_pagesize - 1)) + != (new_addr & ~ (common_pagesize - 1))) + && first_off + last_off <= common_pagesize) + { + *pshndx = shndx_hold; + addr = align_address(aligned_addr, common_pagesize); + off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1)); + new_addr = (*p)->set_section_addresses(addr, &off, pshndx); + } + } + + addr = new_addr; + + if (((*p)->flags() & elfcpp::PF_W) == 0) + was_readonly = true; + } + } + + // Handle the non-PT_LOAD segments, setting their offsets from their + // section's offsets. + for (Segment_list::iterator p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + { + if ((*p)->type() != elfcpp::PT_LOAD) + (*p)->set_offset(); + } + + return off; +} + +// Set the file offset of all the sections not associated with a +// segment. + +off_t +Layout::set_section_offsets(off_t off, unsigned int* pshndx) +{ + for (Section_list::iterator p = this->unattached_section_list_.begin(); + p != this->unattached_section_list_.end(); + ++p) + { + (*p)->set_out_shndx(*pshndx); + ++*pshndx; + if ((*p)->offset() != -1) + continue; + off = align_address(off, (*p)->addralign()); + (*p)->set_address(0, off); + off += (*p)->data_size(); + } + return off; +} + +// Create the symbol table sections. Here we also set the final +// values of the symbols. At this point all the loadable sections are +// fully laid out. + +void +Layout::create_symtab_sections(int size, const Input_objects* input_objects, + Symbol_table* symtab, + off_t* poff) +{ + int symsize; + unsigned int align; + if (size == 32) + { + symsize = elfcpp::Elf_sizes<32>::sym_size; + align = 4; + } + else if (size == 64) + { + symsize = elfcpp::Elf_sizes<64>::sym_size; + align = 8; + } + else + gold_unreachable(); + + off_t off = *poff; + off = align_address(off, align); + off_t startoff = off; + + // Save space for the dummy symbol at the start of the section. We + // never bother to write this out--it will just be left as zero. + off += symsize; + unsigned int local_symbol_index = 1; + + // Add STT_SECTION symbols for each Output section which needs one. + for (Section_list::iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + { + if (!(*p)->needs_symtab_index()) + (*p)->set_symtab_index(-1U); + else + { + (*p)->set_symtab_index(local_symbol_index); + ++local_symbol_index; + off += symsize; + } + } + + for (Input_objects::Relobj_iterator p = input_objects->relobj_begin(); + p != input_objects->relobj_end(); + ++p) + { + Task_lock_obj<Object> tlo(**p); + unsigned int index = (*p)->finalize_local_symbols(local_symbol_index, + off, + &this->sympool_); + off += (index - local_symbol_index) * symsize; + local_symbol_index = index; + } + + unsigned int local_symcount = local_symbol_index; + gold_assert(local_symcount * symsize == off - startoff); + + off_t dynoff; + size_t dyn_global_index; + size_t dyncount; + if (this->dynsym_section_ == NULL) + { + dynoff = 0; + dyn_global_index = 0; + dyncount = 0; + } + else + { + dyn_global_index = this->dynsym_section_->info(); + off_t locsize = dyn_global_index * this->dynsym_section_->entsize(); + dynoff = this->dynsym_section_->offset() + locsize; + dyncount = (this->dynsym_section_->data_size() - locsize) / symsize; + gold_assert(dyncount * symsize + == this->dynsym_section_->data_size() - locsize); + } + + off = symtab->finalize(local_symcount, off, dynoff, dyn_global_index, + dyncount, &this->sympool_); + + this->sympool_.set_string_offsets(); + + const char* symtab_name = this->namepool_.add(".symtab", NULL); + Output_section* osymtab = this->make_output_section(symtab_name, + elfcpp::SHT_SYMTAB, + 0); + this->symtab_section_ = osymtab; + + Output_section_data* pos = new Output_data_space(off - startoff, + align); + osymtab->add_output_section_data(pos); + + const char* strtab_name = this->namepool_.add(".strtab", NULL); + Output_section* ostrtab = this->make_output_section(strtab_name, + elfcpp::SHT_STRTAB, + 0); + + Output_section_data* pstr = new Output_data_strtab(&this->sympool_); + ostrtab->add_output_section_data(pstr); + + osymtab->set_address(0, startoff); + osymtab->set_link_section(ostrtab); + osymtab->set_info(local_symcount); + osymtab->set_entsize(symsize); + + *poff = off; +} + +// Create the .shstrtab section, which holds the names of the +// sections. At the time this is called, we have created all the +// output sections except .shstrtab itself. + +Output_section* +Layout::create_shstrtab() +{ + // FIXME: We don't need to create a .shstrtab section if we are + // stripping everything. + + const char* name = this->namepool_.add(".shstrtab", NULL); + + this->namepool_.set_string_offsets(); + + Output_section* os = this->make_output_section(name, elfcpp::SHT_STRTAB, 0); + + Output_section_data* posd = new Output_data_strtab(&this->namepool_); + os->add_output_section_data(posd); + + return os; +} + +// Create the section headers. SIZE is 32 or 64. OFF is the file +// offset. + +Output_section_headers* +Layout::create_shdrs(int size, bool big_endian, off_t* poff) +{ + Output_section_headers* oshdrs; + oshdrs = new Output_section_headers(size, big_endian, this, + &this->segment_list_, + &this->unattached_section_list_, + &this->namepool_); + off_t off = align_address(*poff, oshdrs->addralign()); + oshdrs->set_address(0, off); + off += oshdrs->data_size(); + *poff = off; + this->special_output_list_.push_back(oshdrs); + return oshdrs; +} + +// Create the dynamic symbol table. + +void +Layout::create_dynamic_symtab(const Target* target, Symbol_table* symtab, + Output_section **pdynstr, + unsigned int* plocal_dynamic_count, + std::vector<Symbol*>* pdynamic_symbols, + Versions* pversions) +{ + // Count all the symbols in the dynamic symbol table, and set the + // dynamic symbol indexes. + + // Skip symbol 0, which is always all zeroes. + unsigned int index = 1; + + // Add STT_SECTION symbols for each Output section which needs one. + for (Section_list::iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + { + if (!(*p)->needs_dynsym_index()) + (*p)->set_dynsym_index(-1U); + else + { + (*p)->set_dynsym_index(index); + ++index; + } + } + + // FIXME: Some targets apparently require local symbols in the + // dynamic symbol table. Here is where we will have to count them, + // and set the dynamic symbol indexes, and add the names to + // this->dynpool_. + + unsigned int local_symcount = index; + *plocal_dynamic_count = local_symcount; + + // FIXME: We have to tell set_dynsym_indexes whether the + // -E/--export-dynamic option was used. + index = symtab->set_dynsym_indexes(&this->options_, target, index, + pdynamic_symbols, &this->dynpool_, + pversions); + + int symsize; + unsigned int align; + const int size = target->get_size(); + if (size == 32) + { + symsize = elfcpp::Elf_sizes<32>::sym_size; + align = 4; + } + else if (size == 64) + { + symsize = elfcpp::Elf_sizes<64>::sym_size; + align = 8; + } + else + gold_unreachable(); + + // Create the dynamic symbol table section. + + const char* dynsym_name = this->namepool_.add(".dynsym", NULL); + Output_section* dynsym = this->make_output_section(dynsym_name, + elfcpp::SHT_DYNSYM, + elfcpp::SHF_ALLOC); + + Output_section_data* odata = new Output_data_space(index * symsize, + align); + dynsym->add_output_section_data(odata); + + dynsym->set_info(local_symcount); + dynsym->set_entsize(symsize); + dynsym->set_addralign(align); + + this->dynsym_section_ = dynsym; + + Output_data_dynamic* const odyn = this->dynamic_data_; + odyn->add_section_address(elfcpp::DT_SYMTAB, dynsym); + odyn->add_constant(elfcpp::DT_SYMENT, symsize); + + // Create the dynamic string table section. + + const char* dynstr_name = this->namepool_.add(".dynstr", NULL); + Output_section* dynstr = this->make_output_section(dynstr_name, + elfcpp::SHT_STRTAB, + elfcpp::SHF_ALLOC); + + Output_section_data* strdata = new Output_data_strtab(&this->dynpool_); + dynstr->add_output_section_data(strdata); + + dynsym->set_link_section(dynstr); + this->dynamic_section_->set_link_section(dynstr); + + odyn->add_section_address(elfcpp::DT_STRTAB, dynstr); + odyn->add_section_size(elfcpp::DT_STRSZ, dynstr); + + *pdynstr = dynstr; + + // Create the hash tables. + + // FIXME: We need an option to create a GNU hash table. + + unsigned char* phash; + unsigned int hashlen; + Dynobj::create_elf_hash_table(target, *pdynamic_symbols, local_symcount, + &phash, &hashlen); + + const char* hash_name = this->namepool_.add(".hash", NULL); + Output_section* hashsec = this->make_output_section(hash_name, + elfcpp::SHT_HASH, + elfcpp::SHF_ALLOC); + + Output_section_data* hashdata = new Output_data_const_buffer(phash, + hashlen, + align); + hashsec->add_output_section_data(hashdata); + + hashsec->set_link_section(dynsym); + hashsec->set_entsize(4); + + odyn->add_section_address(elfcpp::DT_HASH, hashsec); +} + +// Create the version sections. + +void +Layout::create_version_sections(const Target* target, const Versions* versions, + unsigned int local_symcount, + const std::vector<Symbol*>& dynamic_symbols, + const Output_section* dynstr) +{ + if (!versions->any_defs() && !versions->any_needs()) + return; + + if (target->get_size() == 32) + { + if (target->is_big_endian()) + this->sized_create_version_sections SELECT_SIZE_ENDIAN_NAME(32, true)( + versions, local_symcount, dynamic_symbols, dynstr + SELECT_SIZE_ENDIAN(32, true)); + else + this->sized_create_version_sections SELECT_SIZE_ENDIAN_NAME(32, false)( + versions, local_symcount, dynamic_symbols, dynstr + SELECT_SIZE_ENDIAN(32, false)); + } + else if (target->get_size() == 64) + { + if (target->is_big_endian()) + this->sized_create_version_sections SELECT_SIZE_ENDIAN_NAME(64, true)( + versions, local_symcount, dynamic_symbols, dynstr + SELECT_SIZE_ENDIAN(64, true)); + else + this->sized_create_version_sections SELECT_SIZE_ENDIAN_NAME(64, false)( + versions, local_symcount, dynamic_symbols, dynstr + SELECT_SIZE_ENDIAN(64, false)); + } + else + gold_unreachable(); +} + +// Create the version sections, sized version. + +template<int size, bool big_endian> +void +Layout::sized_create_version_sections( + const Versions* versions, + unsigned int local_symcount, + const std::vector<Symbol*>& dynamic_symbols, + const Output_section* dynstr + ACCEPT_SIZE_ENDIAN) +{ + const char* vname = this->namepool_.add(".gnu.version", NULL); + Output_section* vsec = this->make_output_section(vname, + elfcpp::SHT_GNU_versym, + elfcpp::SHF_ALLOC); + + unsigned char* vbuf; + unsigned int vsize; + versions->symbol_section_contents SELECT_SIZE_ENDIAN_NAME(size, big_endian)( + &this->dynpool_, local_symcount, dynamic_symbols, &vbuf, &vsize + SELECT_SIZE_ENDIAN(size, big_endian)); + + Output_section_data* vdata = new Output_data_const_buffer(vbuf, vsize, 2); + + vsec->add_output_section_data(vdata); + vsec->set_entsize(2); + vsec->set_link_section(this->dynsym_section_); + + Output_data_dynamic* const odyn = this->dynamic_data_; + odyn->add_section_address(elfcpp::DT_VERSYM, vsec); + + if (versions->any_defs()) + { + const char* vdname = this->namepool_.add(".gnu.version_d", NULL); + Output_section *vdsec; + vdsec = this->make_output_section(vdname, elfcpp::SHT_GNU_verdef, + elfcpp::SHF_ALLOC); + + unsigned char* vdbuf; + unsigned int vdsize; + unsigned int vdentries; + versions->def_section_contents SELECT_SIZE_ENDIAN_NAME(size, big_endian)( + &this->dynpool_, &vdbuf, &vdsize, &vdentries + SELECT_SIZE_ENDIAN(size, big_endian)); + + Output_section_data* vddata = new Output_data_const_buffer(vdbuf, + vdsize, + 4); + + vdsec->add_output_section_data(vddata); + vdsec->set_link_section(dynstr); + vdsec->set_info(vdentries); + + odyn->add_section_address(elfcpp::DT_VERDEF, vdsec); + odyn->add_constant(elfcpp::DT_VERDEFNUM, vdentries); + } + + if (versions->any_needs()) + { + const char* vnname = this->namepool_.add(".gnu.version_r", NULL); + Output_section* vnsec; + vnsec = this->make_output_section(vnname, elfcpp::SHT_GNU_verneed, + elfcpp::SHF_ALLOC); + + unsigned char* vnbuf; + unsigned int vnsize; + unsigned int vnentries; + versions->need_section_contents SELECT_SIZE_ENDIAN_NAME(size, big_endian) + (&this->dynpool_, &vnbuf, &vnsize, &vnentries + SELECT_SIZE_ENDIAN(size, big_endian)); + + Output_section_data* vndata = new Output_data_const_buffer(vnbuf, + vnsize, + 4); + + vnsec->add_output_section_data(vndata); + vnsec->set_link_section(dynstr); + vnsec->set_info(vnentries); + + odyn->add_section_address(elfcpp::DT_VERNEED, vnsec); + odyn->add_constant(elfcpp::DT_VERNEEDNUM, vnentries); + } +} + +// 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(); + gold_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); +} + +// Finish the .dynamic section and PT_DYNAMIC segment. + +void +Layout::finish_dynamic_section(const Input_objects* input_objects, + const Symbol_table* symtab) +{ + Output_segment* oseg = new Output_segment(elfcpp::PT_DYNAMIC, + elfcpp::PF_R | elfcpp::PF_W); + this->segment_list_.push_back(oseg); + oseg->add_initial_output_section(this->dynamic_section_, + elfcpp::PF_R | elfcpp::PF_W); + + Output_data_dynamic* const odyn = this->dynamic_data_; + + for (Input_objects::Dynobj_iterator p = input_objects->dynobj_begin(); + p != input_objects->dynobj_end(); + ++p) + { + // FIXME: Handle --as-needed. + odyn->add_string(elfcpp::DT_NEEDED, (*p)->soname()); + } + + // FIXME: Support --init and --fini. + Symbol* sym = symtab->lookup("_init"); + if (sym != NULL && sym->is_defined() && !sym->is_from_dynobj()) + odyn->add_symbol(elfcpp::DT_INIT, sym); + + sym = symtab->lookup("_fini"); + if (sym != NULL && sym->is_defined() && !sym->is_from_dynobj()) + odyn->add_symbol(elfcpp::DT_FINI, sym); + + // FIXME: Support DT_INIT_ARRAY and DT_FINI_ARRAY. +} + +// The mapping of .gnu.linkonce section names to real section names. + +#define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t, sizeof(t) - 1 } +const Layout::Linkonce_mapping Layout::linkonce_mapping[] = +{ + MAPPING_INIT("d.rel.ro", ".data.rel.ro"), // Must be before "d". + MAPPING_INIT("t", ".text"), + MAPPING_INIT("r", ".rodata"), + MAPPING_INIT("d", ".data"), + MAPPING_INIT("b", ".bss"), + MAPPING_INIT("s", ".sdata"), + MAPPING_INIT("sb", ".sbss"), + MAPPING_INIT("s2", ".sdata2"), + MAPPING_INIT("sb2", ".sbss2"), + MAPPING_INIT("wi", ".debug_info"), + MAPPING_INIT("td", ".tdata"), + MAPPING_INIT("tb", ".tbss"), + MAPPING_INIT("lr", ".lrodata"), + MAPPING_INIT("l", ".ldata"), + MAPPING_INIT("lb", ".lbss"), +}; +#undef MAPPING_INIT + +const int Layout::linkonce_mapping_count = + sizeof(Layout::linkonce_mapping) / sizeof(Layout::linkonce_mapping[0]); + +// Return the name of the output section to use for a .gnu.linkonce +// section. This is based on the default ELF linker script of the old +// GNU linker. For example, we map a name like ".gnu.linkonce.t.foo" +// to ".text". Set *PLEN to the length of the name. *PLEN is +// initialized to the length of NAME. + +const char* +Layout::linkonce_output_name(const char* name, size_t *plen) +{ + const char* s = name + sizeof(".gnu.linkonce") - 1; + if (*s != '.') + return name; + ++s; + const Linkonce_mapping* plm = linkonce_mapping; + for (int i = 0; i < linkonce_mapping_count; ++i, ++plm) + { + if (strncmp(s, plm->from, plm->fromlen) == 0 && s[plm->fromlen] == '.') + { + *plen = plm->tolen; + return plm->to; + } + } + return name; +} + +// Choose the output section name to use given an input section name. +// Set *PLEN to the length of the name. *PLEN is initialized to the +// length of NAME. + +const char* +Layout::output_section_name(const char* name, size_t* plen) +{ + if (Layout::is_linkonce(name)) + { + // .gnu.linkonce sections are laid out as though they were named + // for the sections are placed into. + return Layout::linkonce_output_name(name, plen); + } + + // If the section name has no '.', or only an initial '.', we use + // the name unchanged (i.e., ".text" is unchanged). + + // Otherwise, if the section name does not include ".rel", we drop + // the last '.' and everything that follows (i.e., ".text.XXX" + // becomes ".text"). + + // Otherwise, if the section name has zero or one '.' after the + // ".rel", we use the name unchanged (i.e., ".rel.text" is + // unchanged). + + // Otherwise, we drop the last '.' and everything that follows + // (i.e., ".rel.text.XXX" becomes ".rel.text"). + + const char* s = name; + if (*s == '.') + ++s; + const char* sdot = strchr(s, '.'); + if (sdot == NULL) + return name; + + const char* srel = strstr(s, ".rel"); + if (srel == NULL) + { + *plen = sdot - name; + return name; + } + + sdot = strchr(srel + 1, '.'); + if (sdot == NULL) + return name; + sdot = strchr(sdot + 1, '.'); + if (sdot == NULL) + return name; + + *plen = sdot - name; + return name; +} + +// Record the signature of a comdat section, and return whether to +// include it in the link. If GROUP is true, this is a regular +// section group. If GROUP is false, this is a group signature +// derived from the name of a linkonce section. We want linkonce +// signatures and group signatures to block each other, but we don't +// want a linkonce signature to block another linkonce signature. + +bool +Layout::add_comdat(const char* signature, bool group) +{ + std::string sig(signature); + std::pair<Signatures::iterator, bool> ins( + this->signatures_.insert(std::make_pair(sig, group))); + + if (ins.second) + { + // This is the first time we've seen this signature. + return true; + } + + if (ins.first->second) + { + // We've already seen a real section group with this signature. + return false; + } + else if (group) + { + // This is a real section group, and we've already seen a + // linkonce section with tihs signature. Record that we've seen + // a section group, and don't include this section group. + ins.first->second = true; + return false; + } + else + { + // We've already seen a linkonce section and this is a linkonce + // section. These don't block each other--this may be the same + // symbol name with different section types. + return true; + } +} + +// Write out data not associated with a section or the symbol table. + +void +Layout::write_data(const Symbol_table* symtab, const Target* target, + Output_file* of) const +{ + const Output_section* symtab_section = this->symtab_section_; + for (Section_list::const_iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + { + if ((*p)->needs_symtab_index()) + { + gold_assert(symtab_section != NULL); + unsigned int index = (*p)->symtab_index(); + gold_assert(index > 0 && index != -1U); + off_t off = (symtab_section->offset() + + index * symtab_section->entsize()); + symtab->write_section_symbol(target, *p, of, off); + } + } + + const Output_section* dynsym_section = this->dynsym_section_; + for (Section_list::const_iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + { + if ((*p)->needs_dynsym_index()) + { + gold_assert(dynsym_section != NULL); + unsigned int index = (*p)->dynsym_index(); + gold_assert(index > 0 && index != -1U); + off_t off = (dynsym_section->offset() + + index * dynsym_section->entsize()); + symtab->write_section_symbol(target, *p, of, off); + } + } + + // Write out the Output_sections. Most won't have anything to + // write, since most of the data will come from input sections which + // are handled elsewhere. But some Output_sections do have + // Output_data. + for (Section_list::const_iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + (*p)->write(of); + + // Write out the Output_data which are not in an Output_section. + for (Data_list::const_iterator p = this->special_output_list_.begin(); + p != this->special_output_list_.end(); + ++p) + (*p)->write(of); +} + +// Write_data_task methods. + +// We can always run this task. + +Task::Is_runnable_type +Write_data_task::is_runnable(Workqueue*) +{ + return IS_RUNNABLE; +} + +// We need to unlock FINAL_BLOCKER when finished. + +Task_locker* +Write_data_task::locks(Workqueue* workqueue) +{ + return new Task_locker_block(*this->final_blocker_, workqueue); +} + +// Run the task--write out the data. + +void +Write_data_task::run(Workqueue*) +{ + this->layout_->write_data(this->symtab_, this->target_, this->of_); +} + +// Write_symbols_task methods. + +// We can always run this task. + +Task::Is_runnable_type +Write_symbols_task::is_runnable(Workqueue*) +{ + return IS_RUNNABLE; +} + +// We need to unlock FINAL_BLOCKER when finished. + +Task_locker* +Write_symbols_task::locks(Workqueue* workqueue) +{ + return new Task_locker_block(*this->final_blocker_, workqueue); +} + +// Run the task--write out the symbols. + +void +Write_symbols_task::run(Workqueue*) +{ + this->symtab_->write_globals(this->target_, this->sympool_, this->dynpool_, + this->of_); +} + +// Close_task_runner methods. + +// Run the task--close the file. + +void +Close_task_runner::run(Workqueue*) +{ + this->of_->close(); +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones for implemented targets. + +template +Output_section* +Layout::layout<32, false>(Relobj* object, unsigned int shndx, const char* name, + const elfcpp::Shdr<32, false>& shdr, off_t*); + +template +Output_section* +Layout::layout<32, true>(Relobj* object, unsigned int shndx, const char* name, + const elfcpp::Shdr<32, true>& shdr, off_t*); + +template +Output_section* +Layout::layout<64, false>(Relobj* object, unsigned int shndx, const char* name, + const elfcpp::Shdr<64, false>& shdr, off_t*); + +template +Output_section* +Layout::layout<64, true>(Relobj* object, unsigned int shndx, const char* name, + const elfcpp::Shdr<64, true>& shdr, off_t*); + + +} // End namespace gold. diff --git a/gold/layout.h b/gold/layout.h new file mode 100644 index 000000000000..8b349cc0a4bd --- /dev/null +++ b/gold/layout.h @@ -0,0 +1,430 @@ +// layout.h -- lay out output file sections for gold -*- C++ -*- + +#ifndef GOLD_LAYOUT_H +#define GOLD_LAYOUT_H + +#include <list> +#include <string> +#include <utility> +#include <vector> + +#include "workqueue.h" +#include "object.h" +#include "dynobj.h" +#include "stringpool.h" + +namespace gold +{ + +class General_options; +class Input_objects; +class Symbol_table; +class Output_section_data; +class Output_section; +class Output_section_headers; +class Output_segment; +class Output_data; +class Output_data_dynamic; +class Target; + +// This task function handles mapping the input sections to output +// sections and laying them out in memory. + +class Layout_task_runner : public Task_function_runner +{ + public: + // OPTIONS is the command line options, INPUT_OBJECTS is the list of + // input objects, SYMTAB is the symbol table, LAYOUT is the layout + // object. + Layout_task_runner(const General_options& options, + const Input_objects* input_objects, + Symbol_table* symtab, + Layout* layout) + : options_(options), input_objects_(input_objects), symtab_(symtab), + layout_(layout) + { } + + // Run the operation. + void + run(Workqueue*); + + private: + Layout_task_runner(const Layout_task_runner&); + Layout_task_runner& operator=(const Layout_task_runner&); + + const General_options& options_; + const Input_objects* input_objects_; + Symbol_table* symtab_; + Layout* layout_; +}; + +// This class handles the details of laying out input sections. + +class Layout +{ + public: + Layout(const General_options& options); + + // Given an input section SHNDX, named NAME, with data in SHDR, from + // the object file OBJECT, return the output section where this + // input section should go. Set *OFFSET to the offset within the + // output section. + template<int size, bool big_endian> + Output_section* + layout(Relobj *object, unsigned int shndx, const char* name, + const elfcpp::Shdr<size, big_endian>& shdr, off_t* offset); + + // Add an Output_section_data to the layout. This is used for + // special sections like the GOT section. + void + add_output_section_data(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags, + Output_section_data*); + + // Create dynamic sections if necessary. + void + create_initial_dynamic_sections(const Input_objects*, Symbol_table*); + + // Return the Stringpool used for symbol names. + const Stringpool* + sympool() const + { return &this->sympool_; } + + // Return the Stringpool used for dynamic symbol names and dynamic + // tags. + const Stringpool* + dynpool() const + { return &this->dynpool_; } + + // Return whether a section is a .gnu.linkonce section, given the + // section name. + static inline bool + is_linkonce(const char* name) + { return strncmp(name, ".gnu.linkonce", sizeof(".gnu.linkonce") - 1) == 0; } + + // Record the signature of a comdat section, and return whether to + // include it in the link. The GROUP parameter is true for a + // section group signature, false for a signature derived from a + // .gnu.linkonce section. + bool + add_comdat(const char*, bool group); + + // Finalize the layout after all the input sections have been added. + off_t + finalize(const Input_objects*, Symbol_table*); + + // Return the TLS segment. This will return NULL if there isn't + // one. + Output_segment* + tls_segment() const + { return this->tls_segment_; } + + // Return the normal symbol table. + Output_section* + symtab_section() const + { + gold_assert(this->symtab_section_ != NULL); + return this->symtab_section_; + } + + // Return the dynamic symbol table. + Output_section* + dynsym_section() const + { + gold_assert(this->dynsym_section_ != NULL); + return this->dynsym_section_; + } + + // Return the dynamic tags. + Output_data_dynamic* + dynamic_data() const + { return this->dynamic_data_; } + + // Write out data not associated with an input file or the symbol + // table. + void + write_data(const Symbol_table*, const Target*, Output_file*) const; + + // Return an output section named NAME, or NULL if there is none. + Output_section* + find_output_section(const char* name) const; + + // Return an output segment of type TYPE, with segment flags SET set + // and segment flags CLEAR clear. Return NULL if there is none. + Output_segment* + find_output_segment(elfcpp::PT type, elfcpp::Elf_Word set, + elfcpp::Elf_Word clear) const; + + // The list of segments. + + typedef std::vector<Output_segment*> Segment_list; + + // The list of sections not attached to a segment. + + typedef std::vector<Output_section*> Section_list; + + // The list of information to write out which is not attached to + // either a section or a segment. + typedef std::vector<Output_data*> Data_list; + + private: + Layout(const Layout&); + Layout& operator=(const Layout&); + + // Mapping from .gnu.linkonce section names to output section names. + struct Linkonce_mapping + { + const char* from; + int fromlen; + const char* to; + int tolen; + }; + static const Linkonce_mapping linkonce_mapping[]; + static const int linkonce_mapping_count; + + // Find the first read-only PT_LOAD segment, creating one if + // necessary. + Output_segment* + find_first_load_seg(); + + // Create the output sections for the symbol table. + void + create_symtab_sections(int size, const Input_objects*, Symbol_table*, + off_t*); + + // Create the .shstrtab section. + Output_section* + create_shstrtab(); + + // Create the section header table. + Output_section_headers* + create_shdrs(int size, bool big_endian, off_t*); + + // Create the dynamic symbol table. + void + create_dynamic_symtab(const Target*, Symbol_table*, Output_section** pdynstr, + unsigned int* plocal_dynamic_count, + std::vector<Symbol*>* pdynamic_symbols, + Versions* versions); + + // Finish the .dynamic section and PT_DYNAMIC segment. + void + finish_dynamic_section(const Input_objects*, const Symbol_table*); + + // Create the .interp section and PT_INTERP segment. + void + create_interp(const Target* target); + + // Create the version sections. + void + create_version_sections(const Target*, const Versions*, + unsigned int local_symcount, + const std::vector<Symbol*>& dynamic_symbols, + const Output_section* dynstr); + + template<int size, bool big_endian> + void + sized_create_version_sections(const Versions* versions, + unsigned int local_symcount, + const std::vector<Symbol*>& dynamic_symbols, + const Output_section* dynstr + ACCEPT_SIZE_ENDIAN); + + // Return whether to include this section in the link. + template<int size, bool big_endian> + bool + include_section(Object* object, const char* name, + const elfcpp::Shdr<size, big_endian>&); + + // Return the output section name to use given an input section + // name. Set *PLEN to the length of the name. *PLEN must be + // initialized to the length of NAME. + static const char* + output_section_name(const char* name, size_t* plen); + + // Return the output section name to use for a linkonce section + // name. PLEN is as for output_section_name. + static const char* + linkonce_output_name(const char* name, size_t* plen); + + // Return the output section for NAME, TYPE and FLAGS. + Output_section* + get_output_section(const char* name, Stringpool::Key name_key, + elfcpp::Elf_Word type, elfcpp::Elf_Xword flags); + + // Create a new Output_section. + Output_section* + 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 indexes 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); + + // Map from section flags to segment flags. + static elfcpp::Elf_Word + section_flags_to_segment(elfcpp::Elf_Xword flags); + + // A mapping used for group signatures. + typedef Unordered_map<std::string, bool> Signatures; + + // Mapping from input section name/type/flags to output section. We + // use canonicalized strings here. + + typedef std::pair<Stringpool::Key, + std::pair<elfcpp::Elf_Word, elfcpp::Elf_Xword> > Key; + + struct Hash_key + { + size_t + operator()(const Key& k) const; + }; + + typedef Unordered_map<Key, Output_section*, Hash_key> Section_name_map; + + // A comparison class for segments. + + struct Compare_segments + { + bool + operator()(const Output_segment* seg1, const Output_segment* seg2) + { return Layout::segment_precedes(seg1, seg2); } + }; + + // A reference to the options on the command line. + const General_options& options_; + // The output section names. + Stringpool namepool_; + // The output symbol names. + Stringpool sympool_; + // The dynamic strings, if needed. + Stringpool dynpool_; + // The list of group sections and linkonce sections which we have seen. + Signatures signatures_; + // The mapping from input section name/type/flags to output sections. + Section_name_map section_name_map_; + // The list of output segments. + Segment_list segment_list_; + // The list of output sections. + Section_list section_list_; + // The list of output sections which are not attached to any output + // segment. + Section_list unattached_section_list_; + // The list of unattached Output_data objects which require special + // handling because they are not Output_sections. + Data_list special_output_list_; + // A pointer to the PT_TLS segment if there is one. + Output_segment* tls_segment_; + // The SHT_SYMTAB output section. + Output_section* symtab_section_; + // The SHT_DYNSYM output section if there is one. + Output_section* dynsym_section_; + // The SHT_DYNAMIC output section if there is one. + Output_section* dynamic_section_; + // The dynamic data which goes into dynamic_section_. + Output_data_dynamic* dynamic_data_; +}; + +// This task handles writing out data which is not part of a section +// or segment. + +class Write_data_task : public Task +{ + public: + Write_data_task(const Layout* layout, const Symbol_table* symtab, + const Target* target, Output_file* of, + Task_token* final_blocker) + : layout_(layout), symtab_(symtab), target_(target), of_(of), + final_blocker_(final_blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + const Layout* layout_; + const Symbol_table* symtab_; + const Target* target_; + Output_file* of_; + Task_token* final_blocker_; +}; + +// This task handles writing out the global symbols. + +class Write_symbols_task : public Task +{ + public: + Write_symbols_task(const Symbol_table* symtab, const Target* target, + const Stringpool* sympool, const Stringpool* dynpool, + Output_file* of, Task_token* final_blocker) + : symtab_(symtab), target_(target), sympool_(sympool), dynpool_(dynpool), + of_(of), final_blocker_(final_blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + const Symbol_table* symtab_; + const Target* target_; + const Stringpool* sympool_; + const Stringpool* dynpool_; + Output_file* of_; + Task_token* final_blocker_; +}; + +// This task function handles closing the file. + +class Close_task_runner : public Task_function_runner +{ + public: + Close_task_runner(Output_file* of) + : of_(of) + { } + + // Run the operation. + void + run(Workqueue*); + + private: + Output_file* of_; +}; + +// A small helper function to align an address. + +inline uint64_t +align_address(uint64_t address, uint64_t addralign) +{ + if (addralign != 0) + address = (address + addralign - 1) &~ (addralign - 1); + return address; +} + +} // End namespace gold. + +#endif // !defined(GOLD_LAYOUT_H) diff --git a/gold/main.cc b/gold/main.cc new file mode 100644 index 000000000000..ea65c2da27e1 --- /dev/null +++ b/gold/main.cc @@ -0,0 +1,57 @@ +// main.cc -- gold main function. + +#include "gold.h" + +#include "options.h" +#include "dirsearch.h" +#include "workqueue.h" +#include "object.h" +#include "symtab.h" +#include "layout.h" + +using namespace gold; + +int +main(int argc, char** argv) +{ +#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) + setlocale (LC_MESSAGES, ""); +#endif +#if defined (HAVE_SETLOCALE) + setlocale (LC_CTYPE, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + program_name = argv[0]; + + // Handle the command line options. + Command_line command_line; + command_line.process(argc - 1, argv + 1); + + // The work queue. + Workqueue workqueue(command_line.options()); + + // The list of input objects. + Input_objects input_objects; + + // The symbol table. + Symbol_table symtab; + + // The layout object. + Layout layout(command_line.options()); + + // Get the search path from the -L options. + Dirsearch search_path; + search_path.add(&workqueue, command_line.options().search_path()); + + // Queue up the first set of tasks. + queue_initial_tasks(command_line.options(), search_path, + command_line, &workqueue, &input_objects, + &symtab, &layout); + + // Run the main task processing loop. + workqueue.process(); + + gold_exit(true); +} diff --git a/gold/merge.cc b/gold/merge.cc new file mode 100644 index 000000000000..7af4faa03c92 --- /dev/null +++ b/gold/merge.cc @@ -0,0 +1,333 @@ +// merge.cc -- handle section merging for gold + +#include "gold.h" + +#include <cstdlib> + +#include "merge.h" + +namespace gold +{ + +// Sort the entries in a merge mapping. The key is an input object, a +// section index in that object, and an offset in that section. + +bool +Output_merge_base::Merge_key_less::operator()(const Merge_key& mk1, + const Merge_key& mk2) const +{ + // The order of different objects and different sections doesn't + // matter. We want to get consistent results across links so we + // don't use pointer comparison. + if (mk1.object != mk2.object) + return mk1.object->name() < mk2.object->name(); + if (mk1.shndx != mk2.shndx) + return mk1.shndx < mk2.shndx; + return mk1.offset < mk2.offset; +} + +// Add a mapping from an OFFSET in input section SHNDX in object +// OBJECT to an OUTPUT_OFFSET in a merged output section. This +// manages the mapping used to resolve relocations against merged +// sections. + +void +Output_merge_base::add_mapping(Relobj* object, unsigned int shndx, + off_t offset, off_t output_offset) +{ + Merge_key mk; + mk.object = object; + mk.shndx = shndx; + mk.offset = offset; + std::pair<Merge_map::iterator, bool> ins = + this->merge_map_.insert(std::make_pair(mk, output_offset)); + gold_assert(ins.second); +} + +// Return the output address for an input address. The input address +// is at offset OFFSET in section SHNDX in OBJECT. +// OUTPUT_SECTION_ADDRESS is the address of the output section. If we +// know the address, set *POUTPUT and return true. Otherwise return +// false. + +bool +Output_merge_base::do_output_address(const Relobj* object, unsigned int shndx, + off_t offset, + uint64_t output_section_address, + uint64_t* poutput) const +{ + gold_assert(output_section_address == this->address()); + + Merge_key mk; + mk.object = object; + mk.shndx = shndx; + mk.offset = offset; + Merge_map::const_iterator p = this->merge_map_.lower_bound(mk); + + // If MK is not in the map, lower_bound returns the next iterator + // larger than it. + if (p->first.object != object + || p->first.shndx != shndx + || p->first.offset != offset) + { + if (p == this->merge_map_.begin()) + return false; + --p; + } + + if (p->first.object != object || p->first.shndx != shndx) + return false; + + // Any input section is fully mapped: we don't need to know the size + // of the range starting at P->FIRST.OFFSET. + *poutput = output_section_address + p->second + (offset - p->first.offset); + return true; +} + +// Compute the hash code for a fixed-size constant. + +size_t +Output_merge_data::Merge_data_hash::operator()(Merge_data_key k) const +{ + const unsigned char* p = this->pomd_->constant(k); + uint64_t entsize = this->pomd_->entsize(); + + // Fowler/Noll/Vo (FNV) hash (type FNV-1a). + if (sizeof(size_t) == 8) + { + size_t result = static_cast<size_t>(14695981039346656037ULL); + for (uint64_t i = 0; i < entsize; ++i) + { + result &= (size_t) *p++; + result *= 1099511628211ULL; + } + return result; + } + else + { + size_t result = 2166136261UL; + for (uint64_t i = 0; i < entsize; ++i) + { + result ^= (size_t) *p++; + result *= 16777619UL; + } + return result; + } +} + +// Return whether one hash table key equals another. + +bool +Output_merge_data::Merge_data_eq::operator()(Merge_data_key k1, + Merge_data_key k2) const +{ + const unsigned char* p1 = this->pomd_->constant(k1); + const unsigned char* p2 = this->pomd_->constant(k2); + return memcmp(p1, p2, this->pomd_->entsize()) == 0; +} + +// Add a constant to the end of the section contents. + +void +Output_merge_data::add_constant(const unsigned char* p) +{ + uint64_t entsize = this->entsize(); + if (this->len_ + entsize > this->alc_) + { + if (this->alc_ == 0) + this->alc_ = 128 * entsize; + else + this->alc_ *= 2; + this->p_ = static_cast<unsigned char*>(realloc(this->p_, this->alc_)); + if (this->p_ == NULL) + gold_fatal("out of memory", true); + } + + memcpy(this->p_ + this->len_, p, entsize); + this->len_ += entsize; +} + +// Add the input section SHNDX in OBJECT to a merged output section +// which holds fixed length constants. Return whether we were able to +// handle the section; if not, it will be linked as usual without +// constant merging. + +bool +Output_merge_data::do_add_input_section(Relobj* object, unsigned int shndx) +{ + off_t len; + const unsigned char* p = object->section_contents(shndx, &len); + + uint64_t entsize = this->entsize(); + + if (len % entsize != 0) + return false; + + for (off_t i = 0; i < len; i += entsize, p += entsize) + { + // Add the constant to the section contents. If we find that it + // is already in the hash table, we will remove it again. + Merge_data_key k = this->len_; + this->add_constant(p); + + std::pair<Merge_data_hashtable::iterator, bool> ins = + this->hashtable_.insert(k); + + if (!ins.second) + { + // Key was already present. Remove the copy we just added. + this->len_ -= entsize; + k = *ins.first; + } + + // Record the offset of this constant in the output section. + this->add_mapping(object, shndx, i, k); + } + + return true; +} + +// Set the final data size in a merged output section with fixed size +// constants. + +void +Output_merge_data::do_set_address(uint64_t, off_t) +{ + // Release the memory we don't need. + this->p_ = static_cast<unsigned char*>(realloc(this->p_, this->len_)); + gold_assert(this->p_ != NULL); + this->set_data_size(this->len_); +} + +// Write the data of a merged output section with fixed size constants +// to the file. + +void +Output_merge_data::do_write(Output_file* of) +{ + of->write(this->offset(), this->p_, this->len_); +} + +// Compute a hash code for a Merge_string_key, which is an object, a +// section index, and an offset. + +template<typename Char_type> +size_t +Output_merge_string<Char_type>::Merge_string_key_hash::operator()( + const Merge_string_key& key) const +{ + // This is a very simple minded hash code. Fix it if it we get too + // many collisions. + const std::string& oname(key.object->name()); + return oname[0] + oname.length() + key.shndx + key.offset; +} + +// Compare two Merge_string_keys for equality. + +template<typename Char_type> +bool +Output_merge_string<Char_type>::Merge_string_key_eq::operator()( + const Merge_string_key& k1, const Merge_string_key& k2) const +{ + return (k1.object == k2.object + && k1.shndx == k2.shndx + && k1.offset == k2.offset); +} + +// Add an input section to a merged string section. + +template<typename Char_type> +bool +Output_merge_string<Char_type>::do_add_input_section(Relobj* object, + unsigned int shndx) +{ + off_t len; + const unsigned char* pdata = object->section_contents(shndx, &len); + + const Char_type* p = reinterpret_cast<const Char_type*>(pdata); + + if (len % sizeof(Char_type) != 0) + { + fprintf(stderr, + _("%s: %s: mergeable string section length not multiple of " + "character size\n"), + program_name, object->name().c_str()); + gold_exit(false); + } + len /= sizeof(Char_type); + + off_t i = 0; + while (i < len) + { + off_t plen = 0; + for (const Char_type* pl = p; *pl != 0; ++pl) + { + ++plen; + if (i + plen >= len) + { + fprintf(stderr, + _("%s: %s: entry in mergeable string section " + "not null terminated\n"), + program_name, object->name().c_str()); + gold_exit(false); + } + } + + const Char_type* str = this->stringpool_.add(p, NULL); + + Merge_string_key k(object, shndx, i); + typename Merge_string_hashtable::value_type v(k, str); + bool b = this->hashtable_.insert(v).second; + gold_assert(b); + + p += plen + 1; + i += plen + 1; + } + + return true; +} + +// Set the final data size of a merged string section. This is where +// we finalize the mappings from the input sections to the output +// section. + +template<typename Char_type> +void +Output_merge_string<Char_type>::do_set_address(uint64_t, off_t) +{ + this->stringpool_.set_string_offsets(); + + for (typename Merge_string_hashtable::const_iterator p = + this->hashtable_.begin(); + p != this->hashtable_.end(); + ++p) + this->add_mapping(p->first.object, p->first.shndx, p->first.offset, + this->stringpool_.get_offset(p->second)); + + this->set_data_size(this->stringpool_.get_strtab_size()); + + // Save some memory. + this->hashtable_.clear(); +} + +// Write out a merged string section. + +template<typename Char_type> +void +Output_merge_string<Char_type>::do_write(Output_file* of) +{ + this->stringpool_.write(of, this->offset()); +} + +// Instantiate the templates we need. + +template +class Output_merge_string<char>; + +template +class Output_merge_string<uint16_t>; + +template +class Output_merge_string<uint32_t>; + +} // End namespace gold. diff --git a/gold/merge.h b/gold/merge.h new file mode 100644 index 000000000000..dd97bf2d39e2 --- /dev/null +++ b/gold/merge.h @@ -0,0 +1,226 @@ +// merge.h -- handle section merging for gold -*- C++ -*- + +#ifndef GOLD_MERGE_H +#define GOLD_MERGE_H + +#include <climits> + +#include "stringpool.h" +#include "output.h" + +namespace gold +{ + +// A general class for SHF_MERGE data, to hold functions shared by +// fixed-size constant data and string data. + +class Output_merge_base : public Output_section_data +{ + public: + Output_merge_base(uint64_t entsize) + : Output_section_data(1), merge_map_(), entsize_(entsize) + { } + + // Return the output address for an input address. + bool + do_output_address(const Relobj* object, unsigned int shndx, off_t offset, + uint64_t output_section_address, uint64_t* poutput) const; + + protected: + // Return the entry size. + uint64_t + entsize() const + { return this->entsize_; } + + // Add a mapping from an OFFSET in input section SHNDX in object + // OBJECT to an OUTPUT_OFFSET in the output section. + void + add_mapping(Relobj* object, unsigned int shndx, off_t offset, + off_t output_offset); + + private: + // We build a mapping from OBJECT/SHNDX/OFFSET to an offset in the + // output section. + struct Merge_key + { + const Relobj* object; + unsigned int shndx; + off_t offset; + }; + + struct Merge_key_less + { + bool + operator()(const Merge_key&, const Merge_key&) const; + }; + + typedef std::map<Merge_key, off_t, Merge_key_less> Merge_map; + + // A mapping from input object/section/offset to offset in output + // section. + Merge_map merge_map_; + + // The entry size. For fixed-size constants, this is the size of + // the constants. For strings, this is the size of a character. + uint64_t entsize_; +}; + +// Handle SHF_MERGE sections with fixed-size constant data. + +class Output_merge_data : public Output_merge_base +{ + public: + Output_merge_data(uint64_t entsize) + : Output_merge_base(entsize), p_(NULL), len_(0), alc_(0), + hashtable_(128, Merge_data_hash(this), Merge_data_eq(this)) + { } + + // Add an input section. + bool + do_add_input_section(Relobj* object, unsigned int shndx); + + // Set the final data size. + void + do_set_address(uint64_t, off_t); + + // Write the data to the file. + void + do_write(Output_file*); + + private: + // We build a hash table of the fixed-size constants. Each constant + // is stored as a pointer into the section data we are accumulating. + + // A key in the hash table. This is an offset in the section + // contents we are building. + typedef off_t Merge_data_key; + + // Compute the hash code. To do this we need a pointer back to the + // object holding the data. + class Merge_data_hash + { + public: + Merge_data_hash(const Output_merge_data* pomd) + : pomd_(pomd) + { } + + size_t + operator()(Merge_data_key) const; + + private: + const Output_merge_data* pomd_; + }; + + friend class Merge_data_hash; + + // Compare two entries in the hash table for equality. To do this + // we need a pointer back to the object holding the data. Note that + // we now have a pointer to the object stored in two places in the + // hash table. Fixing this would require specializing the hash + // table, which would be hard to do portably. + class Merge_data_eq + { + public: + Merge_data_eq(const Output_merge_data* pomd) + : pomd_(pomd) + { } + + bool + operator()(Merge_data_key k1, Merge_data_key k2) const; + + private: + const Output_merge_data* pomd_; + }; + + friend class Merge_data_eq; + + // The type of the hash table. + typedef Unordered_set<Merge_data_key, Merge_data_hash, Merge_data_eq> + Merge_data_hashtable; + + // Given a hash table key, which is just an offset into the section + // data, return a pointer to the corresponding constant. + const unsigned char* + constant(Merge_data_key k) const + { + gold_assert(k >= 0 && k < this->len_); + return this->p_ + k; + } + + // Add a constant to the output. + void + add_constant(const unsigned char*); + + // The accumulated data. + unsigned char* p_; + // The length of the accumulated data. + off_t len_; + // The size of the allocated buffer. + size_t alc_; + // The hash table. + Merge_data_hashtable hashtable_; +}; + +// Handle SHF_MERGE sections with string data. This is a template +// based on the type of the characters in the string. + +template<typename Char_type> +class Output_merge_string : public Output_merge_base +{ + public: + Output_merge_string() + : Output_merge_base(sizeof(Char_type)), stringpool_(false), hashtable_() + { } + + // Add an input section. + bool + do_add_input_section(Relobj* object, unsigned int shndx); + + // Set the final data size. + void + do_set_address(uint64_t, off_t); + + // Write the data to the file. + void + do_write(Output_file*); + + private: + // As we see input sections, we build a mapping from object, section + // index and offset to strings. + struct Merge_string_key + { + Relobj* object; + unsigned int shndx; + off_t offset; + + Merge_string_key(Relobj *objecta, unsigned int shndxa, off_t offseta) + : object(objecta), shndx(shndxa), offset(offseta) + { } + }; + + struct Merge_string_key_hash + { + size_t + operator()(const Merge_string_key&) const; + }; + + struct Merge_string_key_eq + { + bool + operator()(const Merge_string_key&, const Merge_string_key&) const; + }; + + typedef Unordered_map<Merge_string_key, const Char_type*, + Merge_string_key_hash, Merge_string_key_eq> + Merge_string_hashtable; + + // As we see the strings, we add them to a Stringpool. + Stringpool_template<Char_type> stringpool_; + // Map from a location in an input object to an entry in the + // Stringpool. + Merge_string_hashtable hashtable_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_MERGE_H) diff --git a/gold/object.cc b/gold/object.cc new file mode 100644 index 000000000000..eb975c6c7345 --- /dev/null +++ b/gold/object.cc @@ -0,0 +1,973 @@ +// object.cc -- support for an object file for linking in gold + +#include "gold.h" + +#include <cerrno> +#include <cstring> +#include <cstdarg> + +#include "target-select.h" +#include "layout.h" +#include "output.h" +#include "symtab.h" +#include "object.h" +#include "dynobj.h" + +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 +Object::error(const char* format, ...) +{ + va_list args; + + fprintf(stderr, "%s: %s: ", program_name, this->name().c_str()); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + putc('\n', stderr); + + gold_exit(false); +} + +// Return a view of the contents of a section. + +const unsigned char* +Object::section_contents(unsigned int shndx, off_t* plen) +{ + Location loc(this->do_section_contents(shndx)); + *plen = loc.data_size; + 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> +Sized_relobj<size, big_endian>::Sized_relobj( + const std::string& name, + Input_file* input_file, + off_t offset, + const elfcpp::Ehdr<size, big_endian>& ehdr) + : Relobj(name, input_file, offset), + elf_file_(this, ehdr), + symtab_shndx_(-1U), + local_symbol_count_(0), + output_local_symbol_count_(0), + symbols_(NULL), + local_symbol_offset_(0), + local_values_() +{ +} + +template<int size, bool big_endian> +Sized_relobj<size, big_endian>::~Sized_relobj() +{ +} + +// Set up an object file based on the file header. This sets up the +// target and reads the section information. + +template<int size, bool big_endian> +void +Sized_relobj<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_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. + +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) + { + // 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) + { + --i; + p -= This::shdr_size; + typename This::Shdr shdr(p); + if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB) + { + this->symtab_shndx_ = i; + break; + } + } + } +} + +// Read the sections and symbols from an object file. + +template<int size, bool big_endian> +void +Sized_relobj<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(); + + this->find_symtab(pshdrs); + + if (this->symtab_shndx_ == 0) + { + // No symbol table. Weird but legal. + sd->symbols = NULL; + sd->symbols_size = 0; + sd->symbol_names = NULL; + sd->symbol_names_size = 0; + return; + } + + // Get the symbol table section header. + typename This::Shdr symtabshdr(pshdrs + + this->symtab_shndx_ * This::shdr_size); + gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); + + // We only need the external symbols. + const int sym_size = This::sym_size; + const unsigned int loccount = symtabshdr.get_sh_info(); + this->local_symbol_count_ = loccount; + off_t locsize = loccount * sym_size; + off_t extoff = symtabshdr.get_sh_offset() + locsize; + off_t extsize = symtabshdr.get_sh_size() - locsize; + + // Read the symbol table. + File_view* fvsymtab = this->get_lasting_view(extoff, extsize); + + // Read the section header for the symbol names. + 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_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: 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); + } + + // Read the symbol names. + File_view* fvstrtab = this->get_lasting_view(strtabshdr.get_sh_offset(), + strtabshdr.get_sh_size()); + + sd->symbols = fvsymtab; + sd->symbols_size = extsize; + sd->symbol_names = fvstrtab; + sd->symbol_names_size = strtabshdr.get_sh_size(); +} + +// Return whether to include a section group in the link. LAYOUT is +// used to keep track of which section groups we have already seen. +// INDEX is the index of the section group and SHDR is the section +// header. If we do not want to include this group, we set bits in +// OMIT for each section which should be discarded. + +template<int size, bool big_endian> +bool +Sized_relobj<size, big_endian>::include_section_group( + Layout* layout, + unsigned int index, + const elfcpp::Shdr<size, big_endian>& shdr, + std::vector<bool>* omit) +{ + // Read the section contents. + const unsigned char* pcon = this->get_view(shdr.get_sh_offset(), + shdr.get_sh_size()); + const elfcpp::Elf_Word* pword = + reinterpret_cast<const elfcpp::Elf_Word*>(pcon); + + // The first word contains flags. We only care about COMDAT section + // groups. Other section groups are always included in the link + // just like ordinary sections. + elfcpp::Elf_Word flags = elfcpp::Swap<32, big_endian>::readval(pword); + if ((flags & elfcpp::GRP_COMDAT) == 0) + return true; + + // Look up the group signature, which is the name of a symbol. This + // is a lot of effort to go to to read a string. Why didn't they + // just use the name of the SHT_GROUP section as the group + // signature? + + // Get the appropriate symbol table header (this will normally be + // the single SHT_SYMTAB section, but in principle it need not be). + const unsigned int link = shdr.get_sh_link(); + typename This::Shdr symshdr(this, this->elf_file_.section_header(link)); + + // Read the symbol table entry. + if (shdr.get_sh_info() >= symshdr.get_sh_size() / This::sym_size) + { + fprintf(stderr, _("%s: %s: section group %u info %u out of range\n"), + program_name, this->name().c_str(), index, shdr.get_sh_info()); + gold_exit(false); + } + off_t symoff = symshdr.get_sh_offset() + shdr.get_sh_info() * This::sym_size; + const unsigned char* psym = this->get_view(symoff, This::sym_size); + elfcpp::Sym<size, big_endian> sym(psym); + + // Read the symbol table names. + off_t symnamelen; + const unsigned char* psymnamesu; + psymnamesu = this->section_contents(symshdr.get_sh_link(), &symnamelen); + const char* psymnames = reinterpret_cast<const char*>(psymnamesu); + + // Get the section group signature. + if (sym.get_st_name() >= symnamelen) + { + fprintf(stderr, _("%s: %s: symbol %u name offset %u out of range\n"), + program_name, this->name().c_str(), shdr.get_sh_info(), + sym.get_st_name()); + gold_exit(false); + } + + const char* signature = psymnames + sym.get_st_name(); + + // It seems that some versions of gas will create a section group + // associated with a section symbol, and then fail to give a name to + // the section symbol. In such a case, use the name of the section. + // FIXME. + std::string secname; + if (signature[0] == '\0' && sym.get_st_type() == elfcpp::STT_SECTION) + { + secname = this->section_name(sym.get_st_shndx()); + signature = secname.c_str(); + } + + // Record this section group, and see whether we've already seen one + // with the same signature. + if (layout->add_comdat(signature, true)) + return true; + + // This is a duplicate. We want to discard the sections in this + // group. + size_t count = shdr.get_sh_size() / sizeof(elfcpp::Elf_Word); + for (size_t i = 1; i < count; ++i) + { + elfcpp::Elf_Word secnum = + elfcpp::Swap<32, big_endian>::readval(pword + i); + if (secnum >= this->shnum()) + { + fprintf(stderr, + _("%s: %s: section %u in section group %u out of range"), + program_name, this->name().c_str(), secnum, + index); + gold_exit(false); + } + (*omit)[secnum] = true; + } + + return false; +} + +// Whether to include a linkonce section in the link. NAME is the +// name of the section and SHDR is the section header. + +// Linkonce sections are a GNU extension implemented in the original +// GNU linker before section groups were defined. The semantics are +// that we only include one linkonce section with a given name. The +// name of a linkonce section is normally .gnu.linkonce.T.SYMNAME, +// where T is the type of section and SYMNAME is the name of a symbol. +// In an attempt to make linkonce sections interact well with section +// groups, we try to identify SYMNAME and use it like a section group +// signature. We want to block section groups with that signature, +// but not other linkonce sections with that signature. We also use +// the full name of the linkonce section as a normal section group +// signature. + +template<int size, bool big_endian> +bool +Sized_relobj<size, big_endian>::include_linkonce_section( + Layout* layout, + const char* name, + const elfcpp::Shdr<size, big_endian>&) +{ + const char* symname = strrchr(name, '.') + 1; + bool include1 = layout->add_comdat(symname, false); + bool include2 = layout->add_comdat(name, true); + return include1 && include2; +} + +// Lay out the input sections. We walk through the sections and check +// whether they should be included in the link. If they should, we +// pass them to the Layout object, which will return an output section +// and an offset. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::do_layout(const General_options& options, + Symbol_table* symtab, + Layout* 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); + + std::vector<Map_to_output>& map_sections(this->map_to_output()); + map_sections.resize(shnum); + + // Keep track of which sections to omit. + std::vector<bool> omit(shnum, false); + + // 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(); + + if (this->handle_gnu_warning_section(name, i, symtab)) + { + if (!options.is_relocatable()) + omit[i] = true; + } + + bool discard = omit[i]; + if (!discard) + { + if (shdr.get_sh_type() == elfcpp::SHT_GROUP) + { + if (!this->include_section_group(layout, i, shdr, &omit)) + discard = true; + } + else if (Layout::is_linkonce(name)) + { + if (!this->include_linkonce_section(layout, name, shdr)) + discard = true; + } + } + + if (discard) + { + // Do not include this section in the link. + map_sections[i].output_section = NULL; + continue; + } + + off_t offset; + Output_section* os = layout->layout(this, i, name, shdr, &offset); + + map_sections[i].output_section = os; + map_sections[i].offset = offset; + } + + delete sd->section_headers; + sd->section_headers = NULL; + delete sd->section_names; + sd->section_names = NULL; +} + +// Add the symbols to the symbol table. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::do_add_symbols(Symbol_table* symtab, + Read_symbols_data* sd) +{ + if (sd->symbols == NULL) + { + gold_assert(sd->symbol_names == NULL); + return; + } + + const int sym_size = This::sym_size; + size_t symcount = sd->symbols_size / sym_size; + if (symcount * sym_size != sd->symbols_size) + { + fprintf(stderr, + _("%s: %s: size of symbols is not multiple of symbol size\n"), + program_name, this->name().c_str()); + gold_exit(false); + } + + this->symbols_ = new Symbol*[symcount]; + + const char* sym_names = + reinterpret_cast<const char*>(sd->symbol_names->data()); + symtab->add_from_relobj(this, sd->symbols->data(), symcount, sym_names, + sd->symbol_names_size, this->symbols_); + + delete sd->symbols; + sd->symbols = NULL; + delete sd->symbol_names; + sd->symbol_names = NULL; +} + +// Finalize the local symbols. Here we record the file offset at +// which they should be output, we add their names to *POOL, and we +// add their values to THIS->LOCAL_VALUES_. Return the symbol index. +// This function is always called from the main thread. The actual +// output of the local symbols will occur in a separate task. + +template<int size, bool big_endian> +unsigned int +Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index, + off_t off, + Stringpool* pool) +{ + gold_assert(this->symtab_shndx_ != -1U); + if (this->symtab_shndx_ == 0) + { + // This object has no symbols. Weird but legal. + return index; + } + + gold_assert(off == static_cast<off_t>(align_address(off, size >> 3))); + + this->local_symbol_offset_ = off; + + // Read the symbol table section header. + const unsigned int symtab_shndx = this->symtab_shndx_; + typename This::Shdr symtabshdr(this, + this->elf_file_.section_header(symtab_shndx)); + gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); + + // Read the local symbols. + const int sym_size = This::sym_size; + const unsigned int loccount = this->local_symbol_count_; + gold_assert(loccount == symtabshdr.get_sh_info()); + off_t locsize = loccount * sym_size; + const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(), + locsize); + + this->local_values_.resize(loccount); + + // Read the symbol names. + const unsigned int strtab_shndx = symtabshdr.get_sh_link(); + off_t strtab_size; + const unsigned char* pnamesu = this->section_contents(strtab_shndx, + &strtab_size); + const char* pnames = reinterpret_cast<const char*>(pnamesu); + + // Loop over the local symbols. + + const std::vector<Map_to_output>& mo(this->map_to_output()); + unsigned int shnum = this->shnum(); + unsigned int count = 0; + // Skip the first, dummy, symbol. + psyms += sym_size; + for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size) + { + elfcpp::Sym<size, big_endian> sym(psyms); + + Symbol_value<size>& lv(this->local_values_[i]); + + unsigned int shndx = sym.get_st_shndx(); + lv.set_input_shndx(shndx); + + if (shndx >= elfcpp::SHN_LORESERVE) + { + if (shndx == elfcpp::SHN_ABS) + lv.set_output_value(sym.get_st_value()); + else + { + // FIXME: Handle SHN_XINDEX. + fprintf(stderr, + _("%s: %s: unknown section index %u " + "for local symbol %u\n"), + program_name, this->name().c_str(), shndx, i); + gold_exit(false); + } + } + else + { + if (shndx >= shnum) + { + fprintf(stderr, + _("%s: %s: local symbol %u section index %u " + "out of range\n"), + program_name, this->name().c_str(), i, shndx); + gold_exit(false); + } + + Output_section* os = mo[shndx].output_section; + + if (os == NULL) + { + lv.set_output_value(0); + lv.set_no_output_symtab_entry(); + continue; + } + + if (mo[shndx].offset == -1) + lv.set_input_value(sym.get_st_value()); + else + lv.set_output_value(mo[shndx].output_section->address() + + mo[shndx].offset + + sym.get_st_value()); + } + + // Decide whether this symbol should go into the output file. + + if (sym.get_st_type() == elfcpp::STT_SECTION) + { + lv.set_no_output_symtab_entry(); + continue; + } + + if (sym.get_st_name() >= strtab_size) + { + fprintf(stderr, + _("%s: %s: local symbol %u section name " + "out of range: %u >= %u\n"), + program_name, this->name().c_str(), + i, sym.get_st_name(), + static_cast<unsigned int>(strtab_size)); + gold_exit(false); + } + + const char* name = pnames + sym.get_st_name(); + pool->add(name, NULL); + lv.set_output_symtab_index(index); + ++index; + ++count; + } + + this->output_local_symbol_count_ = count; + + return index; +} + +// Return the value of a local symbol defined in input section SHNDX, +// with value VALUE, adding addend ADDEND. This handles SHF_MERGE +// sections. +template<int size, bool big_endian> +typename elfcpp::Elf_types<size>::Elf_Addr +Sized_relobj<size, big_endian>::local_value(unsigned int shndx, + Address value, + Address addend) const +{ + const std::vector<Map_to_output>& mo(this->map_to_output()); + Output_section* os = mo[shndx].output_section; + if (os == NULL) + return addend; + gold_assert(mo[shndx].offset == -1); + return os->output_address(this, shndx, value + addend); +} + +// Write out the local symbols. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of, + const Stringpool* sympool) +{ + gold_assert(this->symtab_shndx_ != -1U); + if (this->symtab_shndx_ == 0) + { + // This object has no symbols. Weird but legal. + return; + } + + // Read the symbol table section header. + const unsigned int symtab_shndx = this->symtab_shndx_; + typename This::Shdr symtabshdr(this, + this->elf_file_.section_header(symtab_shndx)); + gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); + const unsigned int loccount = this->local_symbol_count_; + gold_assert(loccount == symtabshdr.get_sh_info()); + + // Read the local symbols. + const int sym_size = This::sym_size; + off_t locsize = loccount * sym_size; + const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(), + locsize); + + // Read the symbol names. + const unsigned int strtab_shndx = symtabshdr.get_sh_link(); + off_t strtab_size; + const unsigned char* pnamesu = this->section_contents(strtab_shndx, + &strtab_size); + const char* pnames = reinterpret_cast<const char*>(pnamesu); + + // Get a view into the output file. + off_t output_size = this->output_local_symbol_count_ * sym_size; + unsigned char* oview = of->get_output_view(this->local_symbol_offset_, + output_size); + + const std::vector<Map_to_output>& mo(this->map_to_output()); + + gold_assert(this->local_values_.size() == loccount); + + unsigned char* ov = oview; + psyms += sym_size; + for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size) + { + elfcpp::Sym<size, big_endian> isym(psyms); + + if (!this->local_values_[i].needs_output_symtab_entry()) + continue; + + unsigned int st_shndx = isym.get_st_shndx(); + if (st_shndx < elfcpp::SHN_LORESERVE) + { + gold_assert(st_shndx < mo.size()); + if (mo[st_shndx].output_section == NULL) + continue; + st_shndx = mo[st_shndx].output_section->out_shndx(); + } + + elfcpp::Sym_write<size, big_endian> osym(ov); + + gold_assert(isym.get_st_name() < strtab_size); + const char* name = pnames + isym.get_st_name(); + osym.put_st_name(sympool->get_offset(name)); + osym.put_st_value(this->local_values_[i].value(this, 0)); + osym.put_st_size(isym.get_st_size()); + osym.put_st_info(isym.get_st_info()); + osym.put_st_other(isym.get_st_other()); + osym.put_st_shndx(st_shndx); + + ov += sym_size; + } + + gold_assert(ov - oview == output_size); + + of->write_output_view(this->local_symbol_offset_, output_size, oview); +} + +// Input_objects methods. + +// Add a regular relocatable object to the list. Return false if this +// object should be ignored. + +bool +Input_objects::add_object(Object* obj) +{ + if (!obj->is_dynamic()) + this->relobj_list_.push_back(static_cast<Relobj*>(obj)); + else + { + // See if this is a duplicate SONAME. + Dynobj* dynobj = static_cast<Dynobj*>(obj); + + std::pair<Unordered_set<std::string>::iterator, bool> ins = + this->sonames_.insert(dynobj->soname()); + if (!ins.second) + { + // We have already seen a dynamic object with this soname. + return false; + } + + this->dynobj_list_.push_back(dynobj); + } + + Target* target = obj->target(); + if (this->target_ == NULL) + this->target_ = target; + else if (this->target_ != target) + { + fprintf(stderr, "%s: %s: incompatible target\n", + program_name, obj->name().c_str()); + gold_exit(false); + } + + return true; +} + +// Relocate_info methods. + +// Return a string describing the location of a relocation. This is +// only used in error messages. + +template<int size, bool big_endian> +std::string +Relocate_info<size, big_endian>::location(size_t relnum, off_t) const +{ + std::string ret(this->object->name()); + ret += ": reloc "; + char buf[100]; + snprintf(buf, sizeof buf, "%zu", relnum); + ret += buf; + ret += " in reloc section "; + snprintf(buf, sizeof buf, "%u", this->reloc_shndx); + ret += buf; + ret += " (" + this->object->section_name(this->reloc_shndx); + ret += ") for section "; + snprintf(buf, sizeof buf, "%u", this->data_shndx); + ret += buf; + ret += " (" + this->object->section_name(this->data_shndx) + ")"; + return ret; +} + +} // End namespace gold. + +namespace +{ + +using namespace gold; + +// Read an ELF file with the header and return the appropriate +// instance of Object. + +template<int size, bool big_endian> +Object* +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) + { + Sized_relobj<size, big_endian>* obj = + new Sized_relobj<size, big_endian>(name, input_file, offset, ehdr); + 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 + { + fprintf(stderr, _("%s: %s: unsupported ELF file type %d\n"), + program_name, name.c_str(), et); + gold_exit(false); + } +} + +} // End anonymous namespace. + +namespace gold +{ + +// Read an ELF file and return the appropriate instance of Object. + +Object* +make_elf_object(const std::string& name, Input_file* input_file, off_t offset, + const unsigned char* p, off_t bytes) +{ + if (bytes < elfcpp::EI_NIDENT) + { + fprintf(stderr, _("%s: %s: ELF file too short\n"), + program_name, name.c_str()); + gold_exit(false); + } + + int v = p[elfcpp::EI_VERSION]; + if (v != elfcpp::EV_CURRENT) + { + if (v == elfcpp::EV_NONE) + fprintf(stderr, _("%s: %s: invalid ELF version 0\n"), + program_name, name.c_str()); + else + fprintf(stderr, _("%s: %s: unsupported ELF version %d\n"), + program_name, name.c_str(), v); + gold_exit(false); + } + + int c = p[elfcpp::EI_CLASS]; + if (c == elfcpp::ELFCLASSNONE) + { + fprintf(stderr, _("%s: %s: invalid ELF class 0\n"), + program_name, name.c_str()); + gold_exit(false); + } + else if (c != elfcpp::ELFCLASS32 + && c != elfcpp::ELFCLASS64) + { + fprintf(stderr, _("%s: %s: unsupported ELF class %d\n"), + program_name, name.c_str(), c); + gold_exit(false); + } + + int d = p[elfcpp::EI_DATA]; + if (d == elfcpp::ELFDATANONE) + { + fprintf(stderr, _("%s: %s: invalid ELF data encoding\n"), + program_name, name.c_str()); + gold_exit(false); + } + else if (d != elfcpp::ELFDATA2LSB + && d != elfcpp::ELFDATA2MSB) + { + fprintf(stderr, _("%s: %s: unsupported ELF data encoding %d\n"), + program_name, name.c_str(), d); + gold_exit(false); + } + + bool big_endian = d == elfcpp::ELFDATA2MSB; + + if (c == elfcpp::ELFCLASS32) + { + if (bytes < elfcpp::Elf_sizes<32>::ehdr_size) + { + fprintf(stderr, _("%s: %s: ELF file too short\n"), + program_name, name.c_str()); + gold_exit(false); + } + if (big_endian) + { + elfcpp::Ehdr<32, true> ehdr(p); + return make_elf_sized_object<32, true>(name, input_file, + offset, ehdr); + } + else + { + elfcpp::Ehdr<32, false> ehdr(p); + return make_elf_sized_object<32, false>(name, input_file, + offset, ehdr); + } + } + else + { + if (bytes < elfcpp::Elf_sizes<32>::ehdr_size) + { + fprintf(stderr, _("%s: %s: ELF file too short\n"), + program_name, name.c_str()); + gold_exit(false); + } + if (big_endian) + { + elfcpp::Ehdr<64, true> ehdr(p); + return make_elf_sized_object<64, true>(name, input_file, + offset, ehdr); + } + else + { + elfcpp::Ehdr<64, false> ehdr(p); + return make_elf_sized_object<64, false>(name, input_file, + offset, ehdr); + } + } +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones for implemented targets. + +template +class Sized_relobj<32, false>; + +template +class Sized_relobj<32, true>; + +template +class Sized_relobj<64, false>; + +template +class Sized_relobj<64, true>; + +template +struct Relocate_info<32, false>; + +template +struct Relocate_info<32, true>; + +template +struct Relocate_info<64, false>; + +template +struct Relocate_info<64, true>; + +} // End namespace gold. diff --git a/gold/object.h b/gold/object.h new file mode 100644 index 000000000000..2027f8e97dff --- /dev/null +++ b/gold/object.h @@ -0,0 +1,820 @@ +// object.h -- support for an object file for linking in gold -*- C++ -*- + +#ifndef GOLD_OBJECT_H +#define GOLD_OBJECT_H + +#include <string> +#include <vector> + +#include "elfcpp.h" +#include "elfcpp_file.h" +#include "fileread.h" +#include "target.h" + +namespace gold +{ + +class General_options; +class Layout; +class Output_section; +class Output_file; +class Dynobj; + +template<typename Stringpool_char> +class Stringpool_template; + +// Data to pass from read_symbols() to add_symbols(). + +struct Read_symbols_data +{ + // Section headers. + File_view* section_headers; + // Section names. + File_view* section_names; + // Size of section name data in bytes. + off_t section_names_size; + // Symbol data. + File_view* symbols; + // Size of symbol data in bytes. + off_t symbols_size; + // Symbol names. + 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 +// read_relocs and processed in scan_relocs. + +struct Section_relocs +{ + // Index of reloc section. + unsigned int reloc_shndx; + // Index of section that relocs apply to. + unsigned int data_shndx; + // Contents of reloc section. + File_view* contents; + // Reloc section type. + unsigned int sh_type; + // Number of reloc entries. + size_t reloc_count; +}; + +// Relocations in an object file. This is read in read_relocs and +// processed in scan_relocs. + +struct Read_relocs_data +{ + typedef std::vector<Section_relocs> Relocs_list; + // The relocations. + Relocs_list relocs; + // The local symbols. + File_view* local_symbols; +}; + +// Object is an abstract base class which represents either a 32-bit +// or a 64-bit input object. This can be a regular object file +// (ET_REL) or a shared object (ET_DYN). + +class Object +{ + public: + // NAME is the name of the object as we would report it to the user + // (e.g., libfoo.a(bar.o) if this is in an archive. INPUT_FILE is + // used to read the file. OFFSET is the offset within the input + // 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), shnum_(-1U), + is_dynamic_(is_dynamic), target_(NULL) + { } + + virtual ~Object() + { } + + // Return the name of the object as we would report it to the tuser. + const std::string& + name() const + { return this->name_; } + + // Return whether this is a dynamic object. + bool + is_dynamic() const + { return this->is_dynamic_; } + + // Return the target structure associated with this object. + Target* + target() const + { return this->target_; } + + // Lock the underlying file. + void + lock() + { this->input_file_->file().lock(); } + + // Unlock the underlying file. + void + unlock() + { this->input_file_->file().unlock(); } + + // Return whether the underlying file is locked. + bool + is_locked() const + { return this->input_file_->file().is_locked(); } + + // Return the sized target structure associated with this object. + // This is like the target method but it returns a pointer of + // appropriate checked type. + template<int size, bool big_endian> + Sized_target<size, big_endian>* + sized_target(ACCEPT_SIZE_ENDIAN_ONLY); + + // Get the number of sections. + unsigned int + shnum() const + { return this->shnum_; } + + // Return a view of the contents of a section. Set *PLEN to the + // size. + const unsigned char* + section_contents(unsigned int shndx, off_t* plen); + + // Return the name of a section given a section index. This is only + // used for error messages. + std::string + section_name(unsigned int shndx) + { return this->do_section_name(shndx); } + + // Return the section flags given a section index. + uint64_t + section_flags(unsigned int shndx) + { return this->do_section_flags(shndx); } + + // Read the symbol information. + void + read_symbols(Read_symbols_data* sd) + { return this->do_read_symbols(sd); } + + // Pass sections which should be included in the link to the Layout + // object, and record where the sections go in the output file. + void + layout(const General_options& options, Symbol_table* symtab, + Layout* layout, Read_symbols_data* sd) + { this->do_layout(options, symtab, layout, sd); } + + // Add symbol information to the global symbol table. + void + add_symbols(Symbol_table* symtab, Read_symbols_data* sd) + { this->do_add_symbols(symtab, sd); } + + // Functions and types for the elfcpp::Elf_file interface. This + // permit us to use Object as the File template parameter for + // elfcpp::Elf_file. + + // The View class is returned by view. It must support a single + // method, data(). This is trivial, because get_view does what we + // need. + class View + { + public: + View(const unsigned char* p) + : p_(p) + { } + + const unsigned char* + data() const + { return this->p_; } + + private: + const unsigned char* p_; + }; + + // Return a View. + View + view(off_t file_offset, off_t data_size) + { return View(this->get_view(file_offset, data_size)); } + + // Report an error. + void + error(const char* format, ...) ATTRIBUTE_PRINTF_2; + + // A location in the file. + struct Location + { + off_t file_offset; + off_t data_size; + + Location(off_t fo, off_t ds) + : file_offset(fo), data_size(ds) + { } + }; + + // Get a View given a Location. + View view(Location loc) + { return View(this->get_view(loc.file_offset, loc.data_size)); } + + protected: + // Read the symbols--implemented by child class. + virtual void + do_read_symbols(Read_symbols_data*) = 0; + + // Lay out sections--implemented by child class. + virtual void + do_layout(const General_options&, Symbol_table*, Layout*, + Read_symbols_data*) = 0; + + // Add symbol information to the global symbol table--implemented by + // child class. + virtual void + do_add_symbols(Symbol_table*, Read_symbols_data*) = 0; + + // Return the location of the contents of a section. Implemented by + // child class. + virtual Location + do_section_contents(unsigned int shndx) = 0; + + // Get the name of a section--implemented by child class. + virtual std::string + do_section_name(unsigned int shndx) = 0; + + // Get section flags--implemented by child class. + virtual uint64_t + do_section_flags(unsigned int shndx) = 0; + + // Get the file. + Input_file* + input_file() const + { return this->input_file_; } + + // Get the offset into the file. + off_t + offset() const + { return this->offset_; } + + // Get a view into the underlying file. + const unsigned char* + get_view(off_t start, off_t size) + { return this->input_file_->file().get_view(start + this->offset_, size); } + + // Get a lasting view into the underlying file. + File_view* + get_lasting_view(off_t start, off_t size) + { + return this->input_file_->file().get_lasting_view(start + this->offset_, + size); + } + + // Read data from the underlying file. + void + read(off_t start, off_t size, void* p) + { this->input_file_->file().read(start + this->offset_, size, p); } + + // Set the target. + void + set_target(int machine, int size, bool big_endian, int osabi, + int abiversion); + + // 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. + Object(const Object&); + Object& operator=(const Object&); + + // Name of object as printed to user. + std::string name_; + // For reading the file. + Input_file* input_file_; + // 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. + Target* target_; +}; + +// Implement sized_target inline for efficiency. This approach breaks +// static type checking, but is made safe using asserts. + +template<int size, bool big_endian> +inline Sized_target<size, big_endian>* +Object::sized_target(ACCEPT_SIZE_ENDIAN_ONLY) +{ + gold_assert(this->target_->get_size() == size); + gold_assert(this->target_->is_big_endian() ? big_endian : !big_endian); + return static_cast<Sized_target<size, big_endian>*>(this->target_); +} + +// A regular object (ET_REL). This is an abstract base class itself. +// The implementation is the template class Sized_relobj. + +class Relobj : public Object +{ + public: + Relobj(const std::string& name, Input_file* input_file, off_t offset = 0) + : Object(name, input_file, false, offset) + { } + + // Read the relocs. + void + read_relocs(Read_relocs_data* rd) + { return this->do_read_relocs(rd); } + + // Scan the relocs and adjust the symbol table. + void + scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Read_relocs_data* rd) + { return this->do_scan_relocs(options, symtab, layout, rd); } + + // Initial local symbol processing: set the offset where local + // symbol information will be stored; add local symbol names to + // *POOL; return the new local symbol index. + unsigned int + finalize_local_symbols(unsigned int index, off_t off, + Stringpool_template<char>* pool) + { return this->do_finalize_local_symbols(index, off, pool); } + + // Relocate the input sections and write out the local symbols. + void + relocate(const General_options& options, const Symbol_table* symtab, + const Layout* layout, Output_file* of) + { return this->do_relocate(options, symtab, layout, of); } + + // Return whether an input section is being included in the link. + bool + is_section_included(unsigned int shndx) const + { + gold_assert(shndx < this->map_to_output_.size()); + return this->map_to_output_[shndx].output_section != NULL; + } + + // Given a section index, return the corresponding Output_section + // (which will be NULL if the section is not included in the link) + // and set *POFF to the offset within that section. + inline Output_section* + output_section(unsigned int shndx, off_t* poff) const; + + // Set the offset of an input section within its output section. + void + set_section_offset(unsigned int shndx, off_t off) + { + gold_assert(shndx < this->map_to_output_.size()); + this->map_to_output_[shndx].offset = off; + } + + protected: + // What we need to know to map an input section to an output + // section. We keep an array of these, one for each input section, + // indexed by the input section number. + struct Map_to_output + { + // The output section. This is NULL if the input section is to be + // discarded. + Output_section* output_section; + // The offset within the output section. This is -1 if the + // section requires special handling. + off_t offset; + }; + + // Read the relocs--implemented by child class. + virtual void + do_read_relocs(Read_relocs_data*) = 0; + + // Scan the relocs--implemented by child class. + virtual void + do_scan_relocs(const General_options&, Symbol_table*, Layout*, + Read_relocs_data*) = 0; + + // Finalize local symbols--implemented by child class. + virtual unsigned int + do_finalize_local_symbols(unsigned int, off_t, + Stringpool_template<char>*) = 0; + + // Relocate the input sections and write out the local + // symbols--implemented by child class. + virtual void + do_relocate(const General_options& options, const Symbol_table* symtab, + const Layout*, Output_file* of) = 0; + + // Return the vector mapping input sections to output sections. + std::vector<Map_to_output>& + map_to_output() + { return this->map_to_output_; } + + const std::vector<Map_to_output>& + map_to_output() const + { return this->map_to_output_; } + + private: + // Mapping from input sections to output section. + std::vector<Map_to_output> map_to_output_; +}; + +// Implement Object::output_section inline for efficiency. +inline Output_section* +Relobj::output_section(unsigned int shndx, off_t* poff) const +{ + gold_assert(shndx < this->map_to_output_.size()); + const Map_to_output& mo(this->map_to_output_[shndx]); + *poff = mo.offset; + return mo.output_section; +} + +// This POD class is holds the value of a symbol. This is used for +// local symbols, and for all symbols during relocation processing. +// In order to process relocs we need to be able to handle SHF_MERGE +// sections correctly. + +template<int size> +class Symbol_value +{ + public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Value; + + Symbol_value() + : output_symtab_index_(0), input_shndx_(0), needs_output_address_(false), + value_(0) + { } + + // Get the value of this symbol. OBJECT is the object in which this + // symbol is defined, and ADDEND is an addend to add to the value. + template<bool big_endian> + Value + value(const Sized_relobj<size, big_endian>* object, Value addend) const + { + if (!this->needs_output_address_) + return this->value_ + addend; + return object->local_value(this->input_shndx_, this->value_, addend); + } + + // Set the value of this symbol in the output symbol table. + void + set_output_value(Value value) + { + this->value_ = value; + this->needs_output_address_ = false; + } + + // If this symbol is mapped to an output section which requires + // special handling to determine the output value, we store the + // value of the symbol in the input file. This is used for + // SHF_MERGE sections. + void + set_input_value(Value value) + { + this->value_ = value; + this->needs_output_address_ = true; + } + + // Return whether this symbol should go into the output symbol + // table. + bool + needs_output_symtab_entry() const + { + gold_assert(this->output_symtab_index_ != 0); + return this->output_symtab_index_ != -1U; + } + + // Return the index in the output symbol table. + unsigned int + output_symtab_index() const + { + gold_assert(this->output_symtab_index_ != 0); + return this->output_symtab_index_; + } + + // Set the index in the output symbol table. + void + set_output_symtab_index(unsigned int i) + { + gold_assert(this->output_symtab_index_ == 0); + this->output_symtab_index_ = i; + } + + // Record that this symbol should not go into the output symbol + // table. + void + set_no_output_symtab_entry() + { + gold_assert(this->output_symtab_index_ == 0); + this->output_symtab_index_ = -1U; + } + + // Set the index of the input section in the input file. + void + set_input_shndx(unsigned int i) + { this->input_shndx_ = i; } + + private: + // The index of this local symbol in the output symbol table. This + // will be -1 if the symbol should not go into the symbol table. + unsigned int output_symtab_index_; + // The section index in the input file in which this symbol is + // defined. + unsigned int input_shndx_ : 31; + // Whether getting the value of this symbol requires calling an + // Output_section method. For example, this will be true of a + // STT_SECTION symbol in a SHF_MERGE section. + bool needs_output_address_ : 1; + // The value of the symbol. If !needs_output_address_, this is the + // value in the output file. If needs_output_address_, this is the + // value in the input file. + Value value_; +}; + +// A regular object file. This is size and endian specific. + +template<int size, bool big_endian> +class Sized_relobj : public Relobj +{ + public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + typedef std::vector<Symbol_value<size> > Local_values; + + Sized_relobj(const std::string& name, Input_file* input_file, off_t offset, + const typename elfcpp::Ehdr<size, big_endian>&); + + ~Sized_relobj(); + + // Set up the object file based on the ELF header. + void + setup(const typename elfcpp::Ehdr<size, big_endian>&); + + // Return the index of local symbol SYM in the ordinary symbol + // table. A value of -1U means that the symbol is not being output. + unsigned int + symtab_index(unsigned int sym) const + { + gold_assert(sym < this->local_values_.size()); + return this->local_values_[sym].output_symtab_index(); + } + + // Read the symbols. + 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*); + + // Read the relocs. + void + do_read_relocs(Read_relocs_data*); + + // Scan the relocs and adjust the symbol table. + void + do_scan_relocs(const General_options&, Symbol_table*, Layout*, + Read_relocs_data*); + + // Finalize the local symbols. + unsigned int + do_finalize_local_symbols(unsigned int, off_t, + Stringpool_template<char>*); + + // Relocate the input sections and write out the local symbols. + void + do_relocate(const General_options& options, const Symbol_table* symtab, + const Layout*, Output_file* of); + + // Get the name of a section. + std::string + do_section_name(unsigned int shndx) + { return this->elf_file_.section_name(shndx); } + + // Return the location of the contents of a section. + Object::Location + do_section_contents(unsigned int shndx) + { return this->elf_file_.section_contents(shndx); } + + // Return section flags. + uint64_t + do_section_flags(unsigned int shndx) + { return this->elf_file_.section_flags(shndx); } + + // Return the appropriate Sized_target structure. + Sized_target<size, big_endian>* + sized_target() + { + return this->Object::sized_target + SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + SELECT_SIZE_ENDIAN_ONLY(size, big_endian)); + } + + // Return the value of a local symbol define in input section SHNDX, + // with value VALUE, adding addend ADDEND. This handles SHF_MERGE + // sections. + Address + local_value(unsigned int shndx, Address value, Address addend) const; + + private: + // For convenience. + typedef Sized_relobj<size, big_endian> This; + static const int ehdr_size = elfcpp::Elf_sizes<size>::ehdr_size; + static const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; + 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, + const elfcpp::Shdr<size, big_endian>&, + std::vector<bool>*); + + // Whether to include a linkonce section in the link. + bool + include_linkonce_section(Layout*, const char*, + const elfcpp::Shdr<size, big_endian>&); + + // Views and sizes when relocating. + struct View_size + { + unsigned char* view; + typename elfcpp::Elf_types<size>::Elf_Addr address; + off_t offset; + off_t view_size; + }; + + typedef std::vector<View_size> Views; + + // Write section data to the output file. Record the views and + // sizes in VIEWS for use when relocating. + void + write_sections(const unsigned char* pshdrs, Output_file*, Views*); + + // Relocate the sections in the output file. + void + relocate_sections(const General_options& options, const Symbol_table*, + const Layout*, const unsigned char* pshdrs, Views*); + + // Write out the local symbols. + void + write_local_symbols(Output_file*, + const Stringpool_template<char>*); + + // General access to the ELF file. + elfcpp::Elf_file<size, big_endian, Object> elf_file_; + // Index of SHT_SYMTAB section. + unsigned int symtab_shndx_; + // The number of local symbols. + unsigned int local_symbol_count_; + // The number of local symbols which go into the output file. + unsigned int output_local_symbol_count_; + // The entries in the symbol table for the external symbols. + Symbol** symbols_; + // File offset for local symbols. + off_t local_symbol_offset_; + // Values of local symbols. + Local_values local_values_; +}; + +// A class to manage the list of all objects. + +class Input_objects +{ + public: + Input_objects() + : relobj_list_(), dynobj_list_(), target_(NULL), sonames_() + { } + + // The type of the list of input relocateable objects. + typedef std::vector<Relobj*> Relobj_list; + typedef Relobj_list::const_iterator Relobj_iterator; + + // The type of the list of input dynamic objects. + typedef std::vector<Dynobj*> Dynobj_list; + typedef Dynobj_list::const_iterator Dynobj_iterator; + + // Add an object to the list. Return true if all is well, or false + // if this object should be ignored. + bool + add_object(Object*); + + // Get the target we should use for the output file. + Target* + target() const + { return this->target_; } + + // Iterate over all regular objects. + + Relobj_iterator + relobj_begin() const + { return this->relobj_list_.begin(); } + + Relobj_iterator + relobj_end() const + { return this->relobj_list_.end(); } + + // Iterate over all dynamic objects. + + Dynobj_iterator + dynobj_begin() const + { return this->dynobj_list_.begin(); } + + Dynobj_iterator + dynobj_end() const + { return this->dynobj_list_.end(); } + + // Return whether we have seen any dynamic objects. + bool + any_dynamic() const + { return !this->dynobj_list_.empty(); } + + private: + Input_objects(const Input_objects&); + Input_objects& operator=(const Input_objects&); + + // The list of ordinary objects included in the link. + Relobj_list relobj_list_; + // The list of dynamic objects included in the link. + Dynobj_list dynobj_list_; + // The target. + Target* target_; + // SONAMEs that we have seen. + Unordered_set<std::string> sonames_; +}; + +// Some of the information we pass to the relocation routines. We +// group this together to avoid passing a dozen different arguments. + +template<int size, bool big_endian> +struct Relocate_info +{ + // Command line options. + const General_options* options; + // Symbol table. + const Symbol_table* symtab; + // Layout. + const Layout* layout; + // Object being relocated. + Sized_relobj<size, big_endian>* object; + // Number of local symbols. + unsigned int local_symbol_count; + // Values of local symbols. + const typename Sized_relobj<size, big_endian>::Local_values* local_values; + // Global symbols. + const Symbol* const * symbols; + // Section index of relocation section. + unsigned int reloc_shndx; + // Section index of section being relocated. + unsigned int data_shndx; + + // Return a string showing the location of a relocation. This is + // only used for error messages. + std::string + location(size_t relnum, off_t reloffset) const; +}; + +// Return an Object appropriate for the input file. P is BYTES long, +// and holds the ELF header. + +extern Object* +make_elf_object(const std::string& name, Input_file*, + off_t offset, const unsigned char* p, + off_t bytes); + +} // end namespace gold + +#endif // !defined(GOLD_OBJECT_H) diff --git a/gold/options.cc b/gold/options.cc new file mode 100644 index 000000000000..b8339e8a6253 --- /dev/null +++ b/gold/options.cc @@ -0,0 +1,579 @@ +// options.c -- handle command line options for gold + +#include <iostream> + +#include "gold.h" +#include "options.h" + +namespace gold +{ + +// The information we keep for a single command line option. + +struct options::One_option +{ + // The single character option name, or '\0' if this is only a long + // option. + char short_option; + + // The long option name, or NULL if this is only a short option. + const char* long_option; + + // Description of the option for --help output, or NULL if there is none. + const char* doc; + + // How to print the option name in --help output, or NULL to use the + // default. + const char* help_output; + + // Long option dash control. This is ignored if long_option is + // NULL. + enum + { + // Long option normally takes one dash; two dashes are also + // accepted. + ONE_DASH, + // Long option normally takes two dashes; one dash is also + // accepted. + TWO_DASHES, + // Long option always takes two dashes. + EXACTLY_TWO_DASHES + } dash; + + // Function for special handling, or NULL. Returns the number of + // arguments to skip. This will normally be at least 1, but it may + // be 0 if this function changes *argv. ARG points to the location + // in *ARGV where the option starts, which may be helpful for a + // short option. + int (*special)(int argc, char** argv, char *arg, Command_line*); + + // If this is a position independent option which does not take an + // argument, this is the member function to call to record it. + void (General_options::*general_noarg)(); + + // If this is a position independent function which takes an + // argument, this is the member function to call to record it. + void (General_options::*general_arg)(const char*); + + // If this is a position dependent option which does not take an + // argument, this is the member function to call to record it. + void (Position_dependent_options::*dependent_noarg)(); + + // If this is a position dependent option which takes an argument, + // this is the member function to record it. + void (Position_dependent_options::*dependent_arg)(const char*); + + // Return whether this option takes an argument. + bool + takes_argument() const + { return this->general_arg != NULL || this->dependent_arg != NULL; } +}; + +class options::Command_line_options +{ + public: + static const One_option options[]; + static const int options_size; +}; + +} // End namespace gold. + +namespace +{ + +// Handle the special -l option, which adds an input file. + +int +library(int argc, char** argv, char* arg, gold::Command_line* cmdline) +{ + return cmdline->process_l_option(argc, argv, arg); +} + +// Handle the special --start-group option. + +int +start_group(int, char**, char* arg, gold::Command_line* cmdline) +{ + cmdline->start_group(arg); + return 1; +} + +// Handle the special --end-group option. + +int +end_group(int, char**, char* arg, gold::Command_line* cmdline) +{ + cmdline->end_group(arg); + return 1; +} + +// Report usage information for ld --help, and exit. + +int +help(int, char**, char*, gold::Command_line*) +{ + printf(_("Usage: %s [options] file...\nOptions:\n"), gold::program_name); + + const int options_size = gold::options::Command_line_options::options_size; + const gold::options::One_option* options = + gold::options::Command_line_options::options; + for (int i = 0; i < options_size; ++i) + { + if (options[i].doc == NULL) + continue; + + printf(" "); + int len = 2; + bool comma = false; + + int j = i; + do + { + if (options[j].help_output != NULL) + { + if (comma) + { + printf(", "); + len += 2; + } + printf(options[j].help_output); + len += std::strlen(options[i].help_output); + } + else + { + if (options[j].short_option != '\0') + { + if (comma) + { + printf(", "); + len += 2; + } + printf("-%c", options[j].short_option); + len += 2; + } + + if (options[j].long_option != NULL) + { + if (comma) + { + printf(", "); + len += 2; + } + if (options[j].dash == gold::options::One_option::ONE_DASH) + { + printf("-"); + ++len; + } + else + { + printf("--"); + len += 2; + } + printf("%s", options[j].long_option); + len += std::strlen(options[j].long_option); + } + } + ++j; + } + while (j < options_size && options[j].doc == NULL); + + if (len > 30) + { + printf("\n"); + len = 0; + } + for (; len < 30; ++len) + std::putchar(' '); + + std::puts(options[i].doc); + } + + gold::gold_exit(true); + + return 0; +} + +} // End anonymous namespace. + +namespace gold +{ + +// Helper macros used to specify the options. We could also do this +// using constructors, but then g++ would generate code to initialize +// the array. We want the array to be initialized statically so that +// we get better startup time. + +#define GENERAL_NOARG(short_option, long_option, doc, help, dash, func) \ + { short_option, long_option, doc, help, options::One_option::dash, \ + NULL, func, NULL, NULL, NULL } +#define GENERAL_ARG(short_option, long_option, doc, help, dash, func) \ + { short_option, long_option, doc, help, options::One_option::dash, \ + NULL, NULL, func, NULL, NULL } +#define POSDEP_NOARG(short_option, long_option, doc, help, dash, func) \ + { short_option, long_option, doc, help, options::One_option::dash, \ + NULL, NULL, NULL, func, NULL } +#define POSDEP_ARG(short_option, long_option, doc, help, dash, func) \ + { short_option, long_option, doc, help, options::One_option::dash, \ + NULL, NULL, NULL, NULL, func } +#define SPECIAL(short_option, long_option, doc, help, dash, func) \ + { short_option, long_option, doc, help, options::One_option::dash, \ + func, NULL, NULL, NULL, NULL } + +// Here is the actual list of options which we accept. + +const options::One_option +options::Command_line_options::options[] = +{ + SPECIAL('l', "library", N_("Search for library LIBNAME"), + N_("-lLIBNAME --library LIBNAME"), TWO_DASHES, + &library), + SPECIAL('(', "start-group", N_("Start a library search group"), NULL, + 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), + GENERAL_ARG('m', NULL, N_("Ignored for compatibility"), NULL, ONE_DASH, + &General_options::ignore), + GENERAL_ARG('o', "output", N_("Set output file name"), + N_("-o FILE, --output FILE"), TWO_DASHES, + &General_options::set_output_file_name), + GENERAL_NOARG('r', NULL, N_("Generate relocatable output"), NULL, + ONE_DASH, &General_options::set_relocatable), + GENERAL_NOARG('\0', "shared", N_("Generate shared library"), + 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) +}; + +const int options::Command_line_options::options_size = + sizeof (options) / sizeof (options[0]); + +// The default values for the general options. + +General_options::General_options() + : dynamic_linker_(NULL), + search_path_(), + output_file_name_("a.out"), + is_relocatable_(false), + is_shared_(false), + is_static_(false) +{ +} + +// The default values for the position dependent options. + +Position_dependent_options::Position_dependent_options() + : do_static_search_(false) +{ +} + +// 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 + { + gold_assert(!this->input_argument_list_.empty()); + gold_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() +{ + gold_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() +{ + gold_assert(this->in_group_); + this->in_group_ = false; +} + +// Command_line options. + +Command_line::Command_line() + : options_(), position_options_(), inputs_() +{ +} + +// Process the command line options. + +void +Command_line::process(int argc, char** argv) +{ + const int options_size = options::Command_line_options::options_size; + const options::One_option* options = + options::Command_line_options::options; + bool no_more_options = false; + int i = 0; + while (i < argc) + { + if (argv[i][0] != '-' || no_more_options) + { + this->add_file(argv[i], false); + ++i; + continue; + } + + // Option starting with '-'. + int dashes = 1; + if (argv[i][1] == '-') + { + dashes = 2; + if (argv[i][2] == '\0') + { + no_more_options = true; + continue; + } + } + + // Look for a long option match. + char* opt = argv[i] + dashes; + char first = opt[0]; + int skiparg = 0; + char* arg = strchr(opt, '='); + if (arg != NULL) + *arg = '\0'; + else if (i + 1 < argc) + { + arg = argv[i + 1]; + skiparg = 1; + } + + int j; + for (j = 0; j < options_size; ++j) + { + if (options[j].long_option != NULL + && (dashes == 2 + || (options[j].dash + != options::One_option::EXACTLY_TWO_DASHES)) + && first == options[j].long_option[0] + && strcmp(opt, options[j].long_option) == 0) + { + if (options[j].special) + i += options[j].special(argc - 1, argv + i, opt, this); + else + { + if (!options[j].takes_argument()) + { + arg = NULL; + skiparg = 0; + } + else + { + if (arg == NULL) + this->usage(_("missing argument"), argv[i]); + } + this->apply_option(options[j], arg); + i += skiparg + 1; + } + break; + } + } + if (j < options_size) + continue; + + // If we saw two dashes, we need to see a long option. + if (dashes == 2) + this->usage(_("unknown option"), argv[i]); + + // Look for a short option match. There may be more than one + // short option in a given argument. + bool done = false; + char* s = argv[i] + 1; + ++i; + while (*s != '\0' && !done) + { + char opt = *s; + int j; + for (j = 0; j < options_size; ++j) + { + if (options[j].short_option == opt) + { + if (options[j].special) + { + // Undo the argument skip done above. + --i; + i += options[j].special(argc - i, argv + i, s, this); + done = true; + } + else + { + arg = NULL; + if (options[j].takes_argument()) + { + if (s[1] != '\0') + { + arg = s + 1; + done = true; + } + else if (i < argc) + { + arg = argv[i]; + ++i; + } + else + this->usage(_("missing argument"), opt); + } + this->apply_option(options[j], arg); + } + break; + } + } + + if (j >= options_size) + this->usage(_("unknown option"), *s); + + ++s; + } + } + + if (this->inputs_.in_group()) + { + fprintf(stderr, _("%s: missing group end"), program_name); + this->usage(); + } + + // FIXME: We should only do this when configured in native mode. + this->options_.add_to_search_path("/lib"); + this->options_.add_to_search_path("/usr/lib"); +} + +// Apply a command line option. + +void +Command_line::apply_option(const options::One_option& opt, + const char* arg) +{ + if (arg == NULL) + { + if (opt.general_noarg) + (this->options_.*(opt.general_noarg))(); + else if (opt.dependent_noarg) + (this->position_options_.*(opt.dependent_noarg))(); + else + gold_unreachable(); + } + else + { + if (opt.general_arg) + (this->options_.*(opt.general_arg))(arg); + else if (opt.dependent_arg) + (this->position_options_.*(opt.dependent_arg))(arg); + else + gold_unreachable(); + } +} + +// Add an input file or library. + +void +Command_line::add_file(const char* name, bool is_lib) +{ + Input_file_argument file(name, is_lib, this->position_options_); + this->inputs_.add_file(file); +} + +// Handle the -l option, which requires special treatment. + +int +Command_line::process_l_option(int argc, char** argv, char* arg) +{ + int ret; + const char* libname; + if (arg[1] != '\0') + { + ret = 1; + libname = arg + 1; + } + else if (argc > 1) + { + ret = 2; + libname = argv[argc + 1]; + } + else + this->usage(_("missing argument"), arg); + + this->add_file(libname, true); + + return ret; +} + +// Handle the --start-group option. + +void +Command_line::start_group(const char* arg) +{ + if (this->inputs_.in_group()) + this->usage(_("may not nest groups"), arg); + this->inputs_.start_group(); +} + +// Handle the --end-group option. + +void +Command_line::end_group(const char* arg) +{ + if (!this->inputs_.in_group()) + this->usage(_("group end without group start"), arg); + this->inputs_.end_group(); +} + +// Report a usage error. */ + +void +Command_line::usage() +{ + fprintf(stderr, + _("%s: use the --help option for usage information\n"), + program_name); + gold_exit(false); +} + +void +Command_line::usage(const char* msg, const char *opt) +{ + fprintf(stderr, + _("%s: %s: %s\n"), + program_name, opt, msg); + this->usage(); +} + +void +Command_line::usage(const char* msg, char opt) +{ + fprintf(stderr, + _("%s: -%c: %s\n"), + program_name, opt, msg); + this->usage(); +} + +} // End namespace gold. diff --git a/gold/options.h b/gold/options.h new file mode 100644 index 000000000000..56907c058ead --- /dev/null +++ b/gold/options.h @@ -0,0 +1,394 @@ +// options.h -- handle command line options for gold -*- C++ -*- + +// Command_line +// Holds everything we get from the command line. +// General_options (from Command_line::options()) +// Options which are not position dependent. +// Input_argument (from Command_line::inputs()) +// The list of input files, including -l options. +// Position_dependent_options (from Input_argument::options()) +// Position dependent options which apply to this argument. + +#ifndef GOLD_OPTIONS_H +#define GOLD_OPTIONS_H + +#include <list> +#include <string> +#include <vector> + +namespace gold +{ + +class Command_line; +class Input_file_group; + +namespace options { + +class Command_line_options; +struct One_option; + +} // End namespace gold::options. + +// The position independent options which apply to the whole link. +// There are a lot of them. + +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; + + const Dir_list& + search_path() const + { return this->search_path_; } + + // -o: Output file name. + const char* + output_file_name() const + { return this->output_file_name_; } + + // -r: Whether we are doing a relocatable link. + bool + is_relocatable() const + { return this->is_relocatable_; } + + // --shared: Whether generating a shared object. + bool + is_shared() const + { return this->is_shared_; } + + // --static: Whether doing a static link. + bool + is_static() const + { 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); } + + void + set_output_file_name(const char* arg) + { this->output_file_name_ = arg; } + + void + set_relocatable() + { this->is_relocatable_ = true; } + + void + set_shared() + { this->is_shared_ = true; } + + void + set_static() + { this->is_static_ = true; } + + void + 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_; +}; + +// The current state of the position dependent options. + +class Position_dependent_options +{ + public: + Position_dependent_options(); + + // -Bstatic: Whether we are searching for a static archive rather + // than a shared object. + bool + do_static_search() const + { return this->do_static_search_; } + + // --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() + { this->do_static_search_ = true; } + + void + 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. + +class Input_file_argument +{ + public: + Input_file_argument() + : name_(), is_lib_(false), options_() + { } + + Input_file_argument(const char* name, bool is_lib, + const Position_dependent_options& options) + : name_(name), is_lib_(is_lib), options_(options) + { } + + const char* + name() const + { return this->name_.c_str(); } + + const Position_dependent_options& + options() const + { return this->options_; } + + bool + is_lib() const + { return this->is_lib_; } + + private: + // 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_; +}; + +// A file or library, or a group, from the command line. + +class Input_argument +{ + public: + // Create a file or library argument. + explicit Input_argument(Input_file_argument file) + : is_file_(true), file_(file), group_(NULL) + { } + + // Create a group argument. + explicit Input_argument(Input_file_group* group) + : is_file_(false), group_(group) + { } + + // Return whether this is a file. + bool + is_file() const + { return this->is_file_; } + + // Return whether this is a group. + bool + is_group() const + { return !this->is_file_; } + + // Return the information about the file. + const Input_file_argument& + file() const + { + gold_assert(this->is_file_); + return this->file_; + } + + // Return the information about the group. + const Input_file_group* + group() const + { + gold_assert(!this->is_file_); + return this->group_; + } + + Input_file_group* + group() + { + gold_assert(!this->is_file_); + return this->group_; + } + + private: + bool is_file_; + Input_file_argument file_; + Input_file_group* group_; +}; + +// A group from the command line. This is a set of arguments within +// --start-group ... --end-group. + +class Input_file_group +{ + public: + typedef std::vector<Input_argument> Files; + typedef Files::const_iterator const_iterator; + + Input_file_group() + : files_() + { } + + // Add a file to the end of the group. + void + add_file(const Input_file_argument& arg) + { this->files_.push_back(Input_argument(arg)); } + + // Iterators to iterate over the group contents. + + const_iterator + begin() const + { return this->files_.begin(); } + + const_iterator + end() const + { return this->files_.end(); } + + private: + 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 Input_arguments::const_iterator const_iterator; + + Command_line(); + + // Process the command line options. This will exit with an + // appropriate error message if an unrecognized option is seen. + void + process(int argc, char** argv); + + // Handle a -l option. + int + process_l_option(int, char**, char*); + + // Handle a --start-group option. + void + start_group(const char* arg); + + // Handle a --end-group option. + void + end_group(const char* arg); + + // Get the general options. + const General_options& + options() const + { return this->options_; } + + // Iterators to iterate over the list of input files. + + const_iterator + begin() const + { return this->inputs_.begin(); } + + const_iterator + end() const + { return this->inputs_.end(); } + + private: + Command_line(const Command_line&); + Command_line& operator=(const Command_line&); + + // Report usage error. + void + usage() ATTRIBUTE_NORETURN; + void + usage(const char* msg, const char* opt) ATTRIBUTE_NORETURN; + void + usage(const char* msg, char opt) ATTRIBUTE_NORETURN; + + // Apply a command line option. + void + apply_option(const gold::options::One_option&, const char*); + + // Add a file. + void + add_file(const char* name, bool is_lib); + + General_options options_; + Position_dependent_options position_options_; + Input_arguments inputs_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_OPTIONS_H) diff --git a/gold/output.cc b/gold/output.cc new file mode 100644 index 000000000000..2a7400def77c --- /dev/null +++ b/gold/output.cc @@ -0,0 +1,1670 @@ +// output.cc -- manage the output file for gold + +#include "gold.h" + +#include <cstdlib> +#include <cerrno> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <algorithm> + +#include "object.h" +#include "symtab.h" +#include "reloc.h" +#include "merge.h" +#include "output.h" + +namespace gold +{ + +// Output_data variables. + +bool Output_data::sizes_are_fixed; + +// Output_data methods. + +Output_data::~Output_data() +{ +} + +// Set the address and offset. + +void +Output_data::set_address(uint64_t addr, off_t off) +{ + this->address_ = addr; + this->offset_ = off; + + // Let the child class know. + this->do_set_address(addr, off); +} + +// Return the default alignment for a size--32 or 64. + +uint64_t +Output_data::default_alignment(int size) +{ + if (size == 32) + return 4; + else if (size == 64) + return 8; + else + gold_unreachable(); +} + +// Output_section_header methods. This currently assumes that the +// segment and section lists are complete at construction time. + +Output_section_headers::Output_section_headers( + int size, + bool big_endian, + const Layout* layout, + const Layout::Segment_list* segment_list, + const Layout::Section_list* unattached_section_list, + const Stringpool* secnamepool) + : size_(size), + big_endian_(big_endian), + layout_(layout), + segment_list_(segment_list), + unattached_section_list_(unattached_section_list), + secnamepool_(secnamepool) +{ + // Count all the sections. Start with 1 for the null section. + off_t count = 1; + for (Layout::Segment_list::const_iterator p = segment_list->begin(); + p != segment_list->end(); + ++p) + if ((*p)->type() == elfcpp::PT_LOAD) + count += (*p)->output_section_count(); + count += unattached_section_list->size(); + + int shdr_size; + if (size == 32) + shdr_size = elfcpp::Elf_sizes<32>::shdr_size; + else if (size == 64) + shdr_size = elfcpp::Elf_sizes<64>::shdr_size; + else + gold_unreachable(); + + this->set_data_size(count * shdr_size); +} + +// Write out the section headers. + +void +Output_section_headers::do_write(Output_file* of) +{ + if (this->size_ == 32) + { + if (this->big_endian_) + this->do_sized_write<32, true>(of); + else + this->do_sized_write<32, false>(of); + } + else if (this->size_ == 64) + { + if (this->big_endian_) + this->do_sized_write<64, true>(of); + else + this->do_sized_write<64, false>(of); + } + else + gold_unreachable(); +} + +template<int size, bool big_endian> +void +Output_section_headers::do_sized_write(Output_file* of) +{ + off_t all_shdrs_size = this->data_size(); + unsigned char* view = of->get_output_view(this->offset(), all_shdrs_size); + + const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; + unsigned char* v = view; + + { + typename elfcpp::Shdr_write<size, big_endian> oshdr(v); + oshdr.put_sh_name(0); + oshdr.put_sh_type(elfcpp::SHT_NULL); + oshdr.put_sh_flags(0); + oshdr.put_sh_addr(0); + oshdr.put_sh_offset(0); + oshdr.put_sh_size(0); + oshdr.put_sh_link(0); + oshdr.put_sh_info(0); + oshdr.put_sh_addralign(0); + oshdr.put_sh_entsize(0); + } + + v += shdr_size; + + unsigned shndx = 1; + for (Layout::Segment_list::const_iterator p = this->segment_list_->begin(); + p != this->segment_list_->end(); + ++p) + v = (*p)->write_section_headers SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + this->layout_, this->secnamepool_, v, &shndx + SELECT_SIZE_ENDIAN(size, big_endian)); + for (Layout::Section_list::const_iterator p = + this->unattached_section_list_->begin(); + p != this->unattached_section_list_->end(); + ++p) + { + gold_assert(shndx == (*p)->out_shndx()); + elfcpp::Shdr_write<size, big_endian> oshdr(v); + (*p)->write_header(this->layout_, this->secnamepool_, &oshdr); + v += shdr_size; + ++shndx; + } + + of->write_output_view(this->offset(), all_shdrs_size, view); +} + +// Output_segment_header methods. + +Output_segment_headers::Output_segment_headers( + int size, + bool big_endian, + const Layout::Segment_list& segment_list) + : size_(size), big_endian_(big_endian), segment_list_(segment_list) +{ + int phdr_size; + if (size == 32) + phdr_size = elfcpp::Elf_sizes<32>::phdr_size; + else if (size == 64) + phdr_size = elfcpp::Elf_sizes<64>::phdr_size; + else + gold_unreachable(); + + this->set_data_size(segment_list.size() * phdr_size); +} + +void +Output_segment_headers::do_write(Output_file* of) +{ + if (this->size_ == 32) + { + if (this->big_endian_) + this->do_sized_write<32, true>(of); + else + this->do_sized_write<32, false>(of); + } + else if (this->size_ == 64) + { + if (this->big_endian_) + this->do_sized_write<64, true>(of); + else + this->do_sized_write<64, false>(of); + } + else + gold_unreachable(); +} + +template<int size, bool big_endian> +void +Output_segment_headers::do_sized_write(Output_file* of) +{ + const int phdr_size = elfcpp::Elf_sizes<size>::phdr_size; + off_t all_phdrs_size = this->segment_list_.size() * phdr_size; + unsigned char* view = of->get_output_view(this->offset(), + all_phdrs_size); + unsigned char* v = view; + for (Layout::Segment_list::const_iterator p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + { + elfcpp::Phdr_write<size, big_endian> ophdr(v); + (*p)->write_header(&ophdr); + v += phdr_size; + } + + of->write_output_view(this->offset(), all_phdrs_size, view); +} + +// Output_file_header methods. + +Output_file_header::Output_file_header(int size, + bool big_endian, + const General_options& options, + const Target* target, + const Symbol_table* symtab, + const Output_segment_headers* osh) + : size_(size), + big_endian_(big_endian), + options_(options), + target_(target), + symtab_(symtab), + segment_header_(osh), + section_header_(NULL), + shstrtab_(NULL) +{ + int ehdr_size; + if (size == 32) + ehdr_size = elfcpp::Elf_sizes<32>::ehdr_size; + else if (size == 64) + ehdr_size = elfcpp::Elf_sizes<64>::ehdr_size; + else + gold_unreachable(); + + this->set_data_size(ehdr_size); +} + +// Set the section table information for a file header. + +void +Output_file_header::set_section_info(const Output_section_headers* shdrs, + const Output_section* shstrtab) +{ + this->section_header_ = shdrs; + this->shstrtab_ = shstrtab; +} + +// Write out the file header. + +void +Output_file_header::do_write(Output_file* of) +{ + if (this->size_ == 32) + { + if (this->big_endian_) + this->do_sized_write<32, true>(of); + else + this->do_sized_write<32, false>(of); + } + else if (this->size_ == 64) + { + if (this->big_endian_) + this->do_sized_write<64, true>(of); + else + this->do_sized_write<64, false>(of); + } + else + gold_unreachable(); +} + +// Write out the file header with appropriate size and endianess. + +template<int size, bool big_endian> +void +Output_file_header::do_sized_write(Output_file* of) +{ + gold_assert(this->offset() == 0); + + int ehdr_size = elfcpp::Elf_sizes<size>::ehdr_size; + unsigned char* view = of->get_output_view(0, ehdr_size); + elfcpp::Ehdr_write<size, big_endian> oehdr(view); + + unsigned char e_ident[elfcpp::EI_NIDENT]; + memset(e_ident, 0, elfcpp::EI_NIDENT); + e_ident[elfcpp::EI_MAG0] = elfcpp::ELFMAG0; + e_ident[elfcpp::EI_MAG1] = elfcpp::ELFMAG1; + e_ident[elfcpp::EI_MAG2] = elfcpp::ELFMAG2; + e_ident[elfcpp::EI_MAG3] = elfcpp::ELFMAG3; + if (size == 32) + e_ident[elfcpp::EI_CLASS] = elfcpp::ELFCLASS32; + else if (size == 64) + e_ident[elfcpp::EI_CLASS] = elfcpp::ELFCLASS64; + else + gold_unreachable(); + e_ident[elfcpp::EI_DATA] = (big_endian + ? elfcpp::ELFDATA2MSB + : elfcpp::ELFDATA2LSB); + e_ident[elfcpp::EI_VERSION] = elfcpp::EV_CURRENT; + // FIXME: Some targets may need to set EI_OSABI and EI_ABIVERSION. + oehdr.put_e_ident(e_ident); + + elfcpp::ET e_type; + // FIXME: ET_DYN. + if (this->options_.is_relocatable()) + e_type = elfcpp::ET_REL; + else + e_type = elfcpp::ET_EXEC; + oehdr.put_e_type(e_type); + + oehdr.put_e_machine(this->target_->machine_code()); + oehdr.put_e_version(elfcpp::EV_CURRENT); + + // FIXME: Need to support -e, and target specific entry symbol. + Symbol* sym = this->symtab_->lookup("_start"); + typename Sized_symbol<size>::Value_type v; + if (sym == NULL) + v = 0; + else + { + Sized_symbol<size>* ssym; + ssym = this->symtab_->get_sized_symbol SELECT_SIZE_NAME(size) ( + sym SELECT_SIZE(size)); + v = ssym->value(); + } + oehdr.put_e_entry(v); + + oehdr.put_e_phoff(this->segment_header_->offset()); + oehdr.put_e_shoff(this->section_header_->offset()); + + // FIXME: The target needs to set the flags. + oehdr.put_e_flags(0); + + oehdr.put_e_ehsize(elfcpp::Elf_sizes<size>::ehdr_size); + oehdr.put_e_phentsize(elfcpp::Elf_sizes<size>::phdr_size); + oehdr.put_e_phnum(this->segment_header_->data_size() + / elfcpp::Elf_sizes<size>::phdr_size); + oehdr.put_e_shentsize(elfcpp::Elf_sizes<size>::shdr_size); + oehdr.put_e_shnum(this->section_header_->data_size() + / elfcpp::Elf_sizes<size>::shdr_size); + oehdr.put_e_shstrndx(this->shstrtab_->out_shndx()); + + of->write_output_view(0, ehdr_size, view); +} + +// Output_data_const methods. + +void +Output_data_const::do_write(Output_file* of) +{ + of->write(this->offset(), this->data_.data(), this->data_.size()); +} + +// Output_data_const_buffer methods. + +void +Output_data_const_buffer::do_write(Output_file* of) +{ + of->write(this->offset(), this->p_, this->data_size()); +} + +// Output_section_data methods. + +// Record the output section, and set the entry size and such. + +void +Output_section_data::set_output_section(Output_section* os) +{ + gold_assert(this->output_section_ == NULL); + this->output_section_ = os; + this->do_adjust_output_section(os); +} + +// Return the section index of the output section. + +unsigned int +Output_section_data::do_out_shndx() const +{ + gold_assert(this->output_section_ != NULL); + return this->output_section_->out_shndx(); +} + +// Output_data_strtab methods. + +// Set the address. We don't actually care about the address, but we +// do set our final size. + +void +Output_data_strtab::do_set_address(uint64_t, off_t) +{ + this->strtab_->set_string_offsets(); + this->set_data_size(this->strtab_->get_strtab_size()); +} + +// Write out a string table. + +void +Output_data_strtab::do_write(Output_file* of) +{ + this->strtab_->write(of, this->offset()); +} + +// Output_reloc methods. + +// Get the symbol index of a relocation. + +template<bool dynamic, int size, bool big_endian> +unsigned int +Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::get_symbol_index() + const +{ + unsigned int index; + switch (this->local_sym_index_) + { + case INVALID_CODE: + gold_unreachable(); + + case GSYM_CODE: + if (this->u1_.gsym == NULL) + index = 0; + else if (dynamic) + index = this->u1_.gsym->dynsym_index(); + else + index = this->u1_.gsym->symtab_index(); + break; + + case SECTION_CODE: + if (dynamic) + index = this->u1_.os->dynsym_index(); + else + index = this->u1_.os->symtab_index(); + break; + + default: + if (dynamic) + { + // FIXME: It seems that some targets may need to generate + // dynamic relocations against local symbols for some + // reasons. This will have to be addressed at some point. + gold_unreachable(); + } + else + index = this->u1_.relobj->symtab_index(this->local_sym_index_); + break; + } + gold_assert(index != -1U); + return index; +} + +// Write out the offset and info fields of a Rel or Rela relocation +// entry. + +template<bool dynamic, int size, bool big_endian> +template<typename Write_rel> +void +Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write_rel( + Write_rel* wr) const +{ + Address address = this->address_; + if (this->shndx_ != INVALID_CODE) + { + off_t off; + Output_section* os = this->u2_.relobj->output_section(this->shndx_, + &off); + gold_assert(os != NULL); + address += os->address() + off; + } + else if (this->u2_.od != NULL) + address += this->u2_.od->address(); + wr->put_r_offset(address); + wr->put_r_info(elfcpp::elf_r_info<size>(this->get_symbol_index(), + this->type_)); +} + +// Write out a Rel relocation. + +template<bool dynamic, int size, bool big_endian> +void +Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write( + unsigned char* pov) const +{ + elfcpp::Rel_write<size, big_endian> orel(pov); + this->write_rel(&orel); +} + +// Write out a Rela relocation. + +template<bool dynamic, int size, bool big_endian> +void +Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>::write( + unsigned char* pov) const +{ + elfcpp::Rela_write<size, big_endian> orel(pov); + this->rel_.write_rel(&orel); + orel.put_r_addend(this->addend_); +} + +// Output_data_reloc_base methods. + +// Adjust the output section. + +template<int sh_type, bool dynamic, int size, bool big_endian> +void +Output_data_reloc_base<sh_type, dynamic, size, big_endian> + ::do_adjust_output_section(Output_section* os) +{ + if (sh_type == elfcpp::SHT_REL) + os->set_entsize(elfcpp::Elf_sizes<size>::rel_size); + else if (sh_type == elfcpp::SHT_RELA) + os->set_entsize(elfcpp::Elf_sizes<size>::rela_size); + else + gold_unreachable(); + if (dynamic) + os->set_should_link_to_dynsym(); + else + os->set_should_link_to_symtab(); +} + +// Write out relocation data. + +template<int sh_type, bool dynamic, int size, bool big_endian> +void +Output_data_reloc_base<sh_type, dynamic, size, big_endian>::do_write( + Output_file* of) +{ + const off_t off = this->offset(); + const off_t oview_size = this->data_size(); + unsigned char* const oview = of->get_output_view(off, oview_size); + + unsigned char* pov = oview; + for (typename Relocs::const_iterator p = this->relocs_.begin(); + p != this->relocs_.end(); + ++p) + { + p->write(pov); + pov += reloc_size; + } + + gold_assert(pov - oview == oview_size); + + of->write_output_view(off, oview_size, oview); + + // We no longer need the relocation entries. + this->relocs_.clear(); +} + +// Output_data_got::Got_entry methods. + +// Write out the entry. + +template<int size, bool big_endian> +void +Output_data_got<size, big_endian>::Got_entry::write( + const General_options* options, + unsigned char* pov) const +{ + Valtype val = 0; + + switch (this->local_sym_index_) + { + case GSYM_CODE: + { + Symbol* gsym = this->u_.gsym; + + // If the symbol is resolved locally, we need to write out its + // value. Otherwise we just write zero. The target code is + // responsible for creating a relocation entry to fill in the + // value at runtime. + if (gsym->final_value_is_known(options)) + { + Sized_symbol<size>* sgsym; + // This cast is a bit ugly. We don't want to put a + // virtual method in Symbol, because we want Symbol to be + // as small as possible. + sgsym = static_cast<Sized_symbol<size>*>(gsym); + val = sgsym->value(); + } + } + break; + + case CONSTANT_CODE: + val = this->u_.constant; + break; + + default: + gold_unreachable(); + } + + elfcpp::Swap<size, big_endian>::writeval(pov, val); +} + +// Output_data_got methods. + +// 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) +{ + if (gsym->has_got_offset()) + return false; + + 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_data_got<size, big_endian>::do_write(Output_file* of) +{ + const int add = size / 8; + + const off_t off = this->offset(); + const off_t oview_size = this->data_size(); + unsigned char* const oview = of->get_output_view(off, oview_size); + + unsigned char* pov = oview; + for (typename Got_entries::const_iterator p = this->entries_.begin(); + p != this->entries_.end(); + ++p) + { + p->write(this->options_, pov); + pov += add; + } + + gold_assert(pov - oview == oview_size); + + of->write_output_view(off, oview_size, oview); + + // We no longer need the GOT entries. + this->entries_.clear(); +} + +// Output_data_dynamic::Dynamic_entry methods. + +// Write out the entry. + +template<int size, bool big_endian> +void +Output_data_dynamic::Dynamic_entry::write( + unsigned char* pov, + const Stringpool* pool + ACCEPT_SIZE_ENDIAN) const +{ + typename elfcpp::Elf_types<size>::Elf_WXword val; + switch (this->classification_) + { + case DYNAMIC_NUMBER: + val = this->u_.val; + break; + + case DYNAMIC_SECTION_ADDRESS: + val = this->u_.od->address(); + break; + + case DYNAMIC_SECTION_SIZE: + val = this->u_.od->data_size(); + break; + + case DYNAMIC_SYMBOL: + { + const Sized_symbol<size>* s = + static_cast<const Sized_symbol<size>*>(this->u_.sym); + val = s->value(); + } + break; + + case DYNAMIC_STRING: + val = pool->get_offset(this->u_.str); + break; + + default: + gold_unreachable(); + } + + elfcpp::Dyn_write<size, big_endian> dw(pov); + dw.put_d_tag(this->tag_); + dw.put_d_val(val); +} + +// Output_data_dynamic methods. + +// Adjust the output section to set the entry size. + +void +Output_data_dynamic::do_adjust_output_section(Output_section* os) +{ + if (this->target_->get_size() == 32) + os->set_entsize(elfcpp::Elf_sizes<32>::dyn_size); + else if (this->target_->get_size() == 64) + os->set_entsize(elfcpp::Elf_sizes<64>::dyn_size); + else + gold_unreachable(); +} + +// Set the final data size. + +void +Output_data_dynamic::do_set_address(uint64_t, off_t) +{ + // Add the terminating entry. + this->add_constant(elfcpp::DT_NULL, 0); + + int dyn_size; + if (this->target_->get_size() == 32) + dyn_size = elfcpp::Elf_sizes<32>::dyn_size; + else if (this->target_->get_size() == 64) + dyn_size = elfcpp::Elf_sizes<64>::dyn_size; + else + gold_unreachable(); + this->set_data_size(this->entries_.size() * dyn_size); +} + +// Write out the dynamic entries. + +void +Output_data_dynamic::do_write(Output_file* of) +{ + if (this->target_->get_size() == 32) + { + if (this->target_->is_big_endian()) + this->sized_write<32, true>(of); + else + this->sized_write<32, false>(of); + } + else if (this->target_->get_size() == 64) + { + if (this->target_->is_big_endian()) + this->sized_write<64, true>(of); + else + this->sized_write<64, false>(of); + } + else + gold_unreachable(); +} + +template<int size, bool big_endian> +void +Output_data_dynamic::sized_write(Output_file* of) +{ + const int dyn_size = elfcpp::Elf_sizes<size>::dyn_size; + + const off_t offset = this->offset(); + const off_t oview_size = this->data_size(); + unsigned char* const oview = of->get_output_view(offset, oview_size); + + unsigned char* pov = oview; + for (typename Dynamic_entries::const_iterator p = this->entries_.begin(); + p != this->entries_.end(); + ++p) + { + p->write SELECT_SIZE_ENDIAN_NAME(size, big_endian)( + pov, this->pool_ SELECT_SIZE_ENDIAN(size, big_endian)); + pov += dyn_size; + } + + gold_assert(pov - oview == oview_size); + + of->write_output_view(offset, oview_size, oview); + + // We no longer need the dynamic entries. + this->entries_.clear(); +} + +// Output_section::Input_section methods. + +// Return the data size. For an input section we store the size here. +// For an Output_section_data, we have to ask it for the size. + +off_t +Output_section::Input_section::data_size() const +{ + if (this->is_input_section()) + return this->u1_.data_size; + else + return this->u2_.posd->data_size(); +} + +// Set the address and file offset. + +void +Output_section::Input_section::set_address(uint64_t addr, off_t off, + off_t secoff) +{ + if (this->is_input_section()) + this->u2_.object->set_section_offset(this->shndx_, off - secoff); + else + this->u2_.posd->set_address(addr, off); +} + +// Try to turn an input address into an output address. + +bool +Output_section::Input_section::output_address(const Relobj* object, + unsigned int shndx, + off_t offset, + uint64_t output_section_address, + uint64_t *poutput) const +{ + if (!this->is_input_section()) + return this->u2_.posd->output_address(object, shndx, offset, + output_section_address, poutput); + else + { + if (this->u2_.object != object) + return false; + off_t output_offset; + Output_section* os = object->output_section(shndx, &output_offset); + gold_assert(os != NULL); + *poutput = output_section_address + output_offset + offset; + return true; + } +} + +// Write out the data. We don't have to do anything for an input +// section--they are handled via Object::relocate--but this is where +// we write out the data for an Output_section_data. + +void +Output_section::Input_section::write(Output_file* of) +{ + if (!this->is_input_section()) + this->u2_.posd->write(of); +} + +// Output_section methods. + +// Construct an Output_section. NAME will point into a Stringpool. + +Output_section::Output_section(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags) + : name_(name), + addralign_(0), + entsize_(0), + link_section_(NULL), + link_(0), + info_section_(NULL), + info_(0), + type_(type), + flags_(flags), + out_shndx_(0), + symtab_index_(0), + dynsym_index_(0), + input_sections_(), + first_input_offset_(0), + needs_symtab_index_(false), + needs_dynsym_index_(false), + should_link_to_symtab_(false), + should_link_to_dynsym_(false) +{ +} + +Output_section::~Output_section() +{ +} + +// Set the entry size. + +void +Output_section::set_entsize(uint64_t v) +{ + if (this->entsize_ == 0) + this->entsize_ = v; + else + gold_assert(this->entsize_ == v); +} + +// Add the input section SHNDX, with header SHDR, named SECNAME, in +// OBJECT, to the Output_section. Return the offset of the input +// section within the output section. We don't always keep track of +// input sections for an Output_section. Instead, each Object keeps +// track of the Output_section for each of its input sections. + +template<int size, bool big_endian> +off_t +Output_section::add_input_section(Relobj* object, unsigned int shndx, + const char* secname, + const elfcpp::Shdr<size, big_endian>& shdr) +{ + elfcpp::Elf_Xword addralign = shdr.get_sh_addralign(); + if ((addralign & (addralign - 1)) != 0) + { + fprintf(stderr, _("%s: %s: invalid alignment %lu for section \"%s\"\n"), + program_name, object->name().c_str(), + static_cast<unsigned long>(addralign), secname); + gold_exit(false); + } + + if (addralign > this->addralign_) + this->addralign_ = addralign; + + // If this is a SHF_MERGE section, we pass all the input sections to + // a Output_data_merge. + if ((shdr.get_sh_flags() & elfcpp::SHF_MERGE) != 0) + { + if (this->add_merge_input_section(object, shndx, shdr.get_sh_flags(), + shdr.get_sh_entsize(), + addralign)) + { + // Tell the relocation routines that they need to call the + // output_address method to determine the final address. + return -1; + } + } + + off_t ssize = this->data_size(); + ssize = align_address(ssize, addralign); + this->set_data_size(ssize + shdr.get_sh_size()); + + // We need to keep track of this section if we are already keeping + // track of sections, or if we are relaxing. FIXME: Add test for + // relaxing. + if (! this->input_sections_.empty()) + this->input_sections_.push_back(Input_section(object, shndx, + shdr.get_sh_size(), + addralign)); + + return ssize; +} + +// Add arbitrary data to an output section. + +void +Output_section::add_output_section_data(Output_section_data* posd) +{ + Input_section inp(posd); + this->add_output_section_data(&inp); +} + +// Add arbitrary data to an output section by Input_section. + +void +Output_section::add_output_section_data(Input_section* inp) +{ + if (this->input_sections_.empty()) + this->first_input_offset_ = this->data_size(); + + this->input_sections_.push_back(*inp); + + uint64_t addralign = inp->addralign(); + if (addralign > this->addralign_) + this->addralign_ = addralign; + + inp->set_output_section(this); +} + +// Add a merge section to an output section. + +void +Output_section::add_output_merge_section(Output_section_data* posd, + bool is_string, uint64_t entsize) +{ + Input_section inp(posd, is_string, entsize); + this->add_output_section_data(&inp); +} + +// Add an input section to a SHF_MERGE section. + +bool +Output_section::add_merge_input_section(Relobj* object, unsigned int shndx, + uint64_t flags, uint64_t entsize, + uint64_t addralign) +{ + // We only merge constants if the alignment is not more than the + // entry size. This could be handled, but it's unusual. + if (addralign > entsize) + return false; + + bool is_string = (flags & elfcpp::SHF_STRINGS) != 0; + Input_section_list::iterator p; + for (p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + if (p->is_merge_section(is_string, entsize)) + break; + + // We handle the actual constant merging in Output_merge_data or + // Output_merge_string_data. + if (p != this->input_sections_.end()) + p->add_input_section(object, shndx); + else + { + Output_section_data* posd; + if (!is_string) + posd = new Output_merge_data(entsize); + else if (entsize == 1) + posd = new Output_merge_string<char>(); + else if (entsize == 2) + posd = new Output_merge_string<uint16_t>(); + else if (entsize == 4) + posd = new Output_merge_string<uint32_t>(); + else + return false; + + this->add_output_merge_section(posd, is_string, entsize); + posd->add_input_section(object, shndx); + } + + return true; +} + +// Return the output virtual address of OFFSET relative to the start +// of input section SHNDX in object OBJECT. + +uint64_t +Output_section::output_address(const Relobj* object, unsigned int shndx, + off_t offset) const +{ + uint64_t addr = this->address() + this->first_input_offset_; + for (Input_section_list::const_iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + { + addr = align_address(addr, p->addralign()); + uint64_t output; + if (p->output_address(object, shndx, offset, addr, &output)) + return output; + addr += p->data_size(); + } + + // If we get here, it means that we don't know the mapping for this + // input section. This might happen in principle if + // add_input_section were called before add_output_section_data. + // But it should never actually happen. + + gold_unreachable(); +} + +// Set the address of an Output_section. This is where we handle +// setting the addresses of any Output_section_data objects. + +void +Output_section::do_set_address(uint64_t address, off_t startoff) +{ + if (this->input_sections_.empty()) + return; + + off_t off = startoff + this->first_input_offset_; + for (Input_section_list::iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + { + off = align_address(off, p->addralign()); + p->set_address(address + (off - startoff), off, startoff); + off += p->data_size(); + } + + this->set_data_size(off - startoff); +} + +// Write the section header to *OSHDR. + +template<int size, bool big_endian> +void +Output_section::write_header(const Layout* layout, + const Stringpool* secnamepool, + elfcpp::Shdr_write<size, big_endian>* oshdr) const +{ + oshdr->put_sh_name(secnamepool->get_offset(this->name_)); + oshdr->put_sh_type(this->type_); + oshdr->put_sh_flags(this->flags_); + oshdr->put_sh_addr(this->address()); + oshdr->put_sh_offset(this->offset()); + oshdr->put_sh_size(this->data_size()); + if (this->link_section_ != NULL) + oshdr->put_sh_link(this->link_section_->out_shndx()); + else if (this->should_link_to_symtab_) + oshdr->put_sh_link(layout->symtab_section()->out_shndx()); + else if (this->should_link_to_dynsym_) + oshdr->put_sh_link(layout->dynsym_section()->out_shndx()); + else + oshdr->put_sh_link(this->link_); + if (this->info_section_ != NULL) + oshdr->put_sh_info(this->info_section_->out_shndx()); + else + oshdr->put_sh_info(this->info_); + oshdr->put_sh_addralign(this->addralign_); + oshdr->put_sh_entsize(this->entsize_); +} + +// Write out the data. For input sections the data is written out by +// Object::relocate, but we have to handle Output_section_data objects +// here. + +void +Output_section::do_write(Output_file* of) +{ + for (Input_section_list::iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + p->write(of); +} + +// Output segment methods. + +Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags) + : output_data_(), + output_bss_(), + vaddr_(0), + paddr_(0), + memsz_(0), + align_(0), + offset_(0), + filesz_(0), + type_(type), + flags_(flags), + is_align_known_(false) +{ +} + +// Add an Output_section to an Output_segment. + +void +Output_segment::add_output_section(Output_section* os, + elfcpp::Elf_Word seg_flags, + bool front) +{ + gold_assert((os->flags() & elfcpp::SHF_ALLOC) != 0); + gold_assert(!this->is_align_known_); + + // Update the segment flags. + this->flags_ |= seg_flags; + + Output_segment::Output_data_list* pdl; + if (os->type() == elfcpp::SHT_NOBITS) + pdl = &this->output_bss_; + else + pdl = &this->output_data_; + + // So that PT_NOTE segments will work correctly, we need to ensure + // that all SHT_NOTE sections are adjacent. This will normally + // happen automatically, because all the SHT_NOTE input sections + // will wind up in the same output section. However, it is possible + // for multiple SHT_NOTE input sections to have different section + // flags, and thus be in different output sections, but for the + // different section flags to map into the same segment flags and + // thus the same output segment. + + // Note that while there may be many input sections in an output + // section, there are normally only a few output sections in an + // output segment. This loop is expected to be fast. + + if (os->type() == elfcpp::SHT_NOTE && !pdl->empty()) + { + Output_segment::Output_data_list::iterator p = pdl->end(); + do + { + --p; + if ((*p)->is_section_type(elfcpp::SHT_NOTE)) + { + // We don't worry about the FRONT parameter. + ++p; + pdl->insert(p, os); + return; + } + } + while (p != pdl->begin()); + } + + // Similarly, so that PT_TLS segments will work, we need to group + // SHF_TLS sections. An SHF_TLS/SHT_NOBITS section is a special + // case: we group the SHF_TLS/SHT_NOBITS sections right after the + // SHF_TLS/SHT_PROGBITS sections. This lets us set up PT_TLS + // correctly. + if ((os->flags() & elfcpp::SHF_TLS) != 0 && !this->output_data_.empty()) + { + pdl = &this->output_data_; + bool nobits = os->type() == elfcpp::SHT_NOBITS; + bool sawtls = false; + Output_segment::Output_data_list::iterator p = pdl->end(); + do + { + --p; + bool insert; + if ((*p)->is_section_flag_set(elfcpp::SHF_TLS)) + { + sawtls = true; + // Put a NOBITS section after the first TLS section. + // But a PROGBITS section after the first TLS/PROGBITS + // section. + insert = nobits || !(*p)->is_section_type(elfcpp::SHT_NOBITS); + } + else + { + // If we've gone past the TLS sections, but we've seen a + // TLS section, then we need to insert this section now. + insert = sawtls; + } + + if (insert) + { + // We don't worry about the FRONT parameter. + ++p; + pdl->insert(p, os); + return; + } + } + while (p != pdl->begin()); + + // There are no TLS sections yet; put this one at the requested + // location in the section list. + } + + 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 +// a segment. + +void +Output_segment::add_initial_output_data(Output_data* od) +{ + gold_assert(!this->is_align_known_); + this->output_data_.push_front(od); +} + +// Return the maximum alignment of the Output_data in Output_segment. +// Once we compute this, we prohibit new sections from being added. + +uint64_t +Output_segment::addralign() +{ + if (!this->is_align_known_) + { + uint64_t addralign; + + addralign = Output_segment::maximum_alignment(&this->output_data_); + if (addralign > this->align_) + this->align_ = addralign; + + addralign = Output_segment::maximum_alignment(&this->output_bss_); + if (addralign > this->align_) + this->align_ = addralign; + + this->is_align_known_ = true; + } + + return this->align_; +} + +// Return the maximum alignment of a list of Output_data. + +uint64_t +Output_segment::maximum_alignment(const Output_data_list* pdl) +{ + uint64_t ret = 0; + for (Output_data_list::const_iterator p = pdl->begin(); + p != pdl->end(); + ++p) + { + uint64_t addralign = (*p)->addralign(); + if (addralign > ret) + ret = addralign; + } + return ret; +} + +// Set the section addresses for an Output_segment. ADDR is the +// address and *POFF is the file offset. Set the section indexes +// starting with *PSHNDX. Return the address of the immediately +// following segment. Update *POFF and *PSHNDX. + +uint64_t +Output_segment::set_section_addresses(uint64_t addr, off_t* poff, + unsigned int* pshndx) +{ + gold_assert(this->type_ == elfcpp::PT_LOAD); + + this->vaddr_ = addr; + this->paddr_ = addr; + + off_t orig_off = *poff; + this->offset_ = orig_off; + + *poff = align_address(*poff, this->addralign()); + + addr = this->set_section_list_addresses(&this->output_data_, addr, poff, + pshndx); + this->filesz_ = *poff - orig_off; + + off_t off = *poff; + + uint64_t ret = this->set_section_list_addresses(&this->output_bss_, addr, + poff, pshndx); + this->memsz_ = *poff - orig_off; + + // Ignore the file offset adjustments made by the BSS Output_data + // objects. + *poff = off; + + return ret; +} + +// Set the addresses and file offsets in a list of Output_data +// structures. + +uint64_t +Output_segment::set_section_list_addresses(Output_data_list* pdl, + uint64_t addr, off_t* poff, + unsigned int* pshndx) +{ + off_t startoff = *poff; + + off_t off = startoff; + for (Output_data_list::iterator p = pdl->begin(); + p != pdl->end(); + ++p) + { + off = align_address(off, (*p)->addralign()); + (*p)->set_address(addr + (off - startoff), off); + + // Unless this is a PT_TLS segment, we want to ignore the size + // of a SHF_TLS/SHT_NOBITS section. Such a section does not + // affect the size of a PT_LOAD segment. + if (this->type_ == elfcpp::PT_TLS + || !(*p)->is_section_flag_set(elfcpp::SHF_TLS) + || !(*p)->is_section_type(elfcpp::SHT_NOBITS)) + off += (*p)->data_size(); + + if ((*p)->is_section()) + { + (*p)->set_out_shndx(*pshndx); + ++*pshndx; + } + } + + *poff = off; + return addr + (off - startoff); +} + +// For a non-PT_LOAD segment, set the offset from the sections, if +// any. + +void +Output_segment::set_offset() +{ + gold_assert(this->type_ != elfcpp::PT_LOAD); + + if (this->output_data_.empty() && this->output_bss_.empty()) + { + this->vaddr_ = 0; + this->paddr_ = 0; + this->memsz_ = 0; + this->align_ = 0; + this->offset_ = 0; + this->filesz_ = 0; + return; + } + + const Output_data* first; + if (this->output_data_.empty()) + first = this->output_bss_.front(); + else + first = this->output_data_.front(); + this->vaddr_ = first->address(); + this->paddr_ = this->vaddr_; + this->offset_ = first->offset(); + + if (this->output_data_.empty()) + this->filesz_ = 0; + else + { + const Output_data* last_data = this->output_data_.back(); + this->filesz_ = (last_data->address() + + last_data->data_size() + - this->vaddr_); + } + + const Output_data* last; + if (this->output_bss_.empty()) + last = this->output_data_.back(); + else + last = this->output_bss_.back(); + this->memsz_ = (last->address() + + last->data_size() + - this->vaddr_); +} + +// Return the number of Output_sections in an Output_segment. + +unsigned int +Output_segment::output_section_count() const +{ + return (this->output_section_count_list(&this->output_data_) + + this->output_section_count_list(&this->output_bss_)); +} + +// Return the number of Output_sections in an Output_data_list. + +unsigned int +Output_segment::output_section_count_list(const Output_data_list* pdl) const +{ + unsigned int count = 0; + for (Output_data_list::const_iterator p = pdl->begin(); + p != pdl->end(); + ++p) + { + if ((*p)->is_section()) + ++count; + } + return count; +} + +// Write the segment data into *OPHDR. + +template<int size, bool big_endian> +void +Output_segment::write_header(elfcpp::Phdr_write<size, big_endian>* ophdr) +{ + ophdr->put_p_type(this->type_); + ophdr->put_p_offset(this->offset_); + ophdr->put_p_vaddr(this->vaddr_); + ophdr->put_p_paddr(this->paddr_); + ophdr->put_p_filesz(this->filesz_); + ophdr->put_p_memsz(this->memsz_); + ophdr->put_p_flags(this->flags_); + ophdr->put_p_align(this->addralign()); +} + +// Write the section headers into V. + +template<int size, bool big_endian> +unsigned char* +Output_segment::write_section_headers(const Layout* layout, + const Stringpool* secnamepool, + unsigned char* v, + unsigned int *pshndx + ACCEPT_SIZE_ENDIAN) const +{ + // Every section that is attached to a segment must be attached to a + // PT_LOAD segment, so we only write out section headers for PT_LOAD + // segments. + if (this->type_ != elfcpp::PT_LOAD) + return v; + + v = this->write_section_headers_list + SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + layout, secnamepool, &this->output_data_, v, pshndx + SELECT_SIZE_ENDIAN(size, big_endian)); + v = this->write_section_headers_list + SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + layout, secnamepool, &this->output_bss_, v, pshndx + SELECT_SIZE_ENDIAN(size, big_endian)); + return v; +} + +template<int size, bool big_endian> +unsigned char* +Output_segment::write_section_headers_list(const Layout* layout, + const Stringpool* secnamepool, + const Output_data_list* pdl, + unsigned char* v, + unsigned int* pshndx + ACCEPT_SIZE_ENDIAN) const +{ + const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size; + for (Output_data_list::const_iterator p = pdl->begin(); + p != pdl->end(); + ++p) + { + if ((*p)->is_section()) + { + const Output_section* ps = static_cast<const Output_section*>(*p); + gold_assert(*pshndx == ps->out_shndx()); + elfcpp::Shdr_write<size, big_endian> oshdr(v); + ps->write_header(layout, secnamepool, &oshdr); + v += shdr_size; + ++*pshndx; + } + } + return v; +} + +// Output_file methods. + +Output_file::Output_file(const General_options& options) + : options_(options), + name_(options.output_file_name()), + o_(-1), + file_size_(0), + base_(NULL) +{ +} + +// Open the output file. + +void +Output_file::open(off_t file_size) +{ + this->file_size_ = file_size; + + int mode = this->options_.is_relocatable() ? 0666 : 0777; + int o = ::open(this->name_, O_RDWR | O_CREAT | O_TRUNC, mode); + if (o < 0) + { + fprintf(stderr, _("%s: %s: open: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + this->o_ = o; + + // Write out one byte to make the file the right size. + if (::lseek(o, file_size - 1, SEEK_SET) < 0) + { + fprintf(stderr, _("%s: %s: lseek: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + char b = 0; + if (::write(o, &b, 1) != 1) + { + fprintf(stderr, _("%s: %s: write: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + + // Map the file into memory. + void* base = ::mmap(NULL, file_size, PROT_READ | PROT_WRITE, + MAP_SHARED, o, 0); + if (base == MAP_FAILED) + { + fprintf(stderr, _("%s: %s: mmap: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + this->base_ = static_cast<unsigned char*>(base); +} + +// Close the output file. + +void +Output_file::close() +{ + if (::munmap(this->base_, this->file_size_) < 0) + { + fprintf(stderr, _("%s: %s: munmap: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + this->base_ = NULL; + + if (::close(this->o_) < 0) + { + fprintf(stderr, _("%s: %s: close: %s\n"), + program_name, this->name_, strerror(errno)); + gold_exit(false); + } + this->o_ = -1; +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones for implemented targets. + +template +off_t +Output_section::add_input_section<32, false>( + Relobj* object, + unsigned int shndx, + const char* secname, + const elfcpp::Shdr<32, false>& shdr); + +template +off_t +Output_section::add_input_section<32, true>( + Relobj* object, + unsigned int shndx, + const char* secname, + const elfcpp::Shdr<32, true>& shdr); + +template +off_t +Output_section::add_input_section<64, false>( + Relobj* object, + unsigned int shndx, + const char* secname, + const elfcpp::Shdr<64, false>& shdr); + +template +off_t +Output_section::add_input_section<64, true>( + Relobj* object, + unsigned int shndx, + const char* secname, + const elfcpp::Shdr<64, true>& shdr); + +template +class Output_data_reloc<elfcpp::SHT_REL, false, 32, false>; + +template +class Output_data_reloc<elfcpp::SHT_REL, false, 32, true>; + +template +class Output_data_reloc<elfcpp::SHT_REL, false, 64, false>; + +template +class Output_data_reloc<elfcpp::SHT_REL, false, 64, true>; + +template +class Output_data_reloc<elfcpp::SHT_REL, true, 32, false>; + +template +class Output_data_reloc<elfcpp::SHT_REL, true, 32, true>; + +template +class Output_data_reloc<elfcpp::SHT_REL, true, 64, false>; + +template +class Output_data_reloc<elfcpp::SHT_REL, true, 64, true>; + +template +class Output_data_reloc<elfcpp::SHT_RELA, false, 32, false>; + +template +class Output_data_reloc<elfcpp::SHT_RELA, false, 32, true>; + +template +class Output_data_reloc<elfcpp::SHT_RELA, false, 64, false>; + +template +class Output_data_reloc<elfcpp::SHT_RELA, false, 64, true>; + +template +class Output_data_reloc<elfcpp::SHT_RELA, true, 32, false>; + +template +class Output_data_reloc<elfcpp::SHT_RELA, true, 32, true>; + +template +class Output_data_reloc<elfcpp::SHT_RELA, true, 64, false>; + +template +class Output_data_reloc<elfcpp::SHT_RELA, true, 64, true>; + +template +class Output_data_got<32, false>; + +template +class Output_data_got<32, true>; + +template +class Output_data_got<64, false>; + +template +class Output_data_got<64, true>; + +} // End namespace gold. + diff --git a/gold/output.h b/gold/output.h new file mode 100644 index 000000000000..7ed53ff06d77 --- /dev/null +++ b/gold/output.h @@ -0,0 +1,1801 @@ +// output.h -- manage the output file for gold -*- C++ -*- + +#ifndef GOLD_OUTPUT_H +#define GOLD_OUTPUT_H + +#include <list> +#include <vector> + +#include "elfcpp.h" +#include "layout.h" +#include "reloc-types.h" + +namespace gold +{ + +class General_options; +class Object; +class Symbol; +class Output_file; +class Output_section; +class Target; +template<int size, bool big_endian> +class Sized_target; +template<int size, bool big_endian> +class Sized_relobj; + +// An abtract class for data which has to go into the output file. + +class Output_data +{ + public: + explicit Output_data(off_t data_size = 0) + : address_(0), data_size_(data_size), offset_(-1) + { } + + virtual + ~Output_data(); + + // Return the address. This is only valid after Layout::finalize is + // finished. + uint64_t + address() const + { return this->address_; } + + // Return the size of the data. This must be valid after + // Layout::finalize calls set_address, but need not be valid before + // then. + off_t + data_size() const + { return this->data_size_; } + + // Return the file offset. This is only valid after + // Layout::finalize is finished. + off_t + offset() const + { return this->offset_; } + + // Return the required alignment. + uint64_t + addralign() const + { return this->do_addralign(); } + + // Return whether this is an Output_section. + bool + is_section() const + { return this->do_is_section(); } + + // Return whether this is an Output_section of the specified type. + bool + is_section_type(elfcpp::Elf_Word stt) const + { return this->do_is_section_type(stt); } + + // Return whether this is an Output_section with the specified flag + // set. + bool + is_section_flag_set(elfcpp::Elf_Xword shf) const + { return this->do_is_section_flag_set(shf); } + + // Return the output section index, if there is an output section. + unsigned int + out_shndx() const + { return this->do_out_shndx(); } + + // Set the output section index, if this is an output section. + void + set_out_shndx(unsigned int shndx) + { this->do_set_out_shndx(shndx); } + + // Set the address and file offset of this data. This is called + // during Layout::finalize. + void + set_address(uint64_t addr, off_t off); + + // Write the data to the output file. This is called after + // Layout::finalize is complete. + void + write(Output_file* file) + { this->do_write(file); } + + // This is called by Layout::finalize to note that all sizes must + // now be fixed. + static void + layout_complete() + { Output_data::sizes_are_fixed = true; } + + protected: + // Functions that child classes may or in some cases must implement. + + // Write the data to the output file. + virtual void + do_write(Output_file*) = 0; + + // Return the required alignment. + virtual uint64_t + do_addralign() const = 0; + + // Return whether this is an Output_section. + virtual bool + do_is_section() const + { return false; } + + // Return whether this is an Output_section of the specified type. + // This only needs to be implement by Output_section. + virtual bool + do_is_section_type(elfcpp::Elf_Word) const + { return false; } + + // Return whether this is an Output_section with the specific flag + // set. This only needs to be implemented by Output_section. + virtual bool + do_is_section_flag_set(elfcpp::Elf_Xword) const + { return false; } + + // Return the output section index, if there is an output section. + virtual unsigned int + do_out_shndx() const + { gold_unreachable(); } + + // Set the output section index, if this is an output section. + virtual void + do_set_out_shndx(unsigned int) + { gold_unreachable(); } + + // Set the address and file offset of the data. This only needs to + // be implemented if the child needs to know. The child class can + // set its size in this call. + virtual void + do_set_address(uint64_t, off_t) + { } + + // Functions that child classes may call. + + // Set the size of the data. + void + set_data_size(off_t data_size) + { + gold_assert(!Output_data::sizes_are_fixed); + this->data_size_ = data_size; + } + + // Return default alignment for a size--32 or 64. + static uint64_t + default_alignment(int size); + + private: + Output_data(const Output_data&); + Output_data& operator=(const Output_data&); + + // This is used for verification, to make sure that we don't try to + // change any sizes after we set the section addresses. + static bool sizes_are_fixed; + + // Memory address in file (not always meaningful). + uint64_t address_; + // Size of data in file. + off_t data_size_; + // Offset within file. + off_t offset_; +}; + +// Output the section headers. + +class Output_section_headers : public Output_data +{ + public: + Output_section_headers(int size, + bool big_endian, + const Layout*, + const Layout::Segment_list*, + const Layout::Section_list*, + const Stringpool*); + + // Write the data to the file. + void + do_write(Output_file*); + + // Return the required alignment. + uint64_t + do_addralign() const + { return Output_data::default_alignment(this->size_); } + + private: + // Write the data to the file with the right size and endianness. + template<int size, bool big_endian> + void + do_sized_write(Output_file*); + + int size_; + bool big_endian_; + const Layout* layout_; + const Layout::Segment_list* segment_list_; + const Layout::Section_list* unattached_section_list_; + const Stringpool* secnamepool_; +}; + +// Output the segment headers. + +class Output_segment_headers : public Output_data +{ + public: + Output_segment_headers(int size, bool big_endian, + const Layout::Segment_list& segment_list); + + // Write the data to the file. + void + do_write(Output_file*); + + // Return the required alignment. + uint64_t + do_addralign() const + { return Output_data::default_alignment(this->size_); } + + private: + // Write the data to the file with the right size and endianness. + template<int size, bool big_endian> + void + do_sized_write(Output_file*); + + int size_; + bool big_endian_; + const Layout::Segment_list& segment_list_; +}; + +// Output the ELF file header. + +class Output_file_header : public Output_data +{ + public: + Output_file_header(int size, + bool big_endian, + const General_options&, + const Target*, + const Symbol_table*, + const Output_segment_headers*); + + // Add information about the section headers. We lay out the ELF + // file header before we create the section headers. + void set_section_info(const Output_section_headers*, + const Output_section* shstrtab); + + // Write the data to the file. + void + do_write(Output_file*); + + // Return the required alignment. + uint64_t + do_addralign() const + { return Output_data::default_alignment(this->size_); } + + // Set the address and offset--we only implement this for error + // checking. + void + do_set_address(uint64_t, off_t off) const + { gold_assert(off == 0); } + + private: + // Write the data to the file with the right size and endianness. + template<int size, bool big_endian> + void + do_sized_write(Output_file*); + + int size_; + bool big_endian_; + const General_options& options_; + const Target* target_; + const Symbol_table* symtab_; + const Output_segment_headers* segment_header_; + const Output_section_headers* section_header_; + const Output_section* shstrtab_; +}; + +// Output sections are mainly comprised of input sections. However, +// there are cases where we have data to write out which is not in an +// input section. Output_section_data is used in such cases. This is +// an abstract base class. + +class Output_section_data : public Output_data +{ + public: + Output_section_data(off_t data_size, uint64_t addralign) + : Output_data(data_size), output_section_(NULL), addralign_(addralign) + { } + + Output_section_data(uint64_t addralign) + : Output_data(0), output_section_(NULL), addralign_(addralign) + { } + + // Return the output section. + const Output_section* + output_section() const + { return this->output_section_; } + + // Record the output section. + void + set_output_section(Output_section* os); + + // Add an input section, for SHF_MERGE sections. This returns true + // if the section was handled. + bool + add_input_section(Relobj* object, unsigned int shndx) + { return this->do_add_input_section(object, shndx); } + + // Given an input OBJECT, an input section index SHNDX within that + // object, and an OFFSET relative to the start of that input + // section, return whether or not the output address is known. + // OUTPUT_SECTION_ADDRESS is the address of the output section which + // this is a part of. If this function returns true, it sets + // *POUTPUT to the output address. + virtual bool + output_address(const Relobj* object, unsigned int shndx, off_t offset, + uint64_t output_section_address, uint64_t *poutput) const + { + return this->do_output_address(object, shndx, offset, + output_section_address, poutput); + } + + protected: + // The child class must implement do_write. + + // The child class may implement specific adjustments to the output + // section. + virtual void + do_adjust_output_section(Output_section*) + { } + + // May be implemented by child class. Return true if the section + // was handled. + virtual bool + do_add_input_section(Relobj*, unsigned int) + { gold_unreachable(); } + + // The child class may implement output_address. + virtual bool + do_output_address(const Relobj*, unsigned int, off_t, uint64_t, + uint64_t*) const + { return false; } + + // Return the required alignment. + uint64_t + do_addralign() const + { return this->addralign_; } + + // Return the section index of the output section. + unsigned int + do_out_shndx() const; + + // Set the alignment. + void + set_addralign(uint64_t addralign) + { this->addralign_ = addralign; } + + private: + // The output section for this section. + const Output_section* output_section_; + // The required alignment. + uint64_t addralign_; +}; + +// A simple case of Output_data in which we have constant data to +// output. + +class Output_data_const : public Output_section_data +{ + public: + 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) + { } + + // Add more data. + void + add_data(const std::string& add) + { + this->data_.append(add); + this->set_data_size(this->data_.size()); + } + + // Write the data to the output file. + void + do_write(Output_file*); + + private: + std::string data_; +}; + +// Another version of Output_data with constant data, in which the +// buffer is allocated by the caller. + +class Output_data_const_buffer : public Output_section_data +{ + public: + Output_data_const_buffer(const unsigned char* p, off_t len, + uint64_t addralign) + : Output_section_data(len, addralign), p_(p) + { } + + // Write the data the output file. + void + do_write(Output_file*); + + private: + const unsigned char* p_; +}; + +// A place holder for data written out via some other mechanism. + +class Output_data_space : public Output_section_data +{ + public: + Output_data_space(off_t data_size, uint64_t addralign) + : Output_section_data(data_size, addralign) + { } + + explicit Output_data_space(uint64_t addralign) + : Output_section_data(addralign) + { } + + // Set the size. + void + set_space_size(off_t space_size) + { this->set_data_size(space_size); } + + // Set the alignment. + void + set_space_alignment(uint64_t align) + { this->set_addralign(align); } + + // Write out the data--this must be handled elsewhere. + void + do_write(Output_file*) + { } +}; + +// A string table which goes into an output section. + +class Output_data_strtab : public Output_section_data +{ + public: + Output_data_strtab(Stringpool* strtab) + : Output_section_data(1), strtab_(strtab) + { } + + // This is called to set the address and file offset. Here we make + // sure that the Stringpool is finalized. + void + do_set_address(uint64_t, off_t); + + // Write out the data. + void + do_write(Output_file*); + + private: + Stringpool* strtab_; +}; + +// This POD class is used to represent a single reloc in the output +// file. This could be a private class within Output_data_reloc, but +// the templatization is complex enough that I broke it out into a +// separate class. The class is templatized on either elfcpp::SHT_REL +// or elfcpp::SHT_RELA, and also on whether this is a dynamic +// relocation or an ordinary relocation. + +// A relocation can be against a global symbol, a local symbol, an +// output section, or the undefined symbol at index 0. We represent +// the latter by using a NULL global symbol. + +template<int sh_type, bool dynamic, int size, bool big_endian> +class Output_reloc; + +template<bool dynamic, int size, bool big_endian> +class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> +{ + public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + + // An uninitialized entry. We need this because we want to put + // instances of this class into an STL container. + Output_reloc() + : local_sym_index_(INVALID_CODE) + { } + + // A reloc against a global symbol. + + Output_reloc(Symbol* gsym, unsigned int type, Output_data* od, + Address address) + : address_(address), local_sym_index_(GSYM_CODE), type_(type), + shndx_(INVALID_CODE) + { + this->u1_.gsym = gsym; + this->u2_.od = od; + } + + Output_reloc(Symbol* gsym, unsigned int type, Relobj* relobj, + unsigned int shndx, Address address) + : address_(address), local_sym_index_(GSYM_CODE), type_(type), + shndx_(shndx) + { + gold_assert(shndx != INVALID_CODE); + this->u1_.gsym = gsym; + this->u2_.relobj = relobj; + } + + // A reloc against a local symbol. + + Output_reloc(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, + unsigned int type, + Output_data* od, + Address address) + : address_(address), local_sym_index_(local_sym_index), type_(type), + shndx_(INVALID_CODE) + { + gold_assert(local_sym_index != GSYM_CODE + && local_sym_index != INVALID_CODE); + this->u1_.relobj = relobj; + this->u2_.od = od; + } + + Output_reloc(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, + unsigned int type, + unsigned int shndx, + Address address) + : address_(address), local_sym_index_(local_sym_index), type_(type), + shndx_(shndx) + { + gold_assert(local_sym_index != GSYM_CODE + && local_sym_index != INVALID_CODE); + gold_assert(shndx != INVALID_CODE); + this->u1_.relobj = relobj; + this->u2_.relobj = relobj; + } + + // A reloc against the STT_SECTION symbol of an output section. + + Output_reloc(Output_section* os, unsigned int type, Output_data* od, + Address address) + : address_(address), local_sym_index_(SECTION_CODE), type_(type), + shndx_(INVALID_CODE) + { + this->u1_.os = os; + this->u2_.od = od; + } + + Output_reloc(Output_section* os, unsigned int type, Relobj* relobj, + unsigned int shndx, Address address) + : address_(address), local_sym_index_(SECTION_CODE), type_(type), + shndx_(shndx) + { + gold_assert(shndx != INVALID_CODE); + this->u1_.os = os; + this->u2_.relobj = relobj; + } + + // Write the reloc entry to an output view. + void + write(unsigned char* pov) const; + + // Write the offset and info fields to Write_rel. + template<typename Write_rel> + void write_rel(Write_rel*) const; + + private: + // Return the symbol index. We can't do a double template + // specialization, so we do a secondary template here. + unsigned int + get_symbol_index() const; + + // Codes for local_sym_index_. + enum + { + // Global symbol. + GSYM_CODE = -1U, + // Output section. + SECTION_CODE = -2U, + // Invalid uninitialized entry. + INVALID_CODE = -3U + }; + + union + { + // For a local symbol, the object. We will never generate a + // relocation against a local symbol in a dynamic object; that + // doesn't make sense. And our callers will always be + // templatized, so we use Sized_relobj here. + Sized_relobj<size, big_endian>* relobj; + // For a global symbol, the symbol. If this is NULL, it indicates + // a relocation against the undefined 0 symbol. + Symbol* gsym; + // For a relocation against an output section, the output section. + Output_section* os; + } u1_; + union + { + // If shndx_ is not INVALID CODE, the object which holds the input + // section being used to specify the reloc address. + Relobj* relobj; + // If shndx_ is INVALID_CODE, the output data being used to + // specify the reloc address. This may be NULL if the reloc + // address is absolute. + Output_data* od; + } u2_; + // The address offset within the input section or the Output_data. + Address address_; + // For a local symbol, the local symbol index. This is GSYM_CODE + // for a global symbol, or INVALID_CODE for an uninitialized value. + unsigned int local_sym_index_; + // The reloc type--a processor specific code. + unsigned int type_; + // If the reloc address is an input section in an object, the + // section index. This is INVALID_CODE if the reloc address is + // specified in some other way. + unsigned int shndx_; +}; + +// The SHT_RELA version of Output_reloc<>. This is just derived from +// the SHT_REL version of Output_reloc, but it adds an addend. + +template<bool dynamic, int size, bool big_endian> +class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> +{ + public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + typedef typename elfcpp::Elf_types<size>::Elf_Addr Addend; + + // An uninitialized entry. + Output_reloc() + : rel_() + { } + + // A reloc against a global symbol. + + Output_reloc(Symbol* gsym, unsigned int type, Output_data* od, + Address address, Addend addend) + : rel_(gsym, type, od, address), addend_(addend) + { } + + Output_reloc(Symbol* gsym, unsigned int type, Relobj* relobj, + unsigned int shndx, Address address, Addend addend) + : rel_(gsym, type, relobj, shndx, address), addend_(addend) + { } + + // A reloc against a local symbol. + + Output_reloc(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, + unsigned int type, Output_data* od, Address address, + Addend addend) + : rel_(relobj, local_sym_index, type, od, address), addend_(addend) + { } + + Output_reloc(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, + unsigned int type, + unsigned int shndx, + Address address, + Addend addend) + : rel_(relobj, local_sym_index, type, shndx, address), + addend_(addend) + { } + + // A reloc against the STT_SECTION symbol of an output section. + + Output_reloc(Output_section* os, unsigned int type, Output_data* od, + Address address, Addend addend) + : rel_(os, type, od, address), addend_(addend) + { } + + Output_reloc(Output_section* os, unsigned int type, Relobj* relobj, + unsigned int shndx, Address address, Addend addend) + : rel_(os, type, relobj, shndx, address), addend_(addend) + { } + + // Write the reloc entry to an output view. + void + write(unsigned char* pov) const; + + private: + // The basic reloc. + Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> rel_; + // The addend. + Addend addend_; +}; + +// Output_data_reloc is used to manage a section containing relocs. +// SH_TYPE is either elfcpp::SHT_REL or elfcpp::SHT_RELA. DYNAMIC +// indicates whether this is a dynamic relocation or a normal +// relocation. Output_data_reloc_base is a base class. +// Output_data_reloc is the real class, which we specialize based on +// the reloc type. + +template<int sh_type, bool dynamic, int size, bool big_endian> +class Output_data_reloc_base : public Output_section_data +{ + public: + typedef Output_reloc<sh_type, dynamic, size, big_endian> Output_reloc_type; + typedef typename Output_reloc_type::Address Address; + static const int reloc_size = + Reloc_types<sh_type, size, big_endian>::reloc_size; + + // Construct the section. + Output_data_reloc_base() + : Output_section_data(Output_data::default_alignment(size)) + { } + + // Write out the data. + void + do_write(Output_file*); + + protected: + // Set the entry size and the link. + void + do_adjust_output_section(Output_section *os); + + // Add a relocation entry. + void + add(const Output_reloc_type& reloc) + { + this->relocs_.push_back(reloc); + this->set_data_size(this->relocs_.size() * reloc_size); + } + + private: + typedef std::vector<Output_reloc_type> Relocs; + + Relocs relocs_; +}; + +// The class which callers actually create. + +template<int sh_type, bool dynamic, int size, bool big_endian> +class Output_data_reloc; + +// The SHT_REL version of Output_data_reloc. + +template<bool dynamic, int size, bool big_endian> +class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> + : public Output_data_reloc_base<elfcpp::SHT_REL, dynamic, size, big_endian> +{ + private: + typedef Output_data_reloc_base<elfcpp::SHT_REL, dynamic, size, + big_endian> Base; + + public: + typedef typename Base::Output_reloc_type Output_reloc_type; + typedef typename Output_reloc_type::Address Address; + + Output_data_reloc() + : Output_data_reloc_base<elfcpp::SHT_REL, dynamic, size, big_endian>() + { } + + // Add a reloc against a global symbol. + + void + add_global(Symbol* gsym, unsigned int type, Output_data* od, Address address) + { this->add(Output_reloc_type(gsym, type, od, address)); } + + void + add_global(Symbol* gsym, unsigned int type, Relobj* relobj, + unsigned int shndx, Address address) + { this->add(Output_reloc_type(gsym, type, relobj, shndx, address)); } + + // Add a reloc against a local symbol. + + void + add_local(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + Output_data* od, Address address) + { this->add(Output_reloc_type(relobj, local_sym_index, type, od, address)); } + + void + add_local(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + unsigned int shndx, Address address) + { this->add(Output_reloc_type(relobj, local_sym_index, type, shndx, + address)); } + + + // A reloc against the STT_SECTION symbol of an output section. + + void + add_output_section(Output_section* os, unsigned int type, + Output_data* od, Address address) + { this->add(Output_reloc_type(os, type, od, address)); } + + void + add_output_section(Output_section* os, unsigned int type, + Relobj* relobj, unsigned int shndx, Address address) + { this->add(Output_reloc_type(os, type, relobj, shndx, address)); } +}; + +// The SHT_RELA version of Output_data_reloc. + +template<bool dynamic, int size, bool big_endian> +class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> + : public Output_data_reloc_base<elfcpp::SHT_RELA, dynamic, size, big_endian> +{ + private: + typedef Output_data_reloc_base<elfcpp::SHT_RELA, dynamic, size, + big_endian> Base; + + public: + typedef typename Base::Output_reloc_type Output_reloc_type; + typedef typename Output_reloc_type::Address Address; + typedef typename Output_reloc_type::Addend Addend; + + Output_data_reloc() + : Output_data_reloc_base<elfcpp::SHT_RELA, dynamic, size, big_endian>() + { } + + // Add a reloc against a global symbol. + + void + add_global(Symbol* gsym, unsigned int type, Output_data* od, + Address address, Addend addend) + { this->add(Output_reloc_type(gsym, type, od, address, addend)); } + + void + add_global(Symbol* gsym, unsigned int type, Relobj* relobj, + unsigned int shndx, Address address, Addend addend) + { this->add(Output_reloc_type(gsym, type, relobj, shndx, address, addend)); } + + // Add a reloc against a local symbol. + + void + add_local(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + Output_data* od, Address address, Addend addend) + { + this->add(Output_reloc_type(relobj, local_sym_index, type, od, address, + addend)); + } + + void + add_local(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + unsigned int shndx, Address address, Addend addend) + { + this->add(Output_reloc_type(relobj, local_sym_index, type, shndx, address, + addend)); + } + + // A reloc against the STT_SECTION symbol of an output section. + + void + add_output_section(Output_section* os, unsigned int type, Output_data* od, + Address address, Addend addend) + { this->add(Output_reloc_type(os, type, od, address, addend)); } + + void + add_output_section(Output_section* os, unsigned int type, Relobj* relobj, + unsigned int shndx, Address address, Addend addend) + { this->add(Output_reloc_type(os, type, relobj, shndx, address, addend)); } +}; + +// 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. + +template<int size, bool big_endian> +class Output_data_got : public Output_section_data +{ + public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Valtype; + + Output_data_got(const General_options* options) + : Output_section_data(Output_data::default_alignment(size)), + options_(options), entries_() + { } + + // 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. + unsigned int + add_local(Object* object, unsigned int sym_index) + { + this->entries_.push_back(Got_entry(object, sym_index)); + this->set_got_size(); + return this->last_got_offset(); + } + + // Add a constant to the GOT. This returns the offset of the new + // entry from the start of the GOT. + unsigned int + add_constant(Valtype constant) + { + this->entries_.push_back(Got_entry(constant)); + this->set_got_size(); + return this->last_got_offset(); + } + + // Write out the GOT table. + void + do_write(Output_file*); + + private: + // This POD class holds a single GOT entry. + class Got_entry + { + public: + // Create a zero entry. + Got_entry() + : local_sym_index_(CONSTANT_CODE) + { this->u_.constant = 0; } + + // Create a global symbol entry. + explicit Got_entry(Symbol* gsym) + : local_sym_index_(GSYM_CODE) + { this->u_.gsym = gsym; } + + // Create a local symbol entry. + Got_entry(Object* object, unsigned int local_sym_index) + : local_sym_index_(local_sym_index) + { + gold_assert(local_sym_index != GSYM_CODE + && local_sym_index != CONSTANT_CODE); + this->u_.object = object; + } + + // Create a constant entry. The constant is a host value--it will + // be swapped, if necessary, when it is written out. + explicit Got_entry(Valtype constant) + : local_sym_index_(CONSTANT_CODE) + { this->u_.constant = constant; } + + // Write the GOT entry to an output view. + void + write(const General_options*, unsigned char* pov) const; + + private: + enum + { + GSYM_CODE = -1U, + CONSTANT_CODE = -2U + }; + + union + { + // For a local symbol, the object. + Object* object; + // For a global symbol, the symbol. + Symbol* gsym; + // For a constant, the constant. + Valtype constant; + } u_; + // For a local symbol, the local symbol index. This is GSYM_CODE + // for a global symbol, or CONSTANT_CODE for a constant. + unsigned int local_sym_index_; + }; + + typedef std::vector<Got_entry> Got_entries; + + // Return the offset into the GOT of GOT entry I. + unsigned int + got_offset(unsigned int i) const + { return i * (size / 8); } + + // Return the offset into the GOT of the last entry added. + unsigned int + last_got_offset() const + { return this->got_offset(this->entries_.size() - 1); } + + // Set the size of the section. + void + set_got_size() + { this->set_data_size(this->got_offset(this->entries_.size())); } + + // Options. + const General_options* options_; + // The list of GOT entries. + Got_entries entries_; +}; + +// Output_data_dynamic is used to hold the data in SHT_DYNAMIC +// section. + +class Output_data_dynamic : public Output_section_data +{ + public: + Output_data_dynamic(const Target* target, Stringpool* pool) + : Output_section_data(Output_data::default_alignment(target->get_size())), + target_(target), entries_(), pool_(pool) + { } + + // Add a new dynamic entry with a fixed numeric value. + void + add_constant(elfcpp::DT tag, unsigned int val) + { this->add_entry(Dynamic_entry(tag, val)); } + + // Add a new dynamic entry with the address of output data. + void + add_section_address(elfcpp::DT tag, const Output_data* od) + { this->add_entry(Dynamic_entry(tag, od, false)); } + + // Add a new dynamic entry with the size of output data. + void + add_section_size(elfcpp::DT tag, const Output_data* od) + { this->add_entry(Dynamic_entry(tag, od, true)); } + + // Add a new dynamic entry with the address of a symbol. + void + add_symbol(elfcpp::DT tag, const Symbol* sym) + { this->add_entry(Dynamic_entry(tag, sym)); } + + // Add a new dynamic entry with a string. + void + add_string(elfcpp::DT tag, const char* str) + { this->add_entry(Dynamic_entry(tag, this->pool_->add(str, NULL))); } + + // Set the final data size. + void + do_set_address(uint64_t, off_t); + + // Write out the dynamic entries. + void + do_write(Output_file*); + + protected: + // Adjust the output section to set the entry size. + void + do_adjust_output_section(Output_section*); + + private: + // This POD class holds a single dynamic entry. + class Dynamic_entry + { + public: + // Create an entry with a fixed numeric value. + Dynamic_entry(elfcpp::DT tag, unsigned int val) + : tag_(tag), classification_(DYNAMIC_NUMBER) + { this->u_.val = val; } + + // Create an entry with the size or address of a section. + Dynamic_entry(elfcpp::DT tag, const Output_data* od, bool section_size) + : tag_(tag), + classification_(section_size + ? DYNAMIC_SECTION_SIZE + : DYNAMIC_SECTION_ADDRESS) + { this->u_.od = od; } + + // Create an entry with the address of a symbol. + Dynamic_entry(elfcpp::DT tag, const Symbol* sym) + : tag_(tag), classification_(DYNAMIC_SYMBOL) + { this->u_.sym = sym; } + + // Create an entry with a string. + Dynamic_entry(elfcpp::DT tag, const char* str) + : tag_(tag), classification_(DYNAMIC_STRING) + { this->u_.str = str; } + + // Write the dynamic entry to an output view. + template<int size, bool big_endian> + void + write(unsigned char* pov, const Stringpool* ACCEPT_SIZE_ENDIAN) const; + + private: + enum Classification + { + // Number. + DYNAMIC_NUMBER, + // Section address. + DYNAMIC_SECTION_ADDRESS, + // Section size. + DYNAMIC_SECTION_SIZE, + // Symbol adress. + DYNAMIC_SYMBOL, + // String. + DYNAMIC_STRING + }; + + union + { + // For DYNAMIC_NUMBER. + unsigned int val; + // For DYNAMIC_SECTION_ADDRESS and DYNAMIC_SECTION_SIZE. + const Output_data* od; + // For DYNAMIC_SYMBOL. + const Symbol* sym; + // For DYNAMIC_STRING. + const char* str; + } u_; + // The dynamic tag. + elfcpp::DT tag_; + // The type of entry. + Classification classification_; + }; + + // Add an entry to the list. + void + add_entry(const Dynamic_entry& entry) + { this->entries_.push_back(entry); } + + // Sized version of write function. + template<int size, bool big_endian> + void + sized_write(Output_file* of); + + // The type of the list of entries. + typedef std::vector<Dynamic_entry> Dynamic_entries; + + // The target. + const Target* target_; + // The entries. + Dynamic_entries entries_; + // The pool used for strings. + Stringpool* pool_; +}; + +// An output section. We don't expect to have too many output +// sections, so we don't bother to do a template on the size. + +class Output_section : public Output_data +{ + public: + // Create an output section, giving the name, type, and flags. + Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword); + virtual ~Output_section(); + + // Add a new input section SHNDX, named NAME, with header SHDR, from + // object OBJECT. Return the offset within the output section. + template<int size, bool big_endian> + off_t + add_input_section(Relobj* object, unsigned int shndx, const char *name, + const elfcpp::Shdr<size, big_endian>& shdr); + + // Add generated data POSD to this output section. + void + add_output_section_data(Output_section_data* posd); + + // Return the section name. + const char* + name() const + { return this->name_; } + + // Return the section type. + elfcpp::Elf_Word + type() const + { return this->type_; } + + // Return the section flags. + elfcpp::Elf_Xword + flags() const + { return this->flags_; } + + // Return the section index in the output file. + unsigned int + do_out_shndx() const + { return this->out_shndx_; } + + // Set the output section index. + void + do_set_out_shndx(unsigned int shndx) + { this->out_shndx_ = shndx; } + + // Return the entsize field. + uint64_t + entsize() const + { return this->entsize_; } + + // Set the entsize field. + void + set_entsize(uint64_t v); + + // Set the link field to the output section index of a section. + void + set_link_section(const Output_data* od) + { + gold_assert(this->link_ == 0 + && !this->should_link_to_symtab_ + && !this->should_link_to_dynsym_); + this->link_section_ = od; + } + + // Set the link field to a constant. + void + set_link(unsigned int v) + { + gold_assert(this->link_section_ == NULL + && !this->should_link_to_symtab_ + && !this->should_link_to_dynsym_); + this->link_ = v; + } + + // Record that this section should link to the normal symbol table. + void + set_should_link_to_symtab() + { + gold_assert(this->link_section_ == NULL + && this->link_ == 0 + && !this->should_link_to_dynsym_); + this->should_link_to_symtab_ = true; + } + + // Record that this section should link to the dynamic symbol table. + void + set_should_link_to_dynsym() + { + gold_assert(this->link_section_ == NULL + && this->link_ == 0 + && !this->should_link_to_symtab_); + this->should_link_to_dynsym_ = true; + } + + // Return the info field. + unsigned int + info() const + { + gold_assert(this->info_section_ == NULL); + return this->info_; + } + + // Set the info field to the output section index of a section. + void + set_info_section(const Output_data* od) + { + gold_assert(this->info_ == 0); + this->info_section_ = od; + } + + // Set the info field to a constant. + void + set_info(unsigned int v) + { + gold_assert(this->info_section_ == NULL); + this->info_ = v; + } + + // Set the addralign field. + void + set_addralign(uint64_t v) + { this->addralign_ = v; } + + // Indicate that we need a symtab index. + void + set_needs_symtab_index() + { this->needs_symtab_index_ = true; } + + // Return whether we need a symtab index. + bool + needs_symtab_index() const + { return this->needs_symtab_index_; } + + // Get the symtab index. + unsigned int + symtab_index() const + { + gold_assert(this->symtab_index_ != 0); + return this->symtab_index_; + } + + // Set the symtab index. + void + set_symtab_index(unsigned int index) + { + gold_assert(index != 0); + this->symtab_index_ = index; + } + + // Indicate that we need a dynsym index. + void + set_needs_dynsym_index() + { this->needs_dynsym_index_ = true; } + + // Return whether we need a dynsym index. + bool + needs_dynsym_index() const + { return this->needs_dynsym_index_; } + + // Get the dynsym index. + unsigned int + dynsym_index() const + { + gold_assert(this->dynsym_index_ != 0); + return this->dynsym_index_; + } + + // Set the dynsym index. + void + set_dynsym_index(unsigned int index) + { + gold_assert(index != 0); + this->dynsym_index_ = index; + } + + // Return the output virtual address of OFFSET relative to the start + // of input section SHNDX in object OBJECT. + uint64_t + output_address(const Relobj* object, unsigned int shndx, + off_t offset) const; + + // Set the address of the Output_section. For a typical + // Output_section, there is nothing to do, but if there are any + // Output_section_data objects we need to set the final addresses + // here. + void + do_set_address(uint64_t, off_t); + + // Write the data to the file. For a typical Output_section, this + // does nothing: the data is written out by calling Object::Relocate + // on each input object. But if there are any Output_section_data + // objects we do need to write them out here. + void + do_write(Output_file*); + + // Return the address alignment--function required by parent class. + uint64_t + do_addralign() const + { return this->addralign_; } + + // Return whether this is an Output_section. + bool + do_is_section() const + { return true; } + + // Return whether this is a section of the specified type. + bool + do_is_section_type(elfcpp::Elf_Word type) const + { return this->type_ == type; } + + // Return whether the specified section flag is set. + bool + do_is_section_flag_set(elfcpp::Elf_Xword flag) const + { return (this->flags_ & flag) != 0; } + + // Write the section header into *OPHDR. + template<int size, bool big_endian> + void + write_header(const Layout*, const Stringpool*, + elfcpp::Shdr_write<size, big_endian>*) const; + + private: + // In some cases we need to keep a list of the input sections + // associated with this output section. We only need the list if we + // might have to change the offsets of the input section within the + // output section after we add the input section. The ordinary + // input sections will be written out when we process the object + // file, and as such we don't need to track them here. We do need + // to track Output_section_data objects here. We store instances of + // this structure in a std::vector, so it must be a POD. There can + // be many instances of this structure, so we use a union to save + // some space. + class Input_section + { + public: + Input_section() + : shndx_(0), p2align_(0) + { + this->u1_.data_size = 0; + this->u2_.object = NULL; + } + + // For an ordinary input section. + Input_section(Relobj* object, unsigned int shndx, off_t data_size, + uint64_t addralign) + : shndx_(shndx), + p2align_(ffsll(static_cast<long long>(addralign))) + { + gold_assert(shndx != OUTPUT_SECTION_CODE + && shndx != MERGE_DATA_SECTION_CODE + && shndx != MERGE_STRING_SECTION_CODE); + this->u1_.data_size = data_size; + this->u2_.object = object; + } + + // For a non-merge output section. + Input_section(Output_section_data* posd) + : shndx_(OUTPUT_SECTION_CODE), + p2align_(ffsll(static_cast<long long>(posd->addralign()))) + { + this->u1_.data_size = 0; + this->u2_.posd = posd; + } + + // For a merge section. + Input_section(Output_section_data* posd, bool is_string, uint64_t entsize) + : shndx_(is_string + ? MERGE_STRING_SECTION_CODE + : MERGE_DATA_SECTION_CODE), + p2align_(ffsll(static_cast<long long>(posd->addralign()))) + { + this->u1_.entsize = entsize; + this->u2_.posd = posd; + } + + // The required alignment. + uint64_t + addralign() const + { + return (this->p2align_ == 0 + ? 0 + : static_cast<uint64_t>(1) << (this->p2align_ - 1)); + } + + // Return the required size. + off_t + data_size() const; + + // Return whether this is a merge section which matches the + // parameters. + bool + is_merge_section(bool is_string, uint64_t entsize) const + { + return (this->shndx_ == (is_string + ? MERGE_STRING_SECTION_CODE + : MERGE_DATA_SECTION_CODE) + && this->u1_.entsize == entsize); + } + + // Set the output section. + void + set_output_section(Output_section* os) + { + gold_assert(!this->is_input_section()); + this->u2_.posd->set_output_section(os); + } + + // Set the address and file offset. This is called during + // Layout::finalize. SECOFF is the file offset of the enclosing + // section. + void + set_address(uint64_t addr, off_t off, off_t secoff); + + // Add an input section, for SHF_MERGE sections. + bool + add_input_section(Relobj* object, unsigned int shndx) + { + gold_assert(this->shndx_ == MERGE_DATA_SECTION_CODE + || this->shndx_ == MERGE_STRING_SECTION_CODE); + return this->u2_.posd->add_input_section(object, shndx); + } + + // Given an input OBJECT, an input section index SHNDX within that + // object, and an OFFSET relative to the start of that input + // section, return whether or not the output address is known. + // OUTPUT_SECTION_ADDRESS is the address of the output section + // which this is a part of. If this function returns true, it + // sets *POUTPUT to the output address. + bool + output_address(const Relobj* object, unsigned int shndx, off_t offset, + uint64_t output_section_address, uint64_t *poutput) const; + + // Write out the data. This does nothing for an input section. + void + write(Output_file*); + + private: + // Code values which appear in shndx_. If the value is not one of + // these codes, it is the input section index in the object file. + enum + { + // An Output_section_data. + OUTPUT_SECTION_CODE = -1U, + // An Output_section_data for an SHF_MERGE section with + // SHF_STRINGS not set. + MERGE_DATA_SECTION_CODE = -2U, + // An Output_section_data for an SHF_MERGE section with + // SHF_STRINGS set. + MERGE_STRING_SECTION_CODE = -3U + }; + + // Whether this is an input section. + bool + is_input_section() const + { + return (this->shndx_ != OUTPUT_SECTION_CODE + && this->shndx_ != MERGE_DATA_SECTION_CODE + && this->shndx_ != MERGE_STRING_SECTION_CODE); + } + + // For an ordinary input section, this is the section index in the + // input file. For an Output_section_data, this is + // OUTPUT_SECTION_CODE or MERGE_DATA_SECTION_CODE or + // MERGE_STRING_SECTION_CODE. + unsigned int shndx_; + // The required alignment, stored as a power of 2. + unsigned int p2align_; + union + { + // For an ordinary input section, the section size. + off_t data_size; + // For OUTPUT_SECTION_CODE, this is not used. For + // MERGE_DATA_SECTION_CODE or MERGE_STRING_SECTION_CODE, the + // entity size. + uint64_t entsize; + } u1_; + union + { + // For an ordinary input section, the object which holds the + // input section. + Relobj* object; + // For OUTPUT_SECTION_CODE or MERGE_DATA_SECTION_CODE or + // MERGE_STRING_SECTION_CODE, the data. + Output_section_data* posd; + } u2_; + }; + + typedef std::vector<Input_section> Input_section_list; + + // Add a new output section by Input_section. + void + add_output_section_data(Input_section*); + + // Add an SHF_MERGE input section. Returns true if the section was + // handled. + bool + add_merge_input_section(Relobj* object, unsigned int shndx, uint64_t flags, + uint64_t entsize, uint64_t addralign); + + // Add an output SHF_MERGE section POSD to this output section. + // IS_STRING indicates whether it is a SHF_STRINGS section, and + // ENTSIZE is the entity size. This returns the entry added to + // input_sections_. + void + add_output_merge_section(Output_section_data* posd, bool is_string, + uint64_t entsize); + + // Most of these fields are only valid after layout. + + // The name of the section. This will point into a Stringpool. + const char* name_; + // The section address is in the parent class. + // The section alignment. + uint64_t addralign_; + // The section entry size. + uint64_t entsize_; + // The file offset is in the parent class. + // Set the section link field to the index of this section. + const Output_data* link_section_; + // If link_section_ is NULL, this is the link field. + unsigned int link_; + // Set the section info field to the index of this section. + const Output_data* info_section_; + // If info_section_ is NULL, this is the section info field. + unsigned int info_; + // The section type. + elfcpp::Elf_Word type_; + // The section flags. + elfcpp::Elf_Xword flags_; + // The section index. + unsigned int out_shndx_; + // If there is a STT_SECTION for this output section in the normal + // symbol table, this is the symbol index. This starts out as zero. + // It is initialized in Layout::finalize() to be the index, or -1U + // if there isn't one. + unsigned int symtab_index_; + // If there is a STT_SECTION for this output section in the dynamic + // symbol table, this is the symbol index. This starts out as zero. + // It is initialized in Layout::finalize() to be the index, or -1U + // if there isn't one. + unsigned int dynsym_index_; + // The input sections. This will be empty in cases where we don't + // need to keep track of them. + Input_section_list input_sections_; + // The offset of the first entry in input_sections_. + off_t first_input_offset_; + // Whether this output section needs a STT_SECTION symbol in the + // normal symbol table. This will be true if there is a relocation + // which needs it. + bool needs_symtab_index_ : 1; + // Whether this output section needs a STT_SECTION symbol in the + // dynamic symbol table. This will be true if there is a dynamic + // relocation which needs it. + bool needs_dynsym_index_ : 1; + // Whether the link field of this output section should point to the + // normal symbol table. + bool should_link_to_symtab_ : 1; + // Whether the link field of this output section should point to the + // dynamic symbol table. + bool should_link_to_dynsym_ : 1; +}; + +// An output segment. PT_LOAD segments are built from collections of +// output sections. Other segments typically point within PT_LOAD +// segments, and are built directly as needed. + +class Output_segment +{ + public: + // Create an output segment, specifying the type and flags. + Output_segment(elfcpp::Elf_Word, elfcpp::Elf_Word); + + // Return the virtual address. + uint64_t + vaddr() const + { return this->vaddr_; } + + // Return the physical address. + uint64_t + paddr() const + { return this->paddr_; } + + // Return the segment type. + elfcpp::Elf_Word + type() const + { return this->type_; } + + // Return the segment flags. + elfcpp::Elf_Word + flags() const + { return this->flags_; } + + // Return the memory size. + uint64_t + memsz() const + { return this->memsz_; } + + // Return the file size. + off_t + filesz() const + { return this->filesz_; } + + // Return the maximum alignment of the Output_data. + uint64_t + addralign(); + + // Add an Output_section to this segment. + void + 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. + void + add_initial_output_data(Output_data*); + + // Set the address of the segment to ADDR and the offset to *POFF + // (aligned if necessary), and set the addresses and offsets of all + // contained output sections accordingly. Set the section indexes + // of all contained output sections starting with *PSHNDX. Return + // the address of the immediately following segment. Update *POFF + // and *PSHNDX. This should only be called for a PT_LOAD segment. + uint64_t + set_section_addresses(uint64_t addr, off_t* poff, unsigned int* pshndx); + + // Set the offset of this segment based on the section. This should + // only be called for a non-PT_LOAD segment. + void + set_offset(); + + // Return the number of output sections. + unsigned int + output_section_count() const; + + // Write the segment header into *OPHDR. + template<int size, bool big_endian> + void + write_header(elfcpp::Phdr_write<size, big_endian>*); + + // Write the section headers of associated sections into V. + template<int size, bool big_endian> + unsigned char* + write_section_headers(const Layout*, const Stringpool*, unsigned char* v, + unsigned int* pshndx ACCEPT_SIZE_ENDIAN) const; + + private: + Output_segment(const Output_segment&); + Output_segment& operator=(const 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*); + + // Set the section addresses in an Output_data_list. + uint64_t + set_section_list_addresses(Output_data_list*, uint64_t addr, off_t* poff, + unsigned int* pshndx); + + // Return the number of Output_sections in an Output_data_list. + unsigned int + output_section_count_list(const Output_data_list*) const; + + // Write the section headers in the list into V. + template<int size, bool big_endian> + unsigned char* + write_section_headers_list(const Layout*, const Stringpool*, + const Output_data_list*, unsigned char* v, + unsigned int* pshdx ACCEPT_SIZE_ENDIAN) const; + + // The list of output data with contents attached to this segment. + Output_data_list output_data_; + // The list of output data without contents attached to this segment. + Output_data_list output_bss_; + // The segment virtual address. + uint64_t vaddr_; + // The segment physical address. + uint64_t paddr_; + // The size of the segment in memory. + uint64_t memsz_; + // The segment alignment. + uint64_t align_; + // The offset of the segment data within the file. + off_t offset_; + // The size of the segment data in the file. + off_t filesz_; + // The segment type; + elfcpp::Elf_Word type_; + // The segment flags. + elfcpp::Elf_Word flags_; + // Whether we have set align_. + bool is_align_known_; +}; + +// This class represents the output file. + +class Output_file +{ + public: + Output_file(const General_options& options); + + // Open the output file. FILE_SIZE is the final size of the file. + void + open(off_t file_size); + + // Close the output file and make sure there are no error. + void + close(); + + // We currently always use mmap which makes the view handling quite + // simple. In the future we may support other approaches. + + // Write data to the output file. + void + write(off_t offset, const void* data, off_t len) + { memcpy(this->base_ + offset, data, len); } + + // Get a buffer to use to write to the file, given the offset into + // the file and the size. + unsigned char* + get_output_view(off_t start, off_t size) + { + gold_assert(start >= 0 && size >= 0 && start + size <= this->file_size_); + return this->base_ + start; + } + + // VIEW must have been returned by get_output_view. Write the + // buffer to the file, passing in the offset and the size. + void + write_output_view(off_t, off_t, unsigned char*) + { } + + private: + // General options. + const General_options& options_; + // File name. + const char* name_; + // File descriptor. + int o_; + // File size. + off_t file_size_; + // Base of file mapped into memory. + unsigned char* base_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_OUTPUT_H) diff --git a/gold/po/Make-in b/gold/po/Make-in new file mode 100644 index 000000000000..88ce78fa6e46 --- /dev/null +++ b/gold/po/Make-in @@ -0,0 +1,256 @@ +# Makefile for program source directory in GNU NLS utilities package. +# Copyright (C) 1995, 1996, 1997 by Ulrich Drepper <drepper@gnu.ai.mit.edu> +# Copyright 2003, 2006 Free Software Foundation, Inc. +# +# This file may be copied and used freely without restrictions. It can +# be used in projects which are not available under the GNU Public License +# but which still want to provide support for the GNU gettext functionality. +# Please note that the actual code is *not* freely available. + +PACKAGE = @PACKAGE@ +VERSION = @VERSION@ + +SHELL = /bin/sh +@SET_MAKE@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +top_builddir = @top_builddir@ + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +datadir = $(prefix)/@DATADIRNAME@ +localedir = $(datadir)/locale +gnulocaledir = $(prefix)/share/locale +gettextsrcdir = $(prefix)/share/gettext/po +subdir = po + +DESTDIR = + +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +MKINSTALLDIRS = @MKINSTALLDIRS@ + +CC = @CC@ +GENCAT = @GENCAT@ +GMSGFMT = PATH=../src:$$PATH @GMSGFMT@ +MSGFMT = @MSGFMT@ +XGETTEXT = PATH=../src:$$PATH @XGETTEXT@ +MSGMERGE = PATH=../src:$$PATH msgmerge + +DEFS = @DEFS@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ + +INCLUDES = -I.. -I$(top_srcdir)/intl + +COMPILE = $(CC) -c $(DEFS) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(XCFLAGS) + +SOURCES = cat-id-tbl.c +POFILES = @POFILES@ +GMOFILES = @GMOFILES@ +DISTFILES = ChangeLog Makefile.in.in POTFILES.in $(PACKAGE).pot \ +stamp-cat-id $(POFILES) $(GMOFILES) $(SOURCES) + +POTFILES = \ + +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +INSTOBJEXT = @INSTOBJEXT@ + +.SUFFIXES: +.SUFFIXES: .c .o .po .pox .gmo .mo .msg .cat + +.c.o: + $(COMPILE) $< + +.po.pox: + $(MAKE) $(PACKAGE).pot + $(MSGMERGE) $< $(srcdir)/$(PACKAGE).pot -o $*.pox + +.po.mo: + $(MSGFMT) -o $@ $< + +.po.gmo: + file=$(srcdir)/`echo $* | sed 's,.*/,,'`.gmo \ + && rm -f $$file && $(GMSGFMT) -o $$file $< + +.po.cat: + sed -f ../intl/po2msg.sed < $< > $*.msg \ + && rm -f $@ && $(GENCAT) $@ $*.msg + + +all: all-@USE_NLS@ + +all-yes: $(CATALOGS) @MAINT@ $(PACKAGE).pot +all-no: + +$(srcdir)/$(PACKAGE).pot: $(POTFILES) + $(XGETTEXT) --default-domain=$(PACKAGE) --directory=$(top_srcdir) \ + --add-comments --keyword=_ --keyword=N_ \ + --files-from=$(srcdir)/POTFILES.in + rm -f $(srcdir)/$(PACKAGE).pot + mv $(PACKAGE).po $(srcdir)/$(PACKAGE).pot + +$(srcdir)/cat-id-tbl.c: stamp-cat-id; @: +$(srcdir)/stamp-cat-id: $(PACKAGE).pot + rm -f cat-id-tbl.tmp + sed -f ../intl/po2tbl.sed $(srcdir)/$(PACKAGE).pot \ + | sed -e "s/@PACKAGE NAME@/$(PACKAGE)/" > cat-id-tbl.tmp + if cmp -s cat-id-tbl.tmp $(srcdir)/cat-id-tbl.c; then \ + rm cat-id-tbl.tmp; \ + else \ + echo cat-id-tbl.c changed; \ + rm -f $(srcdir)/cat-id-tbl.c; \ + mv cat-id-tbl.tmp $(srcdir)/cat-id-tbl.c; \ + fi + cd $(srcdir) && rm -f stamp-cat-id && echo timestamp > stamp-cat-id + + +install: install-exec install-data +install-exec: +install-info: +install-html: +install-data: install-data-@USE_NLS@ +install-data-no: all +install-data-yes: all + if test -r $(MKINSTALLDIRS); then \ + $(MKINSTALLDIRS) $(DESTDIR)$(datadir); \ + else \ + $(top_srcdir)/mkinstalldirs $(DESTDIR)$(datadir); \ + fi + @catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + case "$$cat" in \ + *.gmo) destdir=$(gnulocaledir);; \ + *) destdir=$(localedir);; \ + esac; \ + lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \ + dir=$(DESTDIR)$$destdir/$$lang/LC_MESSAGES; \ + if test -r $(MKINSTALLDIRS); then \ + $(MKINSTALLDIRS) $$dir; \ + else \ + $(top_srcdir)/mkinstalldirs $$dir; \ + fi; \ + if test -r $$cat; then \ + $(INSTALL_DATA) $$cat $$dir/$(PACKAGE)$(INSTOBJEXT); \ + echo "installing $$cat as $$dir/$(PACKAGE)$(INSTOBJEXT)"; \ + else \ + $(INSTALL_DATA) $(srcdir)/$$cat $$dir/$(PACKAGE)$(INSTOBJEXT); \ + echo "installing $(srcdir)/$$cat as" \ + "$$dir/$(PACKAGE)$(INSTOBJEXT)"; \ + fi; \ + if test -r $$cat.m; then \ + $(INSTALL_DATA) $$cat.m $$dir/$(PACKAGE)$(INSTOBJEXT).m; \ + echo "installing $$cat.m as $$dir/$(PACKAGE)$(INSTOBJEXT).m"; \ + else \ + if test -r $(srcdir)/$$cat.m ; then \ + $(INSTALL_DATA) $(srcdir)/$$cat.m \ + $$dir/$(PACKAGE)$(INSTOBJEXT).m; \ + echo "installing $(srcdir)/$$cat as" \ + "$$dir/$(PACKAGE)$(INSTOBJEXT).m"; \ + else \ + true; \ + fi; \ + fi; \ + done + if test "$(PACKAGE)" = "gettext"; then \ + if test -r $(MKINSTALLDIRS); then \ + $(MKINSTALLDIRS) $(DESTDIR)$(gettextsrcdir); \ + else \ + $(top_srcdir)/mkinstalldirs $(DESTDIR)$(gettextsrcdir); \ + fi; \ + $(INSTALL_DATA) $(srcdir)/Makefile.in.in \ + $(DESTDIR)$(gettextsrcdir)/Makefile.in.in; \ + else \ + : ; \ + fi + +# Define this as empty until I found a useful application. +installcheck: + +uninstall: + catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \ + rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(PACKAGE)$(INSTOBJEXT); \ + rm -f $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(PACKAGE)$(INSTOBJEXT).m; \ + rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(PACKAGE)$(INSTOBJEXT); \ + rm -f $(DESTDIR)$(gnulocaledir)/$$lang/LC_MESSAGES/$(PACKAGE)$(INSTOBJEXT).m; \ + done + rm -f $(DESTDIR)$(gettextsrcdir)/po-Makefile.in.in + +check: all + +cat-id-tbl.o: ../intl/libgettext.h + +html dvi pdf ps info tags TAGS ID: + +mostlyclean: + rm -f core core.* *.pox $(PACKAGE).po *.old.po cat-id-tbl.tmp + rm -fr *.o + +clean: mostlyclean + +distclean: clean + rm -f Makefile Makefile.in POTFILES *.mo *.msg *.cat *.cat.m + +maintainer-clean: distclean + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + rm -f $(GMOFILES) + +distdir = ../$(PACKAGE)-$(VERSION)/$(subdir) +dist distdir: update-po $(DISTFILES) + dists="$(DISTFILES)"; \ + for file in $$dists; do \ + ln $(srcdir)/$$file $(distdir) 2> /dev/null \ + || cp -p $(srcdir)/$$file $(distdir); \ + done + +update-po: Makefile + $(MAKE) $(PACKAGE).pot + PATH=`pwd`/../src:$$PATH; \ + cd $(srcdir); \ + catalogs='$(CATALOGS)'; \ + for cat in $$catalogs; do \ + cat=`basename $$cat`; \ + lang=`echo $$cat | sed 's/\$(CATOBJEXT)$$//'`; \ + mv $$lang.po $$lang.old.po; \ + echo "$$lang:"; \ + if $(MSGMERGE) $$lang.old.po $(PACKAGE).pot -o $$lang.po; then \ + rm -f $$lang.old.po; \ + else \ + echo "msgmerge for $$cat failed!"; \ + rm -f $$lang.po; \ + mv $$lang.old.po $$lang.po; \ + fi; \ + done + +POTFILES: POTFILES.in + ( if test 'x$(srcdir)' != 'x.'; then \ + posrcprefix='$(top_srcdir)/'; \ + else \ + posrcprefix="../"; \ + fi; \ + rm -f $@-t $@ \ + && (sed -e '/^#/d' -e '/^[ ]*$$/d' \ + -e "s@.*@ $$posrcprefix& \\\\@" < $(srcdir)/$@.in \ + | sed -e '$$s/\\$$//') > $@-t \ + && chmod a-w $@-t \ + && mv $@-t $@ ) + +POTFILES.in: @MAINT@ ../Makefile + cd .. && $(MAKE) po/POTFILES.in + +Makefile: Make-in ../config.status POTFILES + cd .. \ + && CONFIG_FILES=$(subdir)/Makefile.in:$(subdir)/Make-in \ + CONFIG_HEADERS= $(SHELL) ./config.status + +# Tell versions [3.59,3.63) of GNU make not to export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in new file mode 100644 index 000000000000..0c4145af71a7 --- /dev/null +++ b/gold/po/POTFILES.in @@ -0,0 +1,46 @@ +archive.cc +archive.h +common.cc +common.h +defstd.cc +defstd.h +dirsearch.cc +dirsearch.h +dynobj.cc +dynobj.h +fileread.cc +fileread.h +gold.cc +gold.h +gold-threads.cc +gold-threads.h +i386.cc +layout.cc +layout.h +merge.cc +merge.h +object.cc +object.h +options.cc +options.h +output.cc +output.h +readsyms.cc +readsyms.h +reloc.cc +reloc.h +reloc-types.h +resolve.cc +script.cc +script-c.h +script.h +stringpool.cc +stringpool.h +symtab.cc +symtab.h +target.h +target-reloc.h +target-select.cc +target-select.h +workqueue.cc +workqueue.h diff --git a/gold/po/gold.pot b/gold/po/gold.pot new file mode 100644 index 000000000000..79d0628dc44f --- /dev/null +++ b/gold/po/gold.pot @@ -0,0 +1,654 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2007-05-16 10:40-0700\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" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: archive.cc:62 +#, c-format +msgid "%s: %s: no archive symbol table (run ranlib)\n" +msgstr "" + +#: archive.cc:91 +#, c-format +msgid "%s: %s: bad archive symbol table names\n" +msgstr "" + +#: archive.cc:129 +#, c-format +msgid "%s; %s: malformed archive header at %ld\n" +msgstr "" + +#: archive.cc:150 +#, c-format +msgid "%s: %s: malformed archive header size at %ld\n" +msgstr "" + +#: archive.cc:162 +#, c-format +msgid "%s: %s: malformed archive header name at %ld\n" +msgstr "" + +#: archive.cc:188 +#, c-format +msgid "%s: %s: bad extended name index at %ld\n" +msgstr "" + +#: archive.cc:199 +#, c-format +msgid "%s: %s: bad extended name entry at header %ld\n" +msgstr "" + +#: archive.cc:280 archive.cc:293 +#, c-format +msgid "%s: %s: member at %ld is not an ELF object" +msgstr "" + +#: dirsearch.cc:50 +#, c-format +msgid "can not read directory %s" +msgstr "" + +#: dynobj.cc:109 +#, c-format +msgid "%s: %s: unexpected duplicate type %u section: %u, %u\n" +msgstr "" + +#: dynobj.cc:150 +#, c-format +msgid "%s: %s: unexpected link in section %u header: %u != %u\n" +msgstr "" + +#: dynobj.cc:188 +#, c-format +msgid "%s: %s: DYNAMIC section %u link out of range: %u\n" +msgstr "" + +#: dynobj.cc:198 +#, c-format +msgid "%s: %s: DYNAMIC section %u link %u is not a strtab\n" +msgstr "" + +#: dynobj.cc:220 +#, c-format +msgid "%s: %s: DT_SONAME value out of range: %lld >= %lld\n" +msgstr "" + +#: dynobj.cc:237 +#, c-format +msgid "%s: %s: missing DT_NULL in dynamic segment\n" +msgstr "" + +#: dynobj.cc:285 +#, c-format +msgid "%s: %s: invalid dynamic symbol table name index: %u\n" +msgstr "" + +#: dynobj.cc:293 +#, c-format +msgid "%s: %s: dynamic symbol table name section has wrong type: %u\n" +msgstr "" + +#: dynobj.cc:368 object.cc:420 +#, c-format +msgid "%s: %s: bad section name offset for section %u: %lu\n" +msgstr "" + +#: dynobj.cc:399 +#, c-format +msgid "%s: %s: duplicate definition for version %u\n" +msgstr "" + +#: dynobj.cc:431 +#, c-format +msgid "%s: %s: unexpected verdef version %u\n" +msgstr "" + +#: dynobj.cc:447 +#, c-format +msgid "%s: %s: verdef vd_cnt field too small: %u\n" +msgstr "" + +#: dynobj.cc:456 +#, c-format +msgid "%s: %s: verdef vd_aux field out of range: %u\n" +msgstr "" + +#: dynobj.cc:468 +#, c-format +msgid "%s: %s: verdaux vda_name field out of range: %u\n" +msgstr "" + +#: dynobj.cc:479 +#, c-format +msgid "%s: %s: verdef vd_next field out of range: %u\n" +msgstr "" + +#: dynobj.cc:513 +#, c-format +msgid "%s: %s: unexpected verneed version %u\n" +msgstr "" + +#: dynobj.cc:524 +#, c-format +msgid "%s: %s: verneed vn_aux field out of range: %u\n" +msgstr "" + +#: dynobj.cc:539 +#, c-format +msgid "%s: %s: vernaux vna_name field out of range: %u\n" +msgstr "" + +#: dynobj.cc:552 +#, c-format +msgid "%s: %s: verneed vna_next field out of range: %u\n" +msgstr "" + +#: dynobj.cc:565 +#, c-format +msgid "%s: %s: verneed vn_next field out of range: %u\n" +msgstr "" + +#: dynobj.cc:613 +#, c-format +msgid "%s: %s: size of dynamic symbols is not multiple of symbol size\n" +msgstr "" + +#: dynobj.cc:1241 +#, c-format +msgid "%s: symbol %s has undefined version %s\n" +msgstr "" + +#: fileread.cc:55 +#, c-format +msgid "%s: warning: close(%s) failed: %s" +msgstr "" + +#: fileread.cc:143 +#, c-format +msgid "%s: %s: lseek to %lld failed: %s" +msgstr "" + +#: fileread.cc:153 +#, c-format +msgid "%s: %s: read failed: %s\n" +msgstr "" + +#: fileread.cc:173 fileread.cc:256 +#, c-format +msgid "%s: %s: file too short: read only %lld of %lld bytes at %lld\n" +msgstr "" + +#: fileread.cc:365 +#, c-format +msgid "%s: cannot find %s\n" +msgstr "" + +#: fileread.cc:373 +#, c-format +msgid "%s: cannot open %s: %s\n" +msgstr "" + +#: gold.cc:106 +msgid "no input files" +msgstr "" + +#: gold-threads.cc:46 +msgid "pthead_mutextattr_init failed" +msgstr "" + +#: gold-threads.cc:49 +msgid "pthread_mutextattr_settype failed" +msgstr "" + +#: gold-threads.cc:53 +msgid "pthread_mutex_init failed" +msgstr "" + +#: gold-threads.cc:56 +msgid "pthread_mutexattr_destroy failed" +msgstr "" + +#: gold-threads.cc:62 +msgid "pthread_mutex_destroy failed" +msgstr "" + +#: gold-threads.cc:69 +msgid "pthread_mutex_lock failed" +msgstr "" + +#: gold-threads.cc:76 +msgid "pthread_mutex_unlock failed" +msgstr "" + +#: gold-threads.cc:157 +msgid "pthread_cond_init failed" +msgstr "" + +#: gold-threads.cc:163 +msgid "pthread_cond_destroy failed" +msgstr "" + +#: gold-threads.cc:170 +msgid "pthread_cond_wait failed" +msgstr "" + +#: gold-threads.cc:177 +msgid "pthread_cond_signal failed" +msgstr "" + +#. FIXME: This needs to specify the location somehow. +#: i386.cc:100 +#, c-format +msgid "%s: missing expected TLS relocation\n" +msgstr "" + +#: i386.cc:729 i386.cc:870 i386.cc:1136 +#, c-format +msgid "%s: %s: unexpected reloc %u in object file\n" +msgstr "" + +#: i386.cc:765 i386.cc:784 +#, c-format +msgid "%s: %s: unsupported reloc %u against local symbol\n" +msgstr "" + +#: i386.cc:906 i386.cc:927 +#, c-format +msgid "%s: %s: unsupported reloc %u against global symbol %s\n" +msgstr "" + +#: i386.cc:950 +#, c-format +msgid "%s: %s: unsupported RELA reloc section\n" +msgstr "" + +#: i386.cc:1041 +#, c-format +msgid "%s: %s: missing expected TLS relocation\n" +msgstr "" + +#: i386.cc:1168 i386.cc:1245 i386.cc:1256 +#, c-format +msgid "%s: %s: unsupported reloc %u\n" +msgstr "" + +#: i386.cc:1195 +#, c-format +msgid "%s: %s: TLS reloc but no TLS segment\n" +msgstr "" + +#: i386.cc:1230 +#, c-format +msgid "%s: %s: unsupported reloc type %u\n" +msgstr "" + +#: i386.cc:1439 +#, c-format +msgid "%s: %s: TLS relocation out of range\n" +msgstr "" + +#: i386.cc:1457 +#, c-format +msgid "%s: %s: TLS relocation against invalid instruction\n" +msgstr "" + +#: merge.cc:252 +#, c-format +msgid "" +"%s: %s: mergeable string section length not multiple of character size\n" +msgstr "" + +#: merge.cc:269 +#, c-format +msgid "%s: %s: entry in mergeable string section not null terminated\n" +msgstr "" + +#: object.cc:30 +#, c-format +msgid "%s: %s: unsupported ELF machine number %d\n" +msgstr "" + +#: object.cc:86 +#, c-format +msgid "%s: %s: section name section has wrong type: %u\n" +msgstr "" + +#: object.cc:228 +#, c-format +msgid "%s: %s: invalid symbol table name index: %u\n" +msgstr "" + +#: object.cc:236 +#, c-format +msgid "%s: %s: symbol table name section has wrong type: %u\n" +msgstr "" + +#: object.cc:292 +#, c-format +msgid "%s: %s: section group %u info %u out of range\n" +msgstr "" + +#: object.cc:309 +#, c-format +msgid "%s: %s: symbol %u name offset %u out of range\n" +msgstr "" + +#: object.cc:343 +#, c-format +msgid "%s: %s: section %u in section group %u out of range" +msgstr "" + +#: object.cc:487 +#, c-format +msgid "%s: %s: size of symbols is not multiple of symbol size\n" +msgstr "" + +#: object.cc:575 +#, c-format +msgid "%s: %s: unknown section index %u for local symbol %u\n" +msgstr "" + +#: object.cc:586 +#, c-format +msgid "%s: %s: local symbol %u section index %u out of range\n" +msgstr "" + +#: object.cc:620 +#, c-format +msgid "%s: %s: local symbol %u section name out of range: %u >= %u\n" +msgstr "" + +#: object.cc:834 +#, c-format +msgid "%s: %s: unsupported ELF file type %d\n" +msgstr "" + +#: object.cc:853 object.cc:906 object.cc:927 +#, c-format +msgid "%s: %s: ELF file too short\n" +msgstr "" + +#: object.cc:862 +#, c-format +msgid "%s: %s: invalid ELF version 0\n" +msgstr "" + +#: object.cc:865 +#, c-format +msgid "%s: %s: unsupported ELF version %d\n" +msgstr "" + +#: object.cc:873 +#, c-format +msgid "%s: %s: invalid ELF class 0\n" +msgstr "" + +#: object.cc:880 +#, c-format +msgid "%s: %s: unsupported ELF class %d\n" +msgstr "" + +#: object.cc:888 +#, c-format +msgid "%s: %s: invalid ELF data encoding\n" +msgstr "" + +#: object.cc:895 +#, c-format +msgid "%s: %s: unsupported ELF data encoding %d\n" +msgstr "" + +#: options.cc:115 +#, c-format +msgid "" +"Usage: %s [options] file...\n" +"Options:\n" +msgstr "" + +#: options.cc:227 +msgid "Search for library LIBNAME" +msgstr "" + +#: options.cc:228 +msgid "-lLIBNAME --library LIBNAME" +msgstr "" + +#: options.cc:230 +msgid "Start a library search group" +msgstr "" + +#: options.cc:232 +msgid "End a library search group" +msgstr "" + +#: options.cc:234 +msgid "Set dynamic linker path" +msgstr "" + +#: options.cc:235 +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:242 +msgid "Set output file name" +msgstr "" + +#: options.cc:243 +msgid "-o FILE, --output FILE" +msgstr "" + +#: options.cc:245 +msgid "Generate relocatable output" +msgstr "" + +#: options.cc:247 +msgid "Generate shared library" +msgstr "" + +#: options.cc:249 +msgid "Do not link against shared libraries" +msgstr "" + +#: 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:393 options.cc:444 options.cc:523 +msgid "missing argument" +msgstr "" + +#: options.cc:406 options.cc:453 +msgid "unknown option" +msgstr "" + +#: options.cc:461 +#, c-format +msgid "%s: missing group end" +msgstr "" + +#: options.cc:536 +msgid "may not nest groups" +msgstr "" + +#: options.cc:546 +msgid "group end without group start" +msgstr "" + +#: options.cc:556 +#, c-format +msgid "%s: use the --help option for usage information\n" +msgstr "" + +#: options.cc:565 script.cc:1133 +#, c-format +msgid "%s: %s: %s\n" +msgstr "" + +#: options.cc:574 +#, c-format +msgid "%s: -%c: %s\n" +msgstr "" + +#: output.cc:903 +#, c-format +msgid "%s: %s: invalid alignment %lu for section \"%s\"\n" +msgstr "" + +#: output.cc:1519 +#, c-format +msgid "%s: %s: open: %s\n" +msgstr "" + +#: output.cc:1528 +#, c-format +msgid "%s: %s: lseek: %s\n" +msgstr "" + +#: output.cc:1535 +#, c-format +msgid "%s: %s: write: %s\n" +msgstr "" + +#: output.cc:1545 +#, c-format +msgid "%s: %s: mmap: %s\n" +msgstr "" + +#: output.cc:1559 +#, c-format +msgid "%s: %s: munmap: %s\n" +msgstr "" + +#: output.cc:1567 +#, c-format +msgid "%s: %s: close: %s\n" +msgstr "" + +#: 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:149 +#, c-format +msgid "%s: %s: not an object or archive\n" +msgstr "" + +#: reloc.cc:169 reloc.cc:413 +#, c-format +msgid "%s: %s: relocation section %u has bad info %u\n" +msgstr "" + +#: reloc.cc:188 reloc.cc:430 +#, c-format +msgid "%s: %s: relocation section %u uses unexpected symbol table %u\n" +msgstr "" + +#: reloc.cc:204 reloc.cc:449 +#, c-format +msgid "%s: %s: unexpected entsize for reloc section %u: %lu != %u" +msgstr "" + +#: reloc.cc:215 reloc.cc:460 +#, c-format +msgid "%s: %s: reloc section %u size %lu uneven" +msgstr "" + +#: resolve.cc:147 +#, c-format +msgid "%s: %s: invalid STB_LOCAL symbol %s in external symbols\n" +msgstr "" + +#: resolve.cc:153 +#, c-format +msgid "%s: %s: unsupported symbol binding %d for symbol %s\n" +msgstr "" + +#: symtab.cc:450 symtab.cc:547 +#, c-format +msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n" +msgstr "" + +#: symtab.cc:467 +#, c-format +msgid "%s: %s: bad global symbol name offset %u at %lu\n" +msgstr "" + +#: symtab.cc:554 +#, c-format +msgid "%s: %s: too few symbol versions\n" +msgstr "" + +#: symtab.cc:574 +#, c-format +msgid "%s: %s: bad symbol name offset %u at %lu\n" +msgstr "" + +#: symtab.cc:618 +#, c-format +msgid "%s: %s: versym for symbol %zu out of range: %u\n" +msgstr "" + +#: symtab.cc:626 +#, c-format +msgid "%s: %s: versym for symbol %zu has no name: %u\n" +msgstr "" + +#: symtab.cc:1106 symtab.cc:1278 +#, c-format +msgid "%s: %s: unsupported symbol section 0x%x\n" +msgstr "" + +#: symtab.cc:1471 +#, c-format +msgid "%s: %s: warning: %s\n" +msgstr "" + +#: target-reloc.h:170 +#, c-format +msgid "%s: %s: reloc has bad offset %zu\n" +msgstr "" + +#: target-reloc.h:180 +#, c-format +msgid "%s: %s: undefined reference to '%s'\n" +msgstr "" diff --git a/gold/readsyms.cc b/gold/readsyms.cc new file mode 100644 index 000000000000..86828fc0a7c4 --- /dev/null +++ b/gold/readsyms.cc @@ -0,0 +1,313 @@ +// readsyms.cc -- read input file symbols for gold + +#include "gold.h" + +#include <cstring> + +#include "elfcpp.h" +#include "options.h" +#include "dirsearch.h" +#include "symtab.h" +#include "object.h" +#include "archive.h" +#include "script.h" +#include "readsyms.h" + +namespace gold +{ + +// Class read_symbols. + +Read_symbols::~Read_symbols() +{ + // The this_blocker_ and next_blocker_ pointers are passed on to the + // Add_symbols task. +} + +// Return whether a Read_symbols task is runnable. We can read an +// ordinary input file immediately. For an archive specified using +// -l, we have to wait until the search path is complete. + +Task::Is_runnable_type +Read_symbols::is_runnable(Workqueue*) +{ + if (this->input_argument_->is_file() + && this->input_argument_->file().is_lib() + && this->dirpath_.token().is_blocked()) + return IS_BLOCKED; + + return IS_RUNNABLE; +} + +// Return a Task_locker for a Read_symbols task. We don't need any +// locks here. + +Task_locker* +Read_symbols::locks(Workqueue*) +{ + return NULL; +} + +// Run a Read_symbols task. This is where we actually read the +// symbols and relocations. + +void +Read_symbols::run(Workqueue* workqueue) +{ + if (this->input_argument_->is_group()) + { + gold_assert(this->input_group_ == NULL); + this->do_group(workqueue); + return; + } + + 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. + + int ehdr_size = elfcpp::Elf_sizes<64>::ehdr_size; + off_t bytes; + const unsigned char* p = input_file->file().get_view(0, ehdr_size, &bytes); + if (bytes >= 4) + { + static unsigned char elfmagic[4] = + { + elfcpp::ELFMAG0, elfcpp::ELFMAG1, + elfcpp::ELFMAG2, elfcpp::ELFMAG3 + }; + if (memcmp(p, elfmagic, 4) == 0) + { + // This is an ELF object. + + 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"), + program_name, input_file->name()); + gold_exit(false); + } + + Read_symbols_data* sd = new Read_symbols_data; + obj->read_symbols(sd); + workqueue->queue_front(new Add_symbols(this->options_, + this->input_objects_, + this->symtab_, this->layout_, + obj, sd, + this->this_blocker_, + this->next_blocker_)); + + // Opening the file locked it, so now we need to unlock it. + input_file->file().unlock(); + + return; + } + } + + if (bytes >= Archive::sarmag) + { + if (memcmp(p, Archive::armag, Archive::sarmag) == 0) + { + // This is an archive. + Archive* arch = new Archive(this->input_argument_->file().name(), + input_file); + arch->setup(); + workqueue->queue(new Add_archive_symbols(this->options_, + this->symtab_, + this->layout_, + this->input_objects_, + arch, + this->input_group_, + this->this_blocker_, + this->next_blocker_)); + return; + } + } + + 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()); + gold_exit(false); +} + +// Handle a group. We need to walk through the arguments over and +// over until we don't see any new undefined symbols. We do this by +// setting off Read_symbols Tasks as usual, but recording the archive +// entries instead of deleting them. We also start a Finish_group +// Task which runs after we've read all the symbols. In that task we +// process the archives in a loop until we are done. + +void +Read_symbols::do_group(Workqueue* workqueue) +{ + Input_group* input_group = new 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; + gold_assert(arg->is_file()); + + Task_token* next_blocker = new Task_token(); + next_blocker->add_blocker(); + workqueue->queue(new Read_symbols(this->options_, this->input_objects_, + this->symtab_, this->layout_, + this->dirpath_, arg, input_group, + this_blocker, next_blocker)); + this_blocker = next_blocker; + } + + const int saw_undefined = this->symtab_->saw_undefined(); + workqueue->queue(new Finish_group(this->options_, + this->input_objects_, + this->symtab_, + this->layout_, + input_group, + saw_undefined, + this_blocker, + this->next_blocker_)); +} + +// Class Add_symbols. + +Add_symbols::~Add_symbols() +{ + if (this->this_blocker_ != NULL) + delete this->this_blocker_; + // next_blocker_ is deleted by the task associated with the next + // input file. +} + +// We are blocked by this_blocker_. We block next_blocker_. We also +// lock the file. + +Task::Is_runnable_type +Add_symbols::is_runnable(Workqueue*) +{ + if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) + return IS_BLOCKED; + if (this->object_->is_locked()) + return IS_LOCKED; + return IS_RUNNABLE; +} + +class Add_symbols::Add_symbols_locker : public Task_locker +{ + public: + Add_symbols_locker(Task_token& token, Workqueue* workqueue, + Object* object) + : blocker_(token, workqueue), objlock_(*object) + { } + + private: + Task_locker_block blocker_; + Task_locker_obj<Object> objlock_; +}; + +Task_locker* +Add_symbols::locks(Workqueue* workqueue) +{ + return new Add_symbols_locker(*this->next_blocker_, workqueue, + this->object_); +} + +// Add the symbols in the object to the symbol table. + +void +Add_symbols::run(Workqueue*) +{ + if (!this->input_objects_->add_object(this->object_)) + { + // FIXME: We need to close the descriptor here. + delete this->object_; + } + else + { + this->object_->layout(this->options_, this->symtab_, this->layout_, + this->sd_); + this->object_->add_symbols(this->symtab_, this->sd_); + } + delete this->sd_; + this->sd_ = NULL; +} + +// Class Finish_group. + +Finish_group::~Finish_group() +{ + if (this->this_blocker_ != NULL) + delete this->this_blocker_; + // next_blocker_ is deleted by the task associated with the next + // input file following the group. +} + +// We need to wait for THIS_BLOCKER_ and unblock NEXT_BLOCKER_. + +Task::Is_runnable_type +Finish_group::is_runnable(Workqueue*) +{ + if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) + return IS_BLOCKED; + return IS_RUNNABLE; +} + +Task_locker* +Finish_group::locks(Workqueue* workqueue) +{ + return new Task_locker_block(*this->next_blocker_, workqueue); +} + +// Loop over the archives until there are no new undefined symbols. + +void +Finish_group::run(Workqueue*) +{ + int saw_undefined = this->saw_undefined_; + while (saw_undefined != this->symtab_->saw_undefined()) + { + saw_undefined = this->symtab_->saw_undefined(); + + for (Input_group::const_iterator p = this->input_group_->begin(); + p != this->input_group_->end(); + ++p) + { + Task_lock_obj<Archive> tl(**p); + + (*p)->add_symbols(this->options_, this->symtab_, this->layout_, + this->input_objects_); + } + } + + // Delete all the archives now that we no longer need them. + for (Input_group::const_iterator p = this->input_group_->begin(); + p != this->input_group_->end(); + ++p) + delete *p; + delete this->input_group_; +} + +} // End namespace gold. diff --git a/gold/readsyms.h b/gold/readsyms.h new file mode 100644 index 000000000000..d5ada61d25ed --- /dev/null +++ b/gold/readsyms.h @@ -0,0 +1,194 @@ +// readsyms.h -- read input file symbols for gold -*- C++ -*- + +#ifndef GOLD_READSYMS_H +#define GOLD_READSYMS_H + +#include <vector> + +#include "workqueue.h" +#include "object.h" + +namespace gold +{ + +class Input_objects; +class Symbol_table; +class Input_group; +class Archive; + +// This Task is responsible for reading the symbols from an input +// file. This also includes reading the relocations so that we can +// check for any that require a PLT and/or a GOT. After the data has +// been read, this queues up another task to actually add the symbols +// to the symbol table. The tasks are separated because the file +// reading can occur in parallel but adding the symbols must be done +// in the order of the input files. + +class Read_symbols : public Task +{ + public: + // DIRPATH is the list of directories to search for libraries. + // INPUT is the file to read. INPUT_GROUP is not NULL if we are in + // the middle of an input group. THIS_BLOCKER is used to prevent + // the associated Add_symbols task from running before the previous + // one has completed; it will be NULL for the first task. + // NEXT_BLOCKER is used to block the next input file from adding + // symbols. + Read_symbols(const General_options& options, Input_objects* input_objects, + Symbol_table* symtab, Layout* layout, const Dirsearch& dirpath, + 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_argument_(input_argument), + input_group_(input_group), this_blocker_(this_blocker), + next_blocker_(next_blocker) + { } + + ~Read_symbols(); + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + // Handle an archive group. + void + do_group(Workqueue*); + + const General_options& options_; + Input_objects* input_objects_; + Symbol_table* symtab_; + Layout* layout_; + const Dirsearch& dirpath_; + const Input_argument* input_argument_; + Input_group* input_group_; + Task_token* this_blocker_; + Task_token* next_blocker_; +}; + +// This Task handles adding the symbols to the symbol table. These +// tasks must be run in the same order as the arguments appear on the +// command line. + +class Add_symbols : public Task +{ + public: + // THIS_BLOCKER is used to prevent this task from running before the + // one for the previous input file. NEXT_BLOCKER is used to prevent + // the next task from running. + Add_symbols(const General_options& options, Input_objects* input_objects, + Symbol_table* symtab, Layout* layout, Object* object, + Read_symbols_data* sd, Task_token* this_blocker, + Task_token* next_blocker) + : options_(options), input_objects_(input_objects), symtab_(symtab), + layout_(layout), object_(object), sd_(sd), this_blocker_(this_blocker), + next_blocker_(next_blocker) + { } + + ~Add_symbols(); + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + +private: + class Add_symbols_locker; + + const General_options& options_; + Input_objects* input_objects_; + Symbol_table* symtab_; + Layout* layout_; + Object* object_; + Read_symbols_data* sd_; + Task_token* this_blocker_; + Task_token* next_blocker_; +}; + +// This class is used to track the archives in a group. + +class Input_group +{ + public: + typedef std::vector<Archive*> Archives; + typedef Archives::const_iterator const_iterator; + + Input_group() + : archives_() + { } + + // Add an archive to the group. + void + add_archive(Archive* arch) + { this->archives_.push_back(arch); } + + // Loop over the archives in the group. + + const_iterator + begin() const + { return this->archives_.begin(); } + + const_iterator + end() const + { return this->archives_.end(); } + + private: + Archives archives_; +}; + +// This class is used to finish up handling a group. It is just a +// closure. + +class Finish_group : public Task +{ + public: + Finish_group(const General_options& options, Input_objects* input_objects, + Symbol_table* symtab, Layout* layout, Input_group* input_group, + int saw_undefined, Task_token* this_blocker, + Task_token* next_blocker) + : options_(options), input_objects_(input_objects), symtab_(symtab), + layout_(layout), input_group_(input_group), + saw_undefined_(saw_undefined), this_blocker_(this_blocker), + next_blocker_(next_blocker) + { } + + ~Finish_group(); + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + const General_options& options_; + Input_objects* input_objects_; + Symbol_table* symtab_; + Layout* layout_; + Input_group* input_group_; + int saw_undefined_; + Task_token* this_blocker_; + Task_token* next_blocker_; +}; + +} // end namespace gold + +#endif // !defined(GOLD_READSYMS_H) diff --git a/gold/reloc-types.h b/gold/reloc-types.h new file mode 100644 index 000000000000..62538d60117c --- /dev/null +++ b/gold/reloc-types.h @@ -0,0 +1,36 @@ +// reloc-types.h -- ELF relocation templates for gold -*- C++ -*- + +// This header files defines a few convenient templated types for use +// when handling ELF relocations. + +#ifndef GOLD_RELOC_TYPES_H +#define GOLD_RELOC_TYPES_H + +#include "elfcpp.h" + +namespace gold +{ + +// Pick the ELF relocation accessor class and the size based on +// SH_TYPE, which is either elfcpp::SHT_REL or elfcpp::SHT_RELA. + +template<int sh_type, int size, bool big_endian> +struct Reloc_types; + +template<int size, bool big_endian> +struct Reloc_types<elfcpp::SHT_REL, size, big_endian> +{ + typedef typename elfcpp::Rel<size, big_endian> Reloc; + static const int reloc_size = elfcpp::Elf_sizes<size>::rel_size; +}; + +template<int size, bool big_endian> +struct Reloc_types<elfcpp::SHT_RELA, size, big_endian> +{ + typedef typename elfcpp::Rela<size, big_endian> Reloc; + static const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; +}; + +}; // End namespace gold. + +#endif // !defined(GOLD_RELOC_TYPE_SH) diff --git a/gold/reloc.cc b/gold/reloc.cc new file mode 100644 index 000000000000..76ab19613953 --- /dev/null +++ b/gold/reloc.cc @@ -0,0 +1,738 @@ +// reloc.cc -- relocate input files for gold. + +#include "gold.h" + +#include "workqueue.h" +#include "object.h" +#include "symtab.h" +#include "output.h" +#include "reloc.h" + +namespace gold +{ + +// Read_relocs methods. + +// These tasks just read the relocation information from the file. +// After reading it, the start another task to process the +// information. These tasks requires access to the file. + +Task::Is_runnable_type +Read_relocs::is_runnable(Workqueue*) +{ + return this->object_->is_locked() ? IS_LOCKED : IS_RUNNABLE; +} + +// Lock the file. + +Task_locker* +Read_relocs::locks(Workqueue*) +{ + return new Task_locker_obj<Object>(*this->object_); +} + +// Read the relocations and then start a Scan_relocs_task. + +void +Read_relocs::run(Workqueue* workqueue) +{ + Read_relocs_data *rd = new Read_relocs_data; + this->object_->read_relocs(rd); + workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_, + this->layout_, this->object_, rd, + this->symtab_lock_, this->blocker_)); +} + +// Scan_relocs methods. + +// These tasks scan the relocations read by Read_relocs and mark up +// the symbol table to indicate which relocations are required. We +// use a lock on the symbol table to keep them from interfering with +// each other. + +Task::Is_runnable_type +Scan_relocs::is_runnable(Workqueue*) +{ + if (!this->symtab_lock_->is_writable() || this->object_->is_locked()) + return IS_LOCKED; + return IS_RUNNABLE; +} + +// Return the locks we hold: one on the file, one on the symbol table +// and one blocker. + +class Scan_relocs::Scan_relocs_locker : public Task_locker +{ + public: + Scan_relocs_locker(Object* object, Task_token& symtab_lock, Task* task, + Task_token& blocker, Workqueue* workqueue) + : objlock_(*object), symtab_locker_(symtab_lock, task), + blocker_(blocker, workqueue) + { } + + private: + Task_locker_obj<Object> objlock_; + Task_locker_write symtab_locker_; + Task_locker_block blocker_; +}; + +Task_locker* +Scan_relocs::locks(Workqueue* workqueue) +{ + return new Scan_relocs_locker(this->object_, *this->symtab_lock_, this, + *this->blocker_, workqueue); +} + +// Scan the relocs. + +void +Scan_relocs::run(Workqueue*) +{ + this->object_->scan_relocs(this->options_, this->symtab_, this->layout_, + this->rd_); + delete this->rd_; + this->rd_ = NULL; +} + +// Relocate_task methods. + +// These tasks are always runnable. + +Task::Is_runnable_type +Relocate_task::is_runnable(Workqueue*) +{ + return IS_RUNNABLE; +} + +// We want to lock the file while we run. We want to unblock +// FINAL_BLOCKER when we are done. + +class Relocate_task::Relocate_locker : public Task_locker +{ + public: + Relocate_locker(Task_token& token, Workqueue* workqueue, + Object* object) + : blocker_(token, workqueue), objlock_(*object) + { } + + private: + Task_locker_block blocker_; + Task_locker_obj<Object> objlock_; +}; + +Task_locker* +Relocate_task::locks(Workqueue* workqueue) +{ + return new Relocate_locker(*this->final_blocker_, workqueue, + this->object_); +} + +// Run the task. + +void +Relocate_task::run(Workqueue*) +{ + this->object_->relocate(this->options_, this->symtab_, this->layout_, + this->of_); +} + +// Read the relocs and local symbols from the object file and store +// the information in RD. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd) +{ + rd->relocs.clear(); + + unsigned int shnum = this->shnum(); + if (shnum == 0) + return; + + rd->relocs.reserve(shnum / 2); + + const unsigned char *pshdrs = this->get_view(this->elf_file_.shoff(), + shnum * This::shdr_size); + // Skip the first, dummy, section. + const unsigned char *ps = pshdrs + This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, ps += This::shdr_size) + { + typename This::Shdr shdr(ps); + + unsigned int sh_type = shdr.get_sh_type(); + if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA) + continue; + + unsigned int shndx = shdr.get_sh_info(); + if (shndx >= shnum) + { + fprintf(stderr, _("%s: %s: relocation section %u has bad info %u\n"), + program_name, this->name().c_str(), i, shndx); + gold_exit(false); + } + + if (!this->is_section_included(shndx)) + continue; + + // We are scanning relocations in order to fill out the GOT and + // PLT sections. Relocations for sections which are not + // allocated (typically debugging sections) should not add new + // GOT and PLT entries. So we skip them. + typename This::Shdr secshdr(pshdrs + shndx * This::shdr_size); + if ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0) + continue; + + if (shdr.get_sh_link() != this->symtab_shndx_) + { + fprintf(stderr, + _("%s: %s: relocation section %u uses unexpected " + "symbol table %u\n"), + program_name, this->name().c_str(), i, shdr.get_sh_link()); + gold_exit(false); + } + + off_t sh_size = shdr.get_sh_size(); + + unsigned int reloc_size; + if (sh_type == elfcpp::SHT_REL) + reloc_size = elfcpp::Elf_sizes<size>::rel_size; + else + reloc_size = elfcpp::Elf_sizes<size>::rela_size; + if (reloc_size != shdr.get_sh_entsize()) + { + fprintf(stderr, + _("%s: %s: unexpected entsize for reloc section %u: " + "%lu != %u"), + program_name, this->name().c_str(), i, + static_cast<unsigned long>(shdr.get_sh_entsize()), + reloc_size); + gold_exit(false); + } + + size_t reloc_count = sh_size / reloc_size; + if (reloc_count * reloc_size != sh_size) + { + fprintf(stderr, _("%s: %s: reloc section %u size %lu uneven"), + program_name, this->name().c_str(), i, + static_cast<unsigned long>(sh_size)); + gold_exit(false); + } + + rd->relocs.push_back(Section_relocs()); + Section_relocs& sr(rd->relocs.back()); + sr.reloc_shndx = i; + sr.data_shndx = shndx; + sr.contents = this->get_lasting_view(shdr.get_sh_offset(), sh_size); + sr.sh_type = sh_type; + sr.reloc_count = reloc_count; + } + + // Read the local symbols. + gold_assert(this->symtab_shndx_ != -1U); + if (this->symtab_shndx_ == 0 || this->local_symbol_count_ == 0) + rd->local_symbols = NULL; + else + { + typename This::Shdr symtabshdr(pshdrs + + this->symtab_shndx_ * This::shdr_size); + gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); + const int sym_size = This::sym_size; + const unsigned int loccount = this->local_symbol_count_; + gold_assert(loccount == symtabshdr.get_sh_info()); + off_t locsize = loccount * sym_size; + rd->local_symbols = this->get_lasting_view(symtabshdr.get_sh_offset(), + locsize); + } +} + +// Scan the relocs and adjust the symbol table. This looks for +// relocations which require GOT/PLT/COPY relocations. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Read_relocs_data* rd) +{ + Sized_target<size, big_endian>* target = this->sized_target(); + + const unsigned char* local_symbols; + if (rd->local_symbols == NULL) + local_symbols = NULL; + else + local_symbols = rd->local_symbols->data(); + + for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin(); + p != rd->relocs.end(); + ++p) + { + target->scan_relocs(options, symtab, layout, this, p->data_shndx, + p->sh_type, p->contents->data(), p->reloc_count, + this->local_symbol_count_, + local_symbols, + this->symbols_); + delete p->contents; + p->contents = NULL; + } + + if (rd->local_symbols != NULL) + { + delete rd->local_symbols; + rd->local_symbols = NULL; + } +} + +// Relocate the input sections and write out the local symbols. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Layout* layout, + Output_file* of) +{ + unsigned int shnum = this->shnum(); + + // Read the section headers. + const unsigned char* pshdrs = this->get_view(this->elf_file_.shoff(), + shnum * This::shdr_size); + + Views views; + views.resize(shnum); + + // Make two passes over the sections. The first one copies the + // section data to the output file. The second one applies + // relocations. + + this->write_sections(pshdrs, of, &views); + + // Apply relocations. + + this->relocate_sections(options, symtab, layout, pshdrs, &views); + + // Write out the accumulated views. + for (unsigned int i = 1; i < shnum; ++i) + { + if (views[i].view != NULL) + of->write_output_view(views[i].offset, views[i].view_size, + views[i].view); + } + + // Write out the local symbols. + this->write_local_symbols(of, layout->sympool()); +} + +// Write section data to the output file. PSHDRS points to the +// section headers. Record the views in *PVIEWS for use when +// relocating. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs, + Output_file* of, + Views* pviews) +{ + unsigned int shnum = this->shnum(); + std::vector<Map_to_output>& map_sections(this->map_to_output()); + + const unsigned char* p = pshdrs + This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) + { + View_size* pvs = &(*pviews)[i]; + + pvs->view = NULL; + + if (map_sections[i].offset == -1) + continue; + + const Output_section* os = map_sections[i].output_section; + if (os == NULL) + continue; + + typename This::Shdr shdr(p); + + if (shdr.get_sh_type() == elfcpp::SHT_NOBITS) + continue; + + off_t start = os->offset() + map_sections[i].offset; + off_t sh_size = shdr.get_sh_size(); + + if (sh_size == 0) + continue; + + gold_assert(map_sections[i].offset >= 0 + && map_sections[i].offset + sh_size <= os->data_size()); + + unsigned char* view = of->get_output_view(start, sh_size); + this->read(shdr.get_sh_offset(), sh_size, view); + + pvs->view = view; + pvs->address = os->address() + map_sections[i].offset; + pvs->offset = start; + pvs->view_size = sh_size; + } +} + +// Relocate section data. VIEWS points to the section data as views +// in the output file. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::relocate_sections( + const General_options& options, + const Symbol_table* symtab, + const Layout* layout, + const unsigned char* pshdrs, + Views* pviews) +{ + unsigned int shnum = this->shnum(); + Sized_target<size, big_endian>* target = this->sized_target(); + + Relocate_info<size, big_endian> relinfo; + relinfo.options = &options; + relinfo.symtab = symtab; + relinfo.layout = layout; + relinfo.object = this; + relinfo.local_symbol_count = this->local_symbol_count_; + relinfo.local_values = &this->local_values_; + relinfo.symbols = this->symbols_; + + const unsigned char* p = pshdrs + This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) + { + typename This::Shdr shdr(p); + + unsigned int sh_type = shdr.get_sh_type(); + if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA) + continue; + + unsigned int index = shdr.get_sh_info(); + if (index >= this->shnum()) + { + fprintf(stderr, _("%s: %s: relocation section %u has bad info %u\n"), + program_name, this->name().c_str(), i, index); + gold_exit(false); + } + + if (!this->is_section_included(index)) + { + // This relocation section is against a section which we + // discarded. + continue; + } + + gold_assert((*pviews)[index].view != NULL); + + if (shdr.get_sh_link() != this->symtab_shndx_) + { + fprintf(stderr, + _("%s: %s: relocation section %u uses unexpected " + "symbol table %u\n"), + program_name, this->name().c_str(), i, shdr.get_sh_link()); + gold_exit(false); + } + + off_t sh_size = shdr.get_sh_size(); + const unsigned char* prelocs = this->get_view(shdr.get_sh_offset(), + sh_size); + + unsigned int reloc_size; + if (sh_type == elfcpp::SHT_REL) + reloc_size = elfcpp::Elf_sizes<size>::rel_size; + else + reloc_size = elfcpp::Elf_sizes<size>::rela_size; + + if (reloc_size != shdr.get_sh_entsize()) + { + fprintf(stderr, + _("%s: %s: unexpected entsize for reloc section %u: " + "%lu != %u"), + program_name, this->name().c_str(), i, + static_cast<unsigned long>(shdr.get_sh_entsize()), + reloc_size); + gold_exit(false); + } + + size_t reloc_count = sh_size / reloc_size; + if (reloc_count * reloc_size != sh_size) + { + fprintf(stderr, _("%s: %s: reloc section %u size %lu uneven"), + program_name, this->name().c_str(), i, + static_cast<unsigned long>(sh_size)); + gold_exit(false); + } + + relinfo.reloc_shndx = i; + relinfo.data_shndx = index; + target->relocate_section(&relinfo, + sh_type, + prelocs, + reloc_count, + (*pviews)[index].view, + (*pviews)[index].address, + (*pviews)[index].view_size); + } +} + +// Copy_relocs::Copy_reloc_entry methods. + +// Return whether we should emit this reloc. We should emit it if the +// symbol is still defined in a dynamic object. If we should not emit +// it, we clear it, to save ourselves the test next time. + +template<int size, bool big_endian> +bool +Copy_relocs<size, big_endian>::Copy_reloc_entry::should_emit() +{ + if (this->sym_ == NULL) + return false; + if (this->sym_->is_from_dynobj()) + return true; + this->sym_ = NULL; + return false; +} + +// Emit a reloc into a SHT_REL section. + +template<int size, bool big_endian> +void +Copy_relocs<size, big_endian>::Copy_reloc_entry::emit( + Output_data_reloc<elfcpp::SHT_REL, true, size, big_endian>* reloc_data) +{ + this->sym_->set_needs_dynsym_entry(); + reloc_data->add_global(this->sym_, this->reloc_type_, this->relobj_, + this->shndx_, this->address_); +} + +// Emit a reloc into a SHT_RELA section. + +template<int size, bool big_endian> +void +Copy_relocs<size, big_endian>::Copy_reloc_entry::emit( + Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian>* reloc_data) +{ + this->sym_->set_needs_dynsym_entry(); + reloc_data->add_global(this->sym_, this->reloc_type_, this->relobj_, + this->shndx_, this->address_, this->addend_); +} + +// Copy_relocs methods. + +// Return whether we need a COPY reloc for a relocation against GSYM. +// The relocation is being applied to section SHNDX in OBJECT. + +template<int size, bool big_endian> +bool +Copy_relocs<size, big_endian>::need_copy_reloc( + const General_options*, + Relobj* object, + unsigned int shndx, + Sized_symbol<size>* sym) +{ + // FIXME: Handle -z nocopyrelocs. + + if (sym->symsize() == 0) + return false; + + // If this is a readonly section, then we need a COPY reloc. + // Otherwise we can use a dynamic reloc. + if ((object->section_flags(shndx) & elfcpp::SHF_WRITE) == 0) + return true; + + return false; +} + +// Save a Rel reloc. + +template<int size, bool big_endian> +void +Copy_relocs<size, big_endian>::save( + Symbol* sym, + Relobj* relobj, + unsigned int shndx, + const elfcpp::Rel<size, big_endian>& rel) +{ + unsigned int reloc_type = elfcpp::elf_r_type<size>(rel.get_r_info()); + this->entries_.push_back(Copy_reloc_entry(sym, reloc_type, relobj, shndx, + rel.get_r_offset(), 0)); +} + +// Save a Rela reloc. + +template<int size, bool big_endian> +void +Copy_relocs<size, big_endian>::save( + Symbol* sym, + Relobj* relobj, + unsigned int shndx, + const elfcpp::Rela<size, big_endian>& rela) +{ + unsigned int reloc_type = elfcpp::elf_r_type<size>(rela.get_r_info()); + this->entries_.push_back(Copy_reloc_entry(sym, reloc_type, relobj, shndx, + rela.get_r_offset(), + rela.get_r_addend())); +} + +// Return whether there are any relocs to emit. We don't want to emit +// a reloc if the symbol is no longer defined in a dynamic object. + +template<int size, bool big_endian> +bool +Copy_relocs<size, big_endian>::any_to_emit() +{ + for (typename Copy_reloc_entries::iterator p = this->entries_.begin(); + p != this->entries_.end(); + ++p) + { + if (p->should_emit()) + return true; + } + return false; +} + +// Emit relocs. + +template<int size, bool big_endian> +template<int sh_type> +void +Copy_relocs<size, big_endian>::emit( + Output_data_reloc<sh_type, true, size, big_endian>* reloc_data) +{ + for (typename Copy_reloc_entries::iterator p = this->entries_.begin(); + p != this->entries_.end(); + ++p) + { + if (p->should_emit()) + p->emit(reloc_data); + } +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones for implemented targets. + +template +void +Sized_relobj<32, false>::do_read_relocs(Read_relocs_data* rd); + +template +void +Sized_relobj<32, true>::do_read_relocs(Read_relocs_data* rd); + +template +void +Sized_relobj<64, false>::do_read_relocs(Read_relocs_data* rd); + +template +void +Sized_relobj<64, true>::do_read_relocs(Read_relocs_data* rd); + +template +void +Sized_relobj<32, false>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Read_relocs_data* rd); + +template +void +Sized_relobj<32, true>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Read_relocs_data* rd); + +template +void +Sized_relobj<64, false>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Read_relocs_data* rd); + +template +void +Sized_relobj<64, true>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Read_relocs_data* rd); + +template +void +Sized_relobj<32, false>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Layout* layout, + Output_file* of); + +template +void +Sized_relobj<32, true>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Layout* layout, + Output_file* of); + +template +void +Sized_relobj<64, false>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Layout* layout, + Output_file* of); + +template +void +Sized_relobj<64, true>::do_relocate(const General_options& options, + const Symbol_table* symtab, + const Layout* layout, + Output_file* of); + +template +class Copy_relocs<32, false>; + +template +class Copy_relocs<32, true>; + +template +class Copy_relocs<64, false>; + +template +class Copy_relocs<64, true>; + +template +void +Copy_relocs<32, false>::emit<elfcpp::SHT_REL>( + Output_data_reloc<elfcpp::SHT_REL, true, 32, false>*); + +template +void +Copy_relocs<32, true>::emit<elfcpp::SHT_REL>( + Output_data_reloc<elfcpp::SHT_REL, true, 32, true>*); + +template +void +Copy_relocs<64, false>::emit<elfcpp::SHT_REL>( + Output_data_reloc<elfcpp::SHT_REL, true, 64, false>*); + +template +void +Copy_relocs<64, true>::emit<elfcpp::SHT_REL>( + Output_data_reloc<elfcpp::SHT_REL, true, 64, true>*); + +template +void +Copy_relocs<32, false>::emit<elfcpp::SHT_RELA>( + Output_data_reloc<elfcpp::SHT_RELA , true, 32, false>*); + +template +void +Copy_relocs<32, true>::emit<elfcpp::SHT_RELA>( + Output_data_reloc<elfcpp::SHT_RELA, true, 32, true>*); + +template +void +Copy_relocs<64, false>::emit<elfcpp::SHT_RELA>( + Output_data_reloc<elfcpp::SHT_RELA, true, 64, false>*); + +template +void +Copy_relocs<64, true>::emit<elfcpp::SHT_RELA>( + Output_data_reloc<elfcpp::SHT_RELA, true, 64, true>*); + +} // End namespace gold. diff --git a/gold/reloc.h b/gold/reloc.h new file mode 100644 index 000000000000..1aa0d896717f --- /dev/null +++ b/gold/reloc.h @@ -0,0 +1,409 @@ +// reloc.h -- relocate input files for gold -*- C++ -*- + +#ifndef GOLD_RELOC_H +#define GOLD_RELOC_H + +#include <byteswap.h> + +#include "workqueue.h" + +namespace gold +{ + +class General_options; +class Relobj; +class Read_relocs_data; +class Symbol; +class Layout; + +template<int size> +class Sized_symbol; + +template<int size, bool big_endian> +class Sized_relobj; + +template<int size> +class Symbol_value; + +template<int sh_type, bool dynamic, int size, bool big_endian> +class Output_data_reloc; + +// A class to read the relocations for an object file, and then queue +// up a task to see if they require any GOT/PLT/COPY relocations in +// the symbol table. + +class Read_relocs : public Task +{ + public: + // SYMTAB_LOCK is used to lock the symbol table. BLOCKER should be + // unblocked when the Scan_relocs task completes. + Read_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Relobj* object, Task_token* symtab_lock, + Task_token* blocker) + : options_(options), symtab_(symtab), layout_(layout), object_(object), + symtab_lock_(symtab_lock), blocker_(blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + const General_options& options_; + Symbol_table* symtab_; + Layout* layout_; + Relobj* object_; + Task_token* symtab_lock_; + Task_token* blocker_; +}; + +// Scan the relocations for an object to see if they require any +// GOT/PLT/COPY relocations. + +class Scan_relocs : public Task +{ + public: + // SYMTAB_LOCK is used to lock the symbol table. BLOCKER should be + // unblocked when the task completes. + Scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Relobj* object, Read_relocs_data* rd, + Task_token* symtab_lock, Task_token* blocker) + : options_(options), symtab_(symtab), layout_(layout), object_(object), + rd_(rd), symtab_lock_(symtab_lock), blocker_(blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Scan_relocs_locker; + + const General_options& options_; + Symbol_table* symtab_; + Layout* layout_; + Relobj* object_; + Read_relocs_data* rd_; + Task_token* symtab_lock_; + Task_token* blocker_; +}; + +// A class to perform all the relocations for an object file. + +class Relocate_task : public Task +{ + public: + Relocate_task(const General_options& options, const Symbol_table* symtab, + const Layout* layout, Relobj* object, Output_file* of, + Task_token* final_blocker) + : options_(options), symtab_(symtab), layout_(layout), object_(object), + of_(of), final_blocker_(final_blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Relocate_locker; + + const General_options& options_; + const Symbol_table* symtab_; + const Layout* layout_; + Relobj* object_; + Output_file* of_; + Task_token* final_blocker_; +}; + +// Standard relocation routines which are used on many targets. Here +// SIZE and BIG_ENDIAN refer to the target, not the relocation type. + +template<int size, bool big_endian> +class Relocate_functions +{ +private: + // Do a simple relocation with the addend in the section contents. + // VALSIZE is the size of the value. + template<int valsize> + static inline void + rel(unsigned char* view, + typename elfcpp::Swap<valsize, big_endian>::Valtype value) + { + typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype; + Valtype* wv = reinterpret_cast<Valtype*>(view); + Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv); + elfcpp::Swap<valsize, big_endian>::writeval(wv, x + value); + } + + // Do a simple relocation using a Symbol_value with the addend in + // the section contents. VALSIZE is the size of the value to + // relocate. + template<int valsize> + static inline void + rel(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval) + { + typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype; + Valtype* wv = reinterpret_cast<Valtype*>(view); + Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv); + x = psymval->value(object, x); + elfcpp::Swap<valsize, big_endian>::writeval(wv, x); + } + + // Do a simple PC relative relocation with the addend in the section + // contents. VALSIZE is the size of the value. + template<int valsize> + static inline void + pcrel(unsigned char* view, + typename elfcpp::Swap<valsize, big_endian>::Valtype value, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { + typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype; + Valtype* wv = reinterpret_cast<Valtype*>(view); + Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv); + elfcpp::Swap<valsize, big_endian>::writeval(wv, x + value - address); + } + + // Do a simple PC relative relocation with a Symbol_value with the + // addend in the section contents. VALSIZE is the size of the + // value. + template<int valsize> + static inline void + pcrel(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { + typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype; + Valtype* wv = reinterpret_cast<Valtype*>(view); + Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv); + x = psymval->value(object, x); + elfcpp::Swap<valsize, big_endian>::writeval(wv, x - address); + } + + typedef Relocate_functions<size, big_endian> This; + +public: + // Do a simple 8-bit REL relocation with the addend in the section + // contents. + static inline void + rel8(unsigned char* view, unsigned char value) + { This::template rel<8>(view, value); } + + static inline void + rel8(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval) + { This::template rel<8>(view, object, psymval); } + + // Do a simple 8-bit PC relative relocation with the addend in the + // section contents. + static inline void + pcrel8(unsigned char* view, unsigned char value, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { This::template pcrel<8>(view, value, address); } + + static inline void + pcrel8(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { This::template pcrel<8>(view, object, psymval, address); } + + // Do a simple 16-bit REL relocation with the addend in the section + // contents. + static inline void + rel16(unsigned char* view, elfcpp::Elf_Half value) + { This::template rel<16>(view, value); } + + static inline void + rel16(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval) + { This::template rel<16>(view, object, psymval); } + + // Do a simple 32-bit PC relative REL relocation with the addend in + // the section contents. + static inline void + pcrel16(unsigned char* view, elfcpp::Elf_Word value, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { This::template pcrel<16>(view, value, address); } + + static inline void + pcrel16(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { This::template pcrel<16>(view, object, psymval, address); } + + // Do a simple 32-bit REL relocation with the addend in the section + // contents. + static inline void + rel32(unsigned char* view, elfcpp::Elf_Word value) + { This::template rel<32>(view, value); } + + static inline void + rel32(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval) + { This::template rel<32>(view, object, psymval); } + + // Do a simple 32-bit PC relative REL relocation with the addend in + // the section contents. + static inline void + pcrel32(unsigned char* view, elfcpp::Elf_Word value, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { This::template pcrel<32>(view, value, address); } + + static inline void + pcrel32(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { This::template pcrel<32>(view, object, psymval, address); } + + // Do a simple 64-bit REL relocation with the addend in the section + // contents. + static inline void + rel64(unsigned char* view, elfcpp::Elf_Xword value) + { This::template rel<64>(view, value); } + + static inline void + rel64(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval) + { This::template rel<64>(view, object, psymval); } + + // Do a simple 64-bit PC relative REL relocation with the addend in + // the section contents. + static inline void + pcrel64(unsigned char* view, elfcpp::Elf_Xword value, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { This::template pcrel<64>(view, value, address); } + + static inline void + pcrel64(unsigned char* view, + const Sized_relobj<size, big_endian>* object, + const Symbol_value<size>* psymval, + typename elfcpp::Elf_types<size>::Elf_Addr address) + { This::template pcrel<64>(view, object, psymval, address); } +}; + +// We try to avoid COPY relocations when possible. A COPY relocation +// may be required when an executable refers to a variable defined in +// a shared library. COPY relocations are problematic because they +// tie the executable to the exact size of the variable in the shared +// library. We can avoid them if all the references to the variable +// are in a writeable section. In that case we can simply use dynamic +// relocations. However, when scanning relocs, we don't know when we +// see the relocation whether we will be forced to use a COPY +// relocation or not. So we have to save the relocation during the +// reloc scanning, and then emit it as a dynamic relocation if +// necessary. This class implements that. It is used by the target +// specific code. + +template<int size, bool big_endian> +class Copy_relocs +{ + public: + Copy_relocs() + : entries_() + { } + + // Return whether we need a COPY reloc for a reloc against GSYM, + // which is being applied to section SHNDX in OBJECT. + static bool + need_copy_reloc(const General_options*, Relobj* object, unsigned int shndx, + Sized_symbol<size>* gsym); + + // Save a Rel against SYM for possible emission later. SHNDX is the + // index of the section to which the reloc is being applied. + void + save(Symbol* sym, Relobj*, unsigned int shndx, + const elfcpp::Rel<size, big_endian>&); + + // Save a Rela against SYM for possible emission later. + void + save(Symbol* sym, Relobj*, unsigned int shndx, + const elfcpp::Rela<size, big_endian>&); + + // Return whether there are any relocs to emit. This also discards + // entries which need not be emitted. + bool + any_to_emit(); + + // Emit relocs for each symbol which did not get a COPY reloc (i.e., + // is still defined in the dynamic object). + template<int sh_type> + void + emit(Output_data_reloc<sh_type, true, size, big_endian>*); + + private: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + typedef typename elfcpp::Elf_types<size>::Elf_Addr Addend; + + // This POD class holds the entries we are saving. + class Copy_reloc_entry + { + public: + Copy_reloc_entry(Symbol* sym, unsigned int reloc_type, + Relobj* relobj, unsigned int shndx, + Address address, Addend addend) + : sym_(sym), reloc_type_(reloc_type), relobj_(relobj), + shndx_(shndx), address_(address), addend_(addend) + { } + + // Return whether we should emit this reloc. If we should not + // emit, we clear it. + bool + should_emit(); + + // Emit this reloc. + + void + emit(Output_data_reloc<elfcpp::SHT_REL, true, size, big_endian>*); + + void + emit(Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian>*); + + private: + Symbol* sym_; + unsigned int reloc_type_; + Relobj* relobj_; + unsigned int shndx_; + Address address_; + Addend addend_; + }; + + // A list of relocs to be saved. + typedef std::vector<Copy_reloc_entry> Copy_reloc_entries; + + // The list of relocs we are saving. + Copy_reloc_entries entries_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_RELOC_H) diff --git a/gold/resolve.cc b/gold/resolve.cc new file mode 100644 index 000000000000..1d874863d628 --- /dev/null +++ b/gold/resolve.cc @@ -0,0 +1,560 @@ +// resolve.cc -- symbol resolution for gold + +#include "gold.h" + +#include "elfcpp.h" +#include "target.h" +#include "object.h" +#include "symtab.h" + +namespace gold +{ + +// Symbol methods used in this file. + +// Override the fields in Symbol. + +template<int size, bool big_endian> +void +Symbol::override_base(const elfcpp::Sym<size, big_endian>& sym, + Object* object, const char* version) +{ + gold_assert(this->source_ == FROM_OBJECT); + this->u_.from_object.object = object; + if (version != NULL && this->version() != version) + { + gold_assert(this->version() == NULL); + this->version_ = version; + } + // FIXME: Handle SHN_XINDEX. + this->u_.from_object.shndx = sym.get_st_shndx(); + this->type_ = sym.get_st_type(); + this->binding_ = sym.get_st_bind(); + this->visibility_ = sym.get_st_visibility(); + this->nonvis_ = sym.get_st_nonvis(); +} + +// Override the fields in Sized_symbol. + +template<int size> +template<bool big_endian> +void +Sized_symbol<size>::override(const elfcpp::Sym<size, big_endian>& sym, + Object* object, const char* version) +{ + this->override_base(sym, object, version); + this->value_ = sym.get_st_value(); + this->symsize_ = sym.get_st_size(); +} + +// Resolve a symbol. This is called the second and subsequent times +// we see a symbol. TO is the pre-existing symbol. SYM is the new +// symbol, seen in OBJECT. VERSION of the version of SYM. + +template<int size, bool big_endian> +void +Symbol_table::resolve(Sized_symbol<size>* to, + const elfcpp::Sym<size, big_endian>& sym, + Object* object, const char* version) +{ + if (object->target()->has_resolve()) + { + Sized_target<size, big_endian>* sized_target; + sized_target = object->sized_target + SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + SELECT_SIZE_ENDIAN_ONLY(size, big_endian)); + sized_target->resolve(to, sym, object, version); + return; + } + + // Build a little code for each symbol. + // Bit 0: 0 for global, 1 for weak. + // Bit 1: 0 for regular object, 1 for shared object + // Bits 2-3: 0 for normal, 1 for undefined, 2 for common + // This gives us values from 0 to 11: + + enum + { + DEF = 0, + WEAK_DEF = 1, + DYN_DEF = 2, + DYN_WEAK_DEF = 3, + UNDEF = 4, + WEAK_UNDEF = 5, + DYN_UNDEF = 6, + DYN_WEAK_UNDEF = 7, + COMMON = 8, + WEAK_COMMON = 9, + DYN_COMMON = 10, + DYN_WEAK_COMMON = 11 + }; + + int tobits; + switch (to->binding()) + { + case elfcpp::STB_GLOBAL: + tobits = 0; + break; + + case elfcpp::STB_WEAK: + tobits = 1; + break; + + case elfcpp::STB_LOCAL: + // We should only see externally visible symbols in the symbol + // table. + gold_unreachable(); + + default: + // Any target which wants to handle STB_LOOS, etc., needs to + // define a resolve method. + gold_unreachable(); + } + + if (to->source() == Symbol::FROM_OBJECT + && to->object()->is_dynamic()) + tobits |= (1 << 1); + + switch (to->shndx()) + { + case elfcpp::SHN_UNDEF: + tobits |= (1 << 2); + break; + + case elfcpp::SHN_COMMON: + tobits |= (2 << 2); + break; + + default: + if (to->type() == elfcpp::STT_COMMON) + tobits |= (2 << 2); + break; + } + + int frombits; + switch (sym.get_st_bind()) + { + case elfcpp::STB_GLOBAL: + frombits = 0; + break; + + case elfcpp::STB_WEAK: + frombits = 1; + break; + + case elfcpp::STB_LOCAL: + fprintf(stderr, + _("%s: %s: invalid STB_LOCAL symbol %s in external symbols\n"), + program_name, object->name().c_str(), to->name()); + gold_exit(false); + + default: + fprintf(stderr, + _("%s: %s: unsupported symbol binding %d for symbol %s\n"), + program_name, object->name().c_str(), + static_cast<int>(sym.get_st_bind()), to->name()); + gold_exit(false); + } + + if (!object->is_dynamic()) + { + // Record that we've seen this symbol in a regular object. + to->set_in_reg(); + } + else + { + frombits |= (1 << 1); + + // Record that we've seen this symbol in a dynamic object. + to->set_in_dyn(); + } + + switch (sym.get_st_shndx()) + { + case elfcpp::SHN_UNDEF: + frombits |= (1 << 2); + break; + + case elfcpp::SHN_COMMON: + frombits |= (2 << 2); + break; + + default: + if (sym.get_st_type() == elfcpp::STT_COMMON) + frombits |= (2 << 2); + break; + } + + if ((tobits & (1 << 1)) != (frombits & (1 << 1))) + { + // This symbol is seen in both a dynamic object and a regular + // object. That means that we need the symbol to go into the + // dynamic symbol table, so that the dynamic linker can use the + // regular symbol to override or define the dynamic symbol. + to->set_needs_dynsym_entry(); + } + + // FIXME: Warn if either but not both of TO and SYM are STT_TLS. + + // We use a giant switch table for symbol resolution. This code is + // unwieldy, but: 1) it is efficient; 2) we definitely handle all + // cases; 3) it is easy to change the handling of a particular case. + // The alternative would be a series of conditionals, but it is easy + // to get the ordering wrong. This could also be done as a table, + // but that is no easier to understand than this large switch + // statement. + + switch (tobits * 16 + frombits) + { + case DEF * 16 + DEF: + // Two definitions of the same symbol. + fprintf(stderr, "%s: %s: multiple definition of %s\n", + program_name, object->name().c_str(), to->name()); + // FIXME: Report locations. Record that we have seen an error. + return; + + case WEAK_DEF * 16 + DEF: + // We've seen a weak definition, and now we see a strong + // definition. In the original SVR4 linker, this was treated as + // a multiple definition error. In the Solaris linker and the + // GNU linker, a weak definition followed by a regular + // definition causes the weak definition to be overridden. We + // are currently compatible with the GNU linker. In the future + // we should add a target specific option to change this. + // FIXME. + to->override(sym, object, version); + return; + + case DYN_DEF * 16 + DEF: + case DYN_WEAK_DEF * 16 + DEF: + // We've seen a definition in a dynamic object, and now we see a + // definition in a regular object. The definition in the + // regular object overrides the definition in the dynamic + // object. + to->override(sym, object, version); + return; + + case UNDEF * 16 + DEF: + case WEAK_UNDEF * 16 + DEF: + case DYN_UNDEF * 16 + DEF: + case DYN_WEAK_UNDEF * 16 + DEF: + // We've seen an undefined reference, and now we see a + // definition. We use the definition. + to->override(sym, object, version); + return; + + case COMMON * 16 + DEF: + case WEAK_COMMON * 16 + DEF: + case DYN_COMMON * 16 + DEF: + case DYN_WEAK_COMMON * 16 + DEF: + // We've seen a common symbol and now we see a definition. The + // definition overrides. FIXME: We should optionally issue, version a + // warning. + to->override(sym, object, version); + return; + + case DEF * 16 + WEAK_DEF: + case WEAK_DEF * 16 + WEAK_DEF: + // We've seen a definition and now we see a weak definition. We + // ignore the new weak definition. + return; + + case DYN_DEF * 16 + WEAK_DEF: + case DYN_WEAK_DEF * 16 + WEAK_DEF: + // We've seen a dynamic definition and now we see a regular weak + // definition. The regular weak definition overrides. + to->override(sym, object, version); + return; + + case UNDEF * 16 + WEAK_DEF: + case WEAK_UNDEF * 16 + WEAK_DEF: + case DYN_UNDEF * 16 + WEAK_DEF: + case DYN_WEAK_UNDEF * 16 + WEAK_DEF: + // A weak definition of a currently undefined symbol. + to->override(sym, object, version); + return; + + case COMMON * 16 + WEAK_DEF: + case WEAK_COMMON * 16 + WEAK_DEF: + // A weak definition does not override a common definition. + return; + + case DYN_COMMON * 16 + WEAK_DEF: + case DYN_WEAK_COMMON * 16 + WEAK_DEF: + // A weak definition does override a definition in a dynamic + // object. FIXME: We should optionally issue a warning. + to->override(sym, object, version); + return; + + case DEF * 16 + DYN_DEF: + case WEAK_DEF * 16 + DYN_DEF: + case DYN_DEF * 16 + DYN_DEF: + case DYN_WEAK_DEF * 16 + DYN_DEF: + // Ignore a dynamic definition if we already have a definition. + return; + + case UNDEF * 16 + DYN_DEF: + case WEAK_UNDEF * 16 + DYN_DEF: + case DYN_UNDEF * 16 + DYN_DEF: + case DYN_WEAK_UNDEF * 16 + DYN_DEF: + // Use a dynamic definition if we have a reference. + to->override(sym, object, version); + return; + + case COMMON * 16 + DYN_DEF: + case WEAK_COMMON * 16 + DYN_DEF: + case DYN_COMMON * 16 + DYN_DEF: + case DYN_WEAK_COMMON * 16 + DYN_DEF: + // Ignore a dynamic definition if we already have a common + // definition. + return; + + case DEF * 16 + DYN_WEAK_DEF: + case WEAK_DEF * 16 + DYN_WEAK_DEF: + case DYN_DEF * 16 + DYN_WEAK_DEF: + case DYN_WEAK_DEF * 16 + DYN_WEAK_DEF: + // Ignore a weak dynamic definition if we already have a + // definition. + return; + + case UNDEF * 16 + DYN_WEAK_DEF: + case WEAK_UNDEF * 16 + DYN_WEAK_DEF: + case DYN_UNDEF * 16 + DYN_WEAK_DEF: + case DYN_WEAK_UNDEF * 16 + DYN_WEAK_DEF: + // Use a weak dynamic definition if we have a reference. + to->override(sym, object, version); + return; + + case COMMON * 16 + DYN_WEAK_DEF: + case WEAK_COMMON * 16 + DYN_WEAK_DEF: + case DYN_COMMON * 16 + DYN_WEAK_DEF: + case DYN_WEAK_COMMON * 16 + DYN_WEAK_DEF: + // Ignore a weak dynamic definition if we already have a common + // definition. + return; + + case DEF * 16 + UNDEF: + case WEAK_DEF * 16 + UNDEF: + case DYN_DEF * 16 + UNDEF: + case DYN_WEAK_DEF * 16 + UNDEF: + case UNDEF * 16 + UNDEF: + // A new undefined reference tells us nothing. + return; + + case WEAK_UNDEF * 16 + UNDEF: + case DYN_UNDEF * 16 + UNDEF: + case DYN_WEAK_UNDEF * 16 + UNDEF: + // A strong undef overrides a dynamic or weak undef. + to->override(sym, object, version); + return; + + case COMMON * 16 + UNDEF: + case WEAK_COMMON * 16 + UNDEF: + case DYN_COMMON * 16 + UNDEF: + case DYN_WEAK_COMMON * 16 + UNDEF: + // A new undefined reference tells us nothing. + return; + + case DEF * 16 + WEAK_UNDEF: + case WEAK_DEF * 16 + WEAK_UNDEF: + case DYN_DEF * 16 + WEAK_UNDEF: + case DYN_WEAK_DEF * 16 + WEAK_UNDEF: + case UNDEF * 16 + WEAK_UNDEF: + case WEAK_UNDEF * 16 + WEAK_UNDEF: + case DYN_UNDEF * 16 + WEAK_UNDEF: + case DYN_WEAK_UNDEF * 16 + WEAK_UNDEF: + case COMMON * 16 + WEAK_UNDEF: + case WEAK_COMMON * 16 + WEAK_UNDEF: + case DYN_COMMON * 16 + WEAK_UNDEF: + case DYN_WEAK_COMMON * 16 + WEAK_UNDEF: + // A new weak undefined reference tells us nothing. + return; + + case DEF * 16 + DYN_UNDEF: + case WEAK_DEF * 16 + DYN_UNDEF: + case DYN_DEF * 16 + DYN_UNDEF: + case DYN_WEAK_DEF * 16 + DYN_UNDEF: + case UNDEF * 16 + DYN_UNDEF: + case WEAK_UNDEF * 16 + DYN_UNDEF: + case DYN_UNDEF * 16 + DYN_UNDEF: + case DYN_WEAK_UNDEF * 16 + DYN_UNDEF: + case COMMON * 16 + DYN_UNDEF: + case WEAK_COMMON * 16 + DYN_UNDEF: + case DYN_COMMON * 16 + DYN_UNDEF: + case DYN_WEAK_COMMON * 16 + DYN_UNDEF: + // A new dynamic undefined reference tells us nothing. + return; + + case DEF * 16 + DYN_WEAK_UNDEF: + case WEAK_DEF * 16 + DYN_WEAK_UNDEF: + case DYN_DEF * 16 + DYN_WEAK_UNDEF: + case DYN_WEAK_DEF * 16 + DYN_WEAK_UNDEF: + case UNDEF * 16 + DYN_WEAK_UNDEF: + case WEAK_UNDEF * 16 + DYN_WEAK_UNDEF: + case DYN_UNDEF * 16 + DYN_WEAK_UNDEF: + case DYN_WEAK_UNDEF * 16 + DYN_WEAK_UNDEF: + case COMMON * 16 + DYN_WEAK_UNDEF: + case WEAK_COMMON * 16 + DYN_WEAK_UNDEF: + case DYN_COMMON * 16 + DYN_WEAK_UNDEF: + case DYN_WEAK_COMMON * 16 + DYN_WEAK_UNDEF: + // A new weak dynamic undefined reference tells us nothing. + return; + + case DEF * 16 + COMMON: + // A common symbol does not override a definition. + return; + + case WEAK_DEF * 16 + COMMON: + case DYN_DEF * 16 + COMMON: + case DYN_WEAK_DEF * 16 + COMMON: + // A common symbol does override a weak definition or a dynamic + // definition. + to->override(sym, object, version); + return; + + case UNDEF * 16 + COMMON: + case WEAK_UNDEF * 16 + COMMON: + case DYN_UNDEF * 16 + COMMON: + case DYN_WEAK_UNDEF * 16 + COMMON: + // A common symbol is a definition for a reference. + to->override(sym, object, version); + return; + + case COMMON * 16 + COMMON: + // Set the size to the maximum. + if (sym.get_st_size() > to->symsize()) + to->set_symsize(sym.get_st_size()); + return; + + case WEAK_COMMON * 16 + COMMON: + // I'm not sure just what a weak common symbol means, but + // presumably it can be overridden by a regular common symbol. + to->override(sym, object, version); + return; + + case DYN_COMMON * 16 + COMMON: + case DYN_WEAK_COMMON * 16 + COMMON: + { + // Use the real common symbol, but adjust the size if necessary. + typename Sized_symbol<size>::Size_type symsize = to->symsize(); + to->override(sym, object, version); + if (to->symsize() < symsize) + to->set_symsize(symsize); + } + return; + + case DEF * 16 + WEAK_COMMON: + case WEAK_DEF * 16 + WEAK_COMMON: + case DYN_DEF * 16 + WEAK_COMMON: + case DYN_WEAK_DEF * 16 + WEAK_COMMON: + // Whatever a weak common symbol is, it won't override a + // definition. + return; + + case UNDEF * 16 + WEAK_COMMON: + case WEAK_UNDEF * 16 + WEAK_COMMON: + case DYN_UNDEF * 16 + WEAK_COMMON: + case DYN_WEAK_UNDEF * 16 + WEAK_COMMON: + // A weak common symbol is better than an undefined symbol. + to->override(sym, object, version); + return; + + case COMMON * 16 + WEAK_COMMON: + case WEAK_COMMON * 16 + WEAK_COMMON: + case DYN_COMMON * 16 + WEAK_COMMON: + case DYN_WEAK_COMMON * 16 + WEAK_COMMON: + // Ignore a weak common symbol in the presence of a real common + // symbol. + return; + + case DEF * 16 + DYN_COMMON: + case WEAK_DEF * 16 + DYN_COMMON: + case DYN_DEF * 16 + DYN_COMMON: + case DYN_WEAK_DEF * 16 + DYN_COMMON: + // Ignore a dynamic common symbol in the presence of a + // definition. + return; + + case UNDEF * 16 + DYN_COMMON: + case WEAK_UNDEF * 16 + DYN_COMMON: + case DYN_UNDEF * 16 + DYN_COMMON: + case DYN_WEAK_UNDEF * 16 + DYN_COMMON: + // A dynamic common symbol is a definition of sorts. + to->override(sym, object, version); + return; + + case COMMON * 16 + DYN_COMMON: + case WEAK_COMMON * 16 + DYN_COMMON: + case DYN_COMMON * 16 + DYN_COMMON: + case DYN_WEAK_COMMON * 16 + DYN_COMMON: + // Set the size to the maximum. + if (sym.get_st_size() > to->symsize()) + to->set_symsize(sym.get_st_size()); + return; + + case DEF * 16 + DYN_WEAK_COMMON: + case WEAK_DEF * 16 + DYN_WEAK_COMMON: + case DYN_DEF * 16 + DYN_WEAK_COMMON: + case DYN_WEAK_DEF * 16 + DYN_WEAK_COMMON: + // A common symbol is ignored in the face of a definition. + return; + + case UNDEF * 16 + DYN_WEAK_COMMON: + case WEAK_UNDEF * 16 + DYN_WEAK_COMMON: + case DYN_UNDEF * 16 + DYN_WEAK_COMMON: + case DYN_WEAK_UNDEF * 16 + DYN_WEAK_COMMON: + // I guess a weak common symbol is better than a definition. + to->override(sym, object, version); + return; + + case COMMON * 16 + DYN_WEAK_COMMON: + case WEAK_COMMON * 16 + DYN_WEAK_COMMON: + case DYN_COMMON * 16 + DYN_WEAK_COMMON: + case DYN_WEAK_COMMON * 16 + DYN_WEAK_COMMON: + // Set the size to the maximum. + if (sym.get_st_size() > to->symsize()) + to->set_symsize(sym.get_st_size()); + return; + + default: + gold_unreachable(); + } +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones needed for implemented +// targets. + +template +void +Symbol_table::resolve<32, true>( + Sized_symbol<32>* to, + const elfcpp::Sym<32, true>& sym, + Object* object, + const char* version); + +template +void +Symbol_table::resolve<32, false>( + Sized_symbol<32>* to, + const elfcpp::Sym<32, false>& sym, + Object* object, + const char* version); + +template +void +Symbol_table::resolve<64, true>( + Sized_symbol<64>* to, + const elfcpp::Sym<64, true>& sym, + Object* object, + const char* version); + +template +void +Symbol_table::resolve<64, false>( + Sized_symbol<64>* to, + const elfcpp::Sym<64, false>& sym, + Object* object, + const char* version); + +} // End namespace gold. diff --git a/gold/script-c.h b/gold/script-c.h new file mode 100644 index 000000000000..e40488985d0f --- /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 000000000000..f5584d9b01ea --- /dev/null +++ b/gold/script.cc @@ -0,0 +1,1193 @@ +// script.cc -- handle linker scripts for gold. + +#include "gold.h" + +#include <string> +#include <vector> +#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) + { + gold_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) + { + gold_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 + { + gold_assert(this->classification_ == TOKEN_STRING); + return this->value_; + } + + int + operator_value() const + { + gold_assert(this->classification_ == TOKEN_OPERATOR); + return this->opcode_; + } + + int64_t + integer_value() const + { + gold_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: + gold_unreachable(); + + 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 000000000000..de2e5af1a661 --- /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/stringpool.cc b/gold/stringpool.cc new file mode 100644 index 000000000000..34c11b4fc42a --- /dev/null +++ b/gold/stringpool.cc @@ -0,0 +1,386 @@ +// stringpool.cc -- a string pool for gold + +#include "gold.h" + +#include <cstring> +#include <algorithm> +#include <vector> + +#include "output.h" +#include "stringpool.h" + +namespace gold +{ + +template<typename Stringpool_char> +Stringpool_template<Stringpool_char>::Stringpool_template(bool zero_null) + : string_set_(), strings_(), strtab_size_(0), next_index_(1), + zero_null_(zero_null) +{ +} + +template<typename Stringpool_char> +Stringpool_template<Stringpool_char>::~Stringpool_template() +{ + for (typename std::list<Stringdata*>::iterator p = this->strings_.begin(); + p != this->strings_.end(); + ++p) + delete[] reinterpret_cast<char*>(*p); +} + +// Return the length of a string of arbitrary character type. + +template<typename Stringpool_char> +size_t +Stringpool_template<Stringpool_char>::string_length(const Stringpool_char* p) +{ + size_t len = 0; + for (; *p != 0; ++p) + ++len; + return len; +} + +// Specialize string_length for char. Maybe we could just use +// std::char_traits<>::length? + +template<> +inline size_t +Stringpool_template<char>::string_length(const char* p) +{ + return strlen(p); +} + +// Equality comparison function. + +template<typename Stringpool_char> +bool +Stringpool_template<Stringpool_char>::Stringpool_eq::operator()( + const Stringpool_char* s1, + const Stringpool_char* s2) const +{ + while (*s1 != 0) + if (*s1++ != *s2++) + return false; + return *s2 == 0; +} + +// Specialize equality comparison for char. + +template<> +bool +Stringpool_template<char>::Stringpool_eq::operator()(const char* s1, + const char* s2) const +{ + return strcmp(s1, s2) == 0; +} + +// Hash function. + +template<typename Stringpool_char> +size_t +Stringpool_template<Stringpool_char>::Stringpool_hash::operator()( + const Stringpool_char* s) const +{ + // Fowler/Noll/Vo (FNV) hash (type FNV-1a). + if (sizeof(size_t) == 8) + { + size_t result = static_cast<size_t>(14695981039346656037ULL); + while (*s != 0) + { + const char* p = reinterpret_cast<const char*>(s); + for (size_t i = 0; i < sizeof(Stringpool_char); ++i) + { + result &= (size_t) *p++; + result *= 1099511628211ULL; + } + ++s; + } + return result; + } + else + { + size_t result = 2166136261UL; + while (*s != 0) + { + const char* p = reinterpret_cast<const char*>(s); + for (size_t i = 0; i < sizeof(Stringpool_char); ++i) + { + result ^= (size_t) *p++; + result *= 16777619UL; + } + ++s; + } + return result; + } +} + +// Add a string to the list of canonical strings. Return a pointer to +// the canonical string. If PKEY is not NULL, set *PKEY to the key. + +template<typename Stringpool_char> +const Stringpool_char* +Stringpool_template<Stringpool_char>::add_string(const Stringpool_char* s, + Key* pkey) +{ + // We are in trouble if we've already computed the string offsets. + gold_assert(this->strtab_size_ == 0); + + // The size we allocate for a new Stringdata. + const size_t buffer_size = 1000; + // The amount we multiply the Stringdata index when calculating the + // key. + const size_t key_mult = 1024; + gold_assert(key_mult >= buffer_size); + + size_t len = (string_length(s) + 1) * sizeof(Stringpool_char); + + size_t alc; + bool front = true; + if (len > buffer_size) + { + alc = sizeof(Stringdata) + len; + front = false; + } + else if (this->strings_.empty()) + alc = sizeof(Stringdata) + buffer_size; + else + { + Stringdata *psd = this->strings_.front(); + if (len > psd->alc - psd->len) + alc = sizeof(Stringdata) + buffer_size; + else + { + char* ret = psd->data + psd->len; + memcpy(ret, s, len); + + if (pkey != NULL) + *pkey = psd->index * key_mult + psd->len; + + psd->len += len; + + return reinterpret_cast<const Stringpool_char*>(ret); + } + } + + Stringdata *psd = reinterpret_cast<Stringdata*>(new char[alc]); + psd->alc = alc - sizeof(Stringdata); + memcpy(psd->data, s, len); + psd->len = len; + psd->index = this->next_index_; + ++this->next_index_; + + if (pkey != NULL) + *pkey = psd->index * key_mult; + + if (front) + this->strings_.push_front(psd); + else + this->strings_.push_back(psd); + + return reinterpret_cast<const Stringpool_char*>(psd->data); +} + +// Add a string to a string pool. + +template<typename Stringpool_char> +const Stringpool_char* +Stringpool_template<Stringpool_char>::add(const Stringpool_char* s, Key* pkey) +{ + // FIXME: This will look up the entry twice in the hash table. The + // problem is that we can't insert S before we canonicalize it. I + // don't think there is a way to handle this correctly with + // unordered_map, so this should be replaced with custom code to do + // what we need, which is to return the empty slot. + + typename String_set_type::const_iterator p = this->string_set_.find(s); + if (p != this->string_set_.end()) + { + if (pkey != NULL) + *pkey = p->second.first; + return p->first; + } + + Key k; + const Stringpool_char* ret = this->add_string(s, &k); + + const off_t ozero = 0; + std::pair<const Stringpool_char*, Val> element(ret, + std::make_pair(k, ozero)); + std::pair<typename String_set_type::iterator, bool> ins = + this->string_set_.insert(element); + gold_assert(ins.second); + + if (pkey != NULL) + *pkey = k; + + return ret; +} + +// Add a prefix of a string to a string pool. + +template<typename Stringpool_char> +const Stringpool_char* +Stringpool_template<Stringpool_char>::add(const Stringpool_char* s, size_t len, + Key* pkey) +{ + // FIXME: This implementation should be rewritten when we rewrite + // the hash table to avoid copying. + std::basic_string<Stringpool_char> st(s, len); + return this->add(st, pkey); +} + +template<typename Stringpool_char> +const Stringpool_char* +Stringpool_template<Stringpool_char>::find(const Stringpool_char* s, + Key* pkey) const +{ + typename String_set_type::const_iterator p = this->string_set_.find(s); + if (p == this->string_set_.end()) + return NULL; + + if (pkey != NULL) + *pkey = p->second.first; + + return p->first; +} + +// Comparison routine used when sorting into an ELF strtab. We want +// to sort this so that when one string is a suffix of another, we +// always see the shorter string immediately after the longer string. +// For example, we want to see these strings in this order: +// abcd +// cd +// d +// When strings are not suffixes, we don't care what order they are +// in, but we need to ensure that suffixes wind up next to each other. +// So we do a reversed lexicographic sort on the reversed string. + +template<typename Stringpool_char> +bool +Stringpool_template<Stringpool_char>::Stringpool_sort_comparison::operator()( + typename String_set_type::iterator it1, + typename String_set_type::iterator it2) const +{ + const Stringpool_char* s1 = it1->first; + const Stringpool_char* s2 = it2->first; + size_t len1 = string_length(s1); + size_t len2 = string_length(s2); + size_t minlen = len1 < len2 ? len1 : len2; + const Stringpool_char* p1 = s1 + len1 - 1; + const Stringpool_char* p2 = s2 + len2 - 1; + for (size_t i = minlen; i > 0; --i, --p1, --p2) + { + if (*p1 != *p2) + return *p1 > *p2; + } + return len1 > len2; +} + +// Return whether s1 is a suffix of s2. + +template<typename Stringpool_char> +bool +Stringpool_template<Stringpool_char>::is_suffix(const Stringpool_char* s1, + const Stringpool_char* s2) +{ + size_t len1 = string_length(s1); + size_t len2 = string_length(s2); + if (len1 > len2) + return false; + return memcmp(s1, s2 + len2 - len1, len1 * sizeof(Stringpool_char)) == 0; +} + +// Turn the stringpool into an ELF strtab: determine the offsets of +// each string in the table. + +template<typename Stringpool_char> +void +Stringpool_template<Stringpool_char>::set_string_offsets() +{ + if (this->strtab_size_ != 0) + { + // We've already computed the offsets. + return; + } + + size_t count = this->string_set_.size(); + + std::vector<typename String_set_type::iterator> v; + v.reserve(count); + + for (typename String_set_type::iterator p = this->string_set_.begin(); + p != this->string_set_.end(); + ++p) + v.push_back(p); + + std::sort(v.begin(), v.end(), Stringpool_sort_comparison()); + + const size_t charsize = sizeof(Stringpool_char); + + // Offset 0 may be reserved for the empty string. + off_t offset = this->zero_null_ ? charsize : 0; + for (size_t i = 0; i < count; ++i) + { + if (this->zero_null_ && v[i]->first[0] == 0) + v[i]->second.second = 0; + else if (i > 0 && is_suffix(v[i]->first, v[i - 1]->first)) + v[i]->second.second = (v[i - 1]->second.second + + ((string_length(v[i - 1]->first) + - string_length(v[i]->first)) + * charsize)); + else + { + v[i]->second.second = offset; + offset += (string_length(v[i]->first) + 1) * charsize; + } + } + + this->strtab_size_ = offset; +} + +// Get the offset of a string in the ELF strtab. The string must +// exist. + +template<typename Stringpool_char> +off_t +Stringpool_template<Stringpool_char>::get_offset(const Stringpool_char* s) + const +{ + gold_assert(this->strtab_size_ != 0); + typename String_set_type::const_iterator p = this->string_set_.find(s); + if (p != this->string_set_.end()) + return p->second.second; + gold_unreachable(); +} + +// Write the ELF strtab into the output file at the specified offset. + +template<typename Stringpool_char> +void +Stringpool_template<Stringpool_char>::write(Output_file* of, off_t offset) +{ + gold_assert(this->strtab_size_ != 0); + unsigned char* viewu = of->get_output_view(offset, this->strtab_size_); + char* view = reinterpret_cast<char*>(viewu); + if (this->zero_null_) + view[0] = '\0'; + for (typename String_set_type::const_iterator p = this->string_set_.begin(); + p != this->string_set_.end(); + ++p) + memcpy(view + p->second.second, p->first, + (string_length(p->first) + 1) * sizeof(Stringpool_char)); + of->write_output_view(offset, this->strtab_size_, viewu); +} + +// Instantiate the templates we need. + +template +class Stringpool_template<char>; + +template +class Stringpool_template<uint16_t>; + +template +class Stringpool_template<uint32_t>; + +} // End namespace gold. diff --git a/gold/stringpool.h b/gold/stringpool.h new file mode 100644 index 000000000000..b9a65f6db98d --- /dev/null +++ b/gold/stringpool.h @@ -0,0 +1,167 @@ +// stringpool.h -- a string pool for gold -*- C++ -*- + +#include <string> +#include <list> + +// Stringpool +// Manage a pool of unique strings. + +#ifndef GOLD_STRINGPOOL_H +#define GOLD_STRINGPOOL_H + +namespace gold +{ + +class Output_file; + +template<typename Stringpool_char> +class Stringpool_template +{ + public: + // The type of a key into the stringpool. A key value will always + // be the same during any run of the linker. The string pointers + // may change when using address space randomization. We use key + // values in order to get repeatable runs when the value is inserted + // into an unordered hash table. Zero is never a valid key. + typedef size_t Key; + + // Create a Stringpool. ZERO_NULL is true if we should reserve + // offset 0 to hold the empty string. + Stringpool_template(bool zero_null = true); + + ~Stringpool_template(); + + // Add a string to the pool. This returns a canonical permanent + // pointer to the string. If PKEY is not NULL, this sets *PKEY to + // the key for the string. + const Stringpool_char* + add(const Stringpool_char*, Key* pkey); + + const Stringpool_char* + add(const std::basic_string<Stringpool_char>& s, Key* pkey) + { return this->add(s.c_str(), pkey); } + + // Add the prefix of a string to the pool. + const Stringpool_char* + add(const Stringpool_char*, size_t, Key* pkey); + + // If a string is present, return the canonical string. Otherwise, + // return NULL. If PKEY is not NULL, set *PKEY to the key. + const Stringpool_char* + find(const Stringpool_char*, Key* pkey) const; + + // Turn the stringpool into an ELF strtab: determine the offsets of + // all the strings. + void + set_string_offsets(); + + // Get the offset of a string in an ELF strtab. This returns the + // offset in bytes, not characters. + off_t + get_offset(const Stringpool_char*) const; + + off_t + get_offset(const std::basic_string<Stringpool_char>& s) const + { return this->get_offset(s.c_str()); } + + // Get the size of the ELF strtab. This returns the number of + // bytes, not characters. + off_t + get_strtab_size() const + { + gold_assert(this->strtab_size_ != 0); + return this->strtab_size_; + } + + // Write the strtab into the output file at the specified offset. + void + write(Output_file*, off_t offset); + + private: + Stringpool_template(const Stringpool_template&); + Stringpool_template& operator=(const Stringpool_template&); + + // Return the length of a string. + static size_t + string_length(const Stringpool_char*); + + // We store the actual data in a list of these buffers. + struct Stringdata + { + // Length of data in buffer. + size_t len; + // Allocated size of buffer. + size_t alc; + // Buffer index. + unsigned int index; + // Buffer. + char data[1]; + }; + + // Copy a string into the buffers, returning a canonical string. + const Stringpool_char* + add_string(const Stringpool_char*, Key*); + + struct Stringpool_hash + { + size_t + operator()(const Stringpool_char*) const; + }; + + struct Stringpool_eq + { + bool + operator()(const Stringpool_char* p1, const Stringpool_char* p2) const; + }; + + // Return whether s1 is a suffix of s2. + static bool + is_suffix(const Stringpool_char* s1, const Stringpool_char* s2); + + // The hash table is a map from string names to a pair of Key and + // ELF strtab offsets. We only use the offsets if we turn this into + // an ELF strtab section. + + typedef std::pair<Key, off_t> Val; + +#ifdef HAVE_TR1_UNORDERED_SET + typedef Unordered_map<const Stringpool_char*, Val, Stringpool_hash, + Stringpool_eq, + std::allocator<std::pair<const Stringpool_char* const, + Val> >, + true> String_set_type; +#else + typedef Unordered_map<const Stringpool_char*, Val, Stringpool_hash, + Stringpool_eq> String_set_type; +#endif + + // Comparison routine used when sorting into an ELF strtab. + + struct Stringpool_sort_comparison + { + bool + operator()(typename String_set_type::iterator, + typename String_set_type::iterator) const; + }; + + // List of Stringdata structures. + typedef std::list<Stringdata*> Stringdata_list; + + // Mapping from const char* to namepool entry. + String_set_type string_set_; + // List of buffers. + Stringdata_list strings_; + // Size of ELF strtab. + off_t strtab_size_; + // Next Stringdata index. + unsigned int next_index_; + // Whether to reserve offset 0 to hold the null string. + bool zero_null_; +}; + +// The most common type of Stringpool. +typedef Stringpool_template<char> Stringpool; + +} // End namespace gold. + +#endif // !defined(GOLD_STRINGPOOL_H) diff --git a/gold/strtab.h b/gold/strtab.h new file mode 100644 index 000000000000..ab22b8fc286f --- /dev/null +++ b/gold/strtab.h @@ -0,0 +1,73 @@ +// strtab.h -- manage an ELF string table for gold -*- C++ -*- + +#ifndef GOLD_STRTAB_H +#define GOLD_STRTAB_H + +#include <cstring> +#include <string> + +namespace gold +{ + +// This class holds an ELF string table. We keep a reference count +// for each string, which we use to determine which strings are +// actually required at the end. When all operations are done, the +// string table is finalized, which sets the offsets to use for each +// string. + +class Strtab +{ + public: + Strtab(); + + ~Strtab(); + + Strtab_ref* add(const char*); + + Strtab_ref* add(const std::string& s) + { return this->add(s.c_str()); } + + private: + Strtab(const Strtab&); + Strtab& operator=(const Strtab&); + + struct strtab_hash + { + std::size_t + operator()(const char*p); + }; + + struct strtab_eq + { + bool + operator()(const char* p1, const char* p2) + { return strcmp(p1, p2) == 0; } + }; + + Unordered_map<const char*, Strtab_ref*, strtab_hash, strtab_eq, + std::allocator<std::pair<const char* const, Strtab_ref*> >, + true> strings_; +}; + +// Users of Strtab work with pointers to Strtab_ref structures. These +// are allocated via new and should be deleted if the string is no +// longer needed. + +class Strtab_ref +{ + public: + ~Strtab_ref(); + + const char* + str() const; + + private: + Strtab_ref(const Strtab_ref&); + Strtab_ref& operator=(const Strtab_ref&); + + int refs_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_STRTAB_H) diff --git a/gold/symtab.cc b/gold/symtab.cc new file mode 100644 index 000000000000..01e000d874e7 --- /dev/null +++ b/gold/symtab.cc @@ -0,0 +1,1567 @@ +// symtab.cc -- the gold symbol table + +#include "gold.h" + +#include <stdint.h> +#include <string> +#include <utility> + +#include "object.h" +#include "dynobj.h" +#include "output.h" +#include "target.h" +#include "workqueue.h" +#include "symtab.h" + +namespace gold +{ + +// Class Symbol. + +// Initialize fields in Symbol. This initializes everything except u_ +// and source_. + +void +Symbol::init_fields(const char* name, const char* version, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis) +{ + this->name_ = name; + this->version_ = version; + this->symtab_index_ = 0; + this->dynsym_index_ = 0; + this->got_offset_ = 0; + this->type_ = type; + this->binding_ = binding; + this->visibility_ = visibility; + this->nonvis_ = nonvis; + this->is_target_special_ = false; + this->is_def_ = false; + this->is_forwarder_ = false; + this->needs_dynsym_entry_ = false; + this->in_reg_ = false; + this->in_dyn_ = false; + this->has_got_offset_ = false; + this->has_warning_ = false; +} + +// Initialize the fields in the base class Symbol for SYM in OBJECT. + +template<int size, bool big_endian> +void +Symbol::init_base(const char* name, const char* version, Object* object, + const elfcpp::Sym<size, big_endian>& sym) +{ + this->init_fields(name, version, sym.get_st_type(), sym.get_st_bind(), + sym.get_st_visibility(), sym.get_st_nonvis()); + this->u_.from_object.object = object; + // FIXME: Handle SHN_XINDEX. + this->u_.from_object.shndx = sym.get_st_shndx(); + this->source_ = FROM_OBJECT; + this->in_reg_ = !object->is_dynamic(); + this->in_dyn_ = object->is_dynamic(); +} + +// Initialize the fields in the base class Symbol for a symbol defined +// in an Output_data. + +void +Symbol::init_base(const char* name, Output_data* od, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis, bool offset_is_from_end) +{ + this->init_fields(name, NULL, type, binding, visibility, nonvis); + this->u_.in_output_data.output_data = od; + this->u_.in_output_data.offset_is_from_end = offset_is_from_end; + this->source_ = IN_OUTPUT_DATA; + this->in_reg_ = true; +} + +// Initialize the fields in the base class Symbol for a symbol defined +// in an Output_segment. + +void +Symbol::init_base(const char* name, Output_segment* os, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis, Segment_offset_base offset_base) +{ + this->init_fields(name, NULL, type, binding, visibility, nonvis); + this->u_.in_output_segment.output_segment = os; + this->u_.in_output_segment.offset_base = offset_base; + this->source_ = IN_OUTPUT_SEGMENT; + this->in_reg_ = true; +} + +// Initialize the fields in the base class Symbol for a symbol defined +// as a constant. + +void +Symbol::init_base(const char* name, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis) +{ + this->init_fields(name, NULL, type, binding, visibility, nonvis); + this->source_ = CONSTANT; + this->in_reg_ = true; +} + +// Initialize the fields in Sized_symbol for SYM in OBJECT. + +template<int size> +template<bool big_endian> +void +Sized_symbol<size>::init(const char* name, const char* version, Object* object, + const elfcpp::Sym<size, big_endian>& sym) +{ + this->init_base(name, version, object, sym); + this->value_ = sym.get_st_value(); + this->symsize_ = sym.get_st_size(); +} + +// Initialize the fields in Sized_symbol for a symbol defined in an +// Output_data. + +template<int size> +void +Sized_symbol<size>::init(const char* name, Output_data* od, + Value_type value, Size_type symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool offset_is_from_end) +{ + this->init_base(name, od, type, binding, visibility, nonvis, + offset_is_from_end); + this->value_ = value; + this->symsize_ = symsize; +} + +// Initialize the fields in Sized_symbol for a symbol defined in an +// Output_segment. + +template<int size> +void +Sized_symbol<size>::init(const char* name, Output_segment* os, + Value_type value, Size_type symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + Segment_offset_base offset_base) +{ + this->init_base(name, os, type, binding, visibility, nonvis, offset_base); + this->value_ = value; + this->symsize_ = symsize; +} + +// Initialize the fields in Sized_symbol for a symbol defined as a +// constant. + +template<int size> +void +Sized_symbol<size>::init(const char* name, Value_type value, Size_type symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis) +{ + this->init_base(name, type, binding, visibility, nonvis); + this->value_ = value; + this->symsize_ = symsize; +} + +// Class Symbol_table. + +Symbol_table::Symbol_table() + : size_(0), saw_undefined_(0), offset_(0), table_(), namepool_(), + forwarders_(), commons_(), warnings_() +{ +} + +Symbol_table::~Symbol_table() +{ +} + +// The hash function. The key is always canonicalized, so we use a +// simple combination of the pointers. + +size_t +Symbol_table::Symbol_table_hash::operator()(const Symbol_table_key& key) const +{ + return key.first ^ key.second; +} + +// The symbol table key equality function. This is only called with +// canonicalized name and version strings, so we can use pointer +// comparison. + +bool +Symbol_table::Symbol_table_eq::operator()(const Symbol_table_key& k1, + const Symbol_table_key& k2) const +{ + return k1.first == k2.first && k1.second == k2.second; +} + +// Make TO a symbol which forwards to FROM. + +void +Symbol_table::make_forwarder(Symbol* from, Symbol* to) +{ + gold_assert(from != to); + gold_assert(!from->is_forwarder() && !to->is_forwarder()); + this->forwarders_[from] = to; + from->set_forwarder(); +} + +// Resolve the forwards from FROM, returning the real symbol. + +Symbol* +Symbol_table::resolve_forwards(const Symbol* from) const +{ + gold_assert(from->is_forwarder()); + Unordered_map<const Symbol*, Symbol*>::const_iterator p = + this->forwarders_.find(from); + gold_assert(p != this->forwarders_.end()); + return p->second; +} + +// Look up a symbol by name. + +Symbol* +Symbol_table::lookup(const char* name, const char* version) const +{ + Stringpool::Key name_key; + name = this->namepool_.find(name, &name_key); + if (name == NULL) + return NULL; + + Stringpool::Key version_key = 0; + if (version != NULL) + { + version = this->namepool_.find(version, &version_key); + if (version == NULL) + return NULL; + } + + Symbol_table_key key(name_key, version_key); + Symbol_table::Symbol_table_type::const_iterator p = this->table_.find(key); + if (p == this->table_.end()) + return NULL; + return p->second; +} + +// Resolve a Symbol with another Symbol. This is only used in the +// unusual case where there are references to both an unversioned +// symbol and a symbol with a version, and we then discover that that +// version is the default version. Because this is unusual, we do +// this the slow way, by converting back to an ELF symbol. + +template<int size, bool big_endian> +void +Symbol_table::resolve(Sized_symbol<size>* to, const Sized_symbol<size>* from, + const char* version ACCEPT_SIZE_ENDIAN) +{ + unsigned char buf[elfcpp::Elf_sizes<size>::sym_size]; + elfcpp::Sym_write<size, big_endian> esym(buf); + // We don't bother to set the st_name field. + esym.put_st_value(from->value()); + esym.put_st_size(from->symsize()); + esym.put_st_info(from->binding(), from->type()); + esym.put_st_other(from->visibility(), from->nonvis()); + esym.put_st_shndx(from->shndx()); + Symbol_table::resolve(to, esym.sym(), from->object(), version); +} + +// Add one symbol from OBJECT to the symbol table. NAME is symbol +// name and VERSION is the version; both are canonicalized. DEF is +// whether this is the default version. + +// If DEF is true, then this is the definition of a default version of +// a symbol. That means that any lookup of NAME/NULL and any lookup +// of NAME/VERSION should always return the same symbol. This is +// obvious for references, but in particular we want to do this for +// definitions: overriding NAME/NULL should also override +// NAME/VERSION. If we don't do that, it would be very hard to +// override functions in a shared library which uses versioning. + +// We implement this by simply making both entries in the hash table +// point to the same Symbol structure. That is easy enough if this is +// the first time we see NAME/NULL or NAME/VERSION, but it is possible +// that we have seen both already, in which case they will both have +// independent entries in the symbol table. We can't simply change +// the symbol table entry, because we have pointers to the entries +// attached to the object files. So we mark the entry attached to the +// object file as a forwarder, and record it in the forwarders_ map. +// Note that entries in the hash table will never be marked as +// forwarders. + +template<int size, bool big_endian> +Symbol* +Symbol_table::add_from_object(Object* object, + const char *name, + Stringpool::Key name_key, + const char *version, + Stringpool::Key version_key, + bool def, + const elfcpp::Sym<size, big_endian>& sym) +{ + Symbol* const snull = NULL; + std::pair<typename Symbol_table_type::iterator, bool> ins = + this->table_.insert(std::make_pair(std::make_pair(name_key, version_key), + snull)); + + std::pair<typename Symbol_table_type::iterator, bool> insdef = + std::make_pair(this->table_.end(), false); + if (def) + { + const Stringpool::Key vnull_key = 0; + insdef = this->table_.insert(std::make_pair(std::make_pair(name_key, + vnull_key), + snull)); + } + + // ins.first: an iterator, which is a pointer to a pair. + // ins.first->first: the key (a pair of name and version). + // ins.first->second: the value (Symbol*). + // ins.second: true if new entry was inserted, false if not. + + Sized_symbol<size>* ret; + bool was_undefined; + bool was_common; + if (!ins.second) + { + // We already have an entry for NAME/VERSION. + ret = this->get_sized_symbol SELECT_SIZE_NAME(size) (ins.first->second + SELECT_SIZE(size)); + gold_assert(ret != NULL); + + was_undefined = ret->is_undefined(); + was_common = ret->is_common(); + + Symbol_table::resolve(ret, sym, object, version); + + if (def) + { + if (insdef.second) + { + // This is the first time we have seen NAME/NULL. Make + // NAME/NULL point to NAME/VERSION. + insdef.first->second = ret; + } + else if (insdef.first->second != ret) + { + // This is the unfortunate case where we already have + // entries for both NAME/VERSION and NAME/NULL. + const Sized_symbol<size>* sym2; + sym2 = this->get_sized_symbol SELECT_SIZE_NAME(size) ( + insdef.first->second + SELECT_SIZE(size)); + Symbol_table::resolve SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + ret, sym2, version SELECT_SIZE_ENDIAN(size, big_endian)); + this->make_forwarder(insdef.first->second, ret); + insdef.first->second = ret; + } + } + } + else + { + // This is the first time we have seen NAME/VERSION. + gold_assert(ins.first->second == NULL); + + was_undefined = false; + was_common = false; + + if (def && !insdef.second) + { + // We already have an entry for NAME/NULL. If we override + // it, then change it to NAME/VERSION. + ret = this->get_sized_symbol SELECT_SIZE_NAME(size) ( + insdef.first->second + SELECT_SIZE(size)); + Symbol_table::resolve(ret, sym, object, version); + ins.first->second = ret; + } + else + { + Sized_target<size, big_endian>* target = + object->sized_target SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + SELECT_SIZE_ENDIAN_ONLY(size, big_endian)); + if (!target->has_make_symbol()) + ret = new Sized_symbol<size>(); + else + { + ret = target->make_symbol(); + if (ret == NULL) + { + // This means that we don't want a symbol table + // entry after all. + if (!def) + this->table_.erase(ins.first); + else + { + this->table_.erase(insdef.first); + // Inserting insdef invalidated ins. + this->table_.erase(std::make_pair(name_key, + version_key)); + } + return NULL; + } + } + + ret->init(name, version, object, sym); + + ins.first->second = ret; + if (def) + { + // This is the first time we have seen NAME/NULL. Point + // it at the new entry for NAME/VERSION. + gold_assert(insdef.second); + insdef.first->second = ret; + } + } + } + + // Record every time we see a new undefined symbol, to speed up + // archive groups. + if (!was_undefined && ret->is_undefined()) + ++this->saw_undefined_; + + // Keep track of common symbols, to speed up common symbol + // allocation. + if (!was_common && ret->is_common()) + this->commons_.push_back(ret); + + return ret; +} + +// Add all the symbols in a relocatable object to the hash table. + +template<int size, bool big_endian> +void +Symbol_table::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) +{ + // 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 != relobj->target()->get_size()) + { + fprintf(stderr, _("%s: %s: mixing 32-bit and 64-bit ELF objects\n"), + program_name, relobj->name().c_str()); + gold_exit(false); + } + + const int sym_size = elfcpp::Elf_sizes<size>::sym_size; + + const unsigned char* p = syms; + for (size_t i = 0; i < count; ++i, p += sym_size) + { + elfcpp::Sym<size, big_endian> sym(p); + elfcpp::Sym<size, big_endian>* psym = &sym; + + unsigned int st_name = psym->get_st_name(); + if (st_name >= sym_name_size) + { + fprintf(stderr, + _("%s: %s: bad global symbol name offset %u at %lu\n"), + 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]; + elfcpp::Sym<size, big_endian> sym2(symbuf); + unsigned int st_shndx = psym->get_st_shndx(); + if (st_shndx != elfcpp::SHN_UNDEF + && st_shndx < elfcpp::SHN_LORESERVE + && !relobj->is_section_included(st_shndx)) + { + memcpy(symbuf, p, sym_size); + elfcpp::Sym_write<size, big_endian> sw(symbuf); + sw.put_st_shndx(elfcpp::SHN_UNDEF); + psym = &sym2; + } + + // 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. + const char* ver = strchr(name, '@'); + + Symbol* res; + if (ver == NULL) + { + Stringpool::Key name_key; + name = this->namepool_.add(name, &name_key); + res = this->add_from_object(relobj, name, name_key, NULL, 0, + false, *psym); + } + else + { + Stringpool::Key name_key; + name = this->namepool_.add(name, ver - name, &name_key); + + bool def = false; + ++ver; + if (*ver == '@') + { + def = true; + ++ver; + } + + Stringpool::Key ver_key; + ver = this->namepool_.add(ver, &ver_key); + + res = this->add_from_object(relobj, name, name_key, ver, ver_key, + def, *psym); + } + + *sympointers++ = res; + } +} + +// 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. + +template<int size, bool big_endian> +Sized_symbol<size>* +Symbol_table::define_special_symbol(const Target* target, const char* name, + const char* version, bool only_if_ref + ACCEPT_SIZE_ENDIAN) +{ + gold_assert(this->size_ == size); + + Symbol* oldsym; + Sized_symbol<size>* sym; + + if (only_if_ref) + { + oldsym = this->lookup(name, version); + if (oldsym == NULL || !oldsym->is_undefined()) + return NULL; + sym = NULL; + + // Canonicalize NAME and VERSION. + name = oldsym->name(); + version = oldsym->version(); + } + else + { + // Canonicalize NAME and VERSION. + Stringpool::Key name_key; + name = this->namepool_.add(name, &name_key); + + Stringpool::Key version_key = 0; + if (version != NULL) + version = this->namepool_.add(version, &version_key); + + Symbol* const snull = NULL; + std::pair<typename Symbol_table_type::iterator, bool> ins = + this->table_.insert(std::make_pair(std::make_pair(name_key, + version_key), + snull)); + + if (!ins.second) + { + // We already have a symbol table entry for NAME/VERSION. + oldsym = ins.first->second; + gold_assert(oldsym != NULL); + sym = NULL; + } + else + { + // We haven't seen this symbol before. + gold_assert(ins.first->second == NULL); + + if (!target->has_make_symbol()) + sym = new Sized_symbol<size>(); + else + { + gold_assert(target->get_size() == size); + gold_assert(target->is_big_endian() ? big_endian : !big_endian); + typedef Sized_target<size, big_endian> My_target; + const My_target* sized_target = + static_cast<const My_target*>(target); + sym = sized_target->make_symbol(); + if (sym == NULL) + return NULL; + } + + ins.first->second = sym; + oldsym = NULL; + } + } + + if (oldsym != NULL) + { + gold_assert(sym == NULL); + + sym = this->get_sized_symbol SELECT_SIZE_NAME(size) (oldsym + SELECT_SIZE(size)); + gold_assert(sym->source() == Symbol::FROM_OBJECT); + const int old_shndx = sym->shndx(); + if (old_shndx != elfcpp::SHN_UNDEF + && old_shndx != elfcpp::SHN_COMMON + && !sym->object()->is_dynamic()) + { + fprintf(stderr, "%s: linker defined: multiple definition of %s\n", + program_name, name); + // FIXME: Report old location. Record that we have seen an + // error. + return NULL; + } + + // Our new definition is going to override the old reference. + } + + return sym; +} + +// Define a symbol based on an Output_data. + +Symbol* +Symbol_table::define_in_output_data(const Target* target, const char* name, + const char* version, Output_data* od, + uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + bool offset_is_from_end, + bool only_if_ref) +{ + gold_assert(target->get_size() == this->size_); + if (this->size_ == 32) + return this->do_define_in_output_data<32>(target, name, version, od, value, + symsize, type, binding, + visibility, nonvis, + offset_is_from_end, only_if_ref); + else if (this->size_ == 64) + return this->do_define_in_output_data<64>(target, name, version, od, value, + symsize, type, binding, + visibility, nonvis, + offset_is_from_end, only_if_ref); + else + gold_unreachable(); +} + +// Define a symbol in an Output_data, sized version. + +template<int size> +Sized_symbol<size>* +Symbol_table::do_define_in_output_data( + const Target* target, + const char* name, + const char* version, + Output_data* od, + typename elfcpp::Elf_types<size>::Elf_Addr value, + typename elfcpp::Elf_types<size>::Elf_WXword symsize, + elfcpp::STT type, + elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + bool offset_is_from_end, + bool only_if_ref) +{ + Sized_symbol<size>* sym; + + if (target->is_big_endian()) + sym = this->define_special_symbol SELECT_SIZE_ENDIAN_NAME(size, true) ( + target, name, version, only_if_ref + SELECT_SIZE_ENDIAN(size, true)); + else + sym = this->define_special_symbol SELECT_SIZE_ENDIAN_NAME(size, false) ( + target, name, version, only_if_ref + SELECT_SIZE_ENDIAN(size, false)); + + if (sym == NULL) + return NULL; + + sym->init(name, od, value, symsize, type, binding, visibility, nonvis, + offset_is_from_end); + + return sym; +} + +// Define a symbol based on an Output_segment. + +Symbol* +Symbol_table::define_in_output_segment(const Target* target, const char* name, + const char* version, Output_segment* os, + uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + Symbol::Segment_offset_base offset_base, + bool only_if_ref) +{ + gold_assert(target->get_size() == this->size_); + if (this->size_ == 32) + return this->do_define_in_output_segment<32>(target, name, version, os, + value, symsize, type, binding, + visibility, nonvis, + offset_base, only_if_ref); + else if (this->size_ == 64) + return this->do_define_in_output_segment<64>(target, name, version, os, + value, symsize, type, binding, + visibility, nonvis, + offset_base, only_if_ref); + else + gold_unreachable(); +} + +// Define a symbol in an Output_segment, sized version. + +template<int size> +Sized_symbol<size>* +Symbol_table::do_define_in_output_segment( + const Target* target, + const char* name, + const char* version, + Output_segment* os, + typename elfcpp::Elf_types<size>::Elf_Addr value, + typename elfcpp::Elf_types<size>::Elf_WXword symsize, + elfcpp::STT type, + elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + Symbol::Segment_offset_base offset_base, + bool only_if_ref) +{ + Sized_symbol<size>* sym; + + if (target->is_big_endian()) + sym = this->define_special_symbol SELECT_SIZE_ENDIAN_NAME(size, true) ( + target, name, version, only_if_ref + SELECT_SIZE_ENDIAN(size, true)); + else + sym = this->define_special_symbol SELECT_SIZE_ENDIAN_NAME(size, false) ( + target, name, version, only_if_ref + SELECT_SIZE_ENDIAN(size, false)); + + if (sym == NULL) + return NULL; + + sym->init(name, os, value, symsize, type, binding, visibility, nonvis, + offset_base); + + return sym; +} + +// Define a special symbol with a constant value. It is a multiple +// definition error if this symbol is already defined. + +Symbol* +Symbol_table::define_as_constant(const Target* target, const char* name, + const char* version, uint64_t value, + uint64_t symsize, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis, bool only_if_ref) +{ + gold_assert(target->get_size() == this->size_); + if (this->size_ == 32) + return this->do_define_as_constant<32>(target, name, version, value, + symsize, type, binding, visibility, + nonvis, only_if_ref); + else if (this->size_ == 64) + return this->do_define_as_constant<64>(target, name, version, value, + symsize, type, binding, visibility, + nonvis, only_if_ref); + else + gold_unreachable(); +} + +// Define a symbol as a constant, sized version. + +template<int size> +Sized_symbol<size>* +Symbol_table::do_define_as_constant( + const Target* target, + const char* name, + const char* version, + typename elfcpp::Elf_types<size>::Elf_Addr value, + typename elfcpp::Elf_types<size>::Elf_WXword symsize, + elfcpp::STT type, + elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + bool only_if_ref) +{ + Sized_symbol<size>* sym; + + if (target->is_big_endian()) + sym = this->define_special_symbol SELECT_SIZE_ENDIAN_NAME(size, true) ( + target, name, version, only_if_ref + SELECT_SIZE_ENDIAN(size, true)); + else + sym = this->define_special_symbol SELECT_SIZE_ENDIAN_NAME(size, false) ( + target, name, version, only_if_ref + SELECT_SIZE_ENDIAN(size, false)); + + if (sym == NULL) + return NULL; + + sym->init(name, value, symsize, type, binding, visibility, nonvis); + + return sym; +} + +// Define a set of symbols in output sections. + +void +Symbol_table::define_symbols(const Layout* layout, const Target* target, + int count, const Define_symbol_in_section* p) +{ + for (int i = 0; i < count; ++i, ++p) + { + Output_section* os = layout->find_output_section(p->output_section); + if (os != NULL) + this->define_in_output_data(target, p->name, NULL, os, p->value, + p->size, p->type, p->binding, + p->visibility, p->nonvis, + p->offset_is_from_end, p->only_if_ref); + else + this->define_as_constant(target, p->name, NULL, 0, p->size, p->type, + p->binding, p->visibility, p->nonvis, + p->only_if_ref); + } +} + +// Define a set of symbols in output segments. + +void +Symbol_table::define_symbols(const Layout* layout, const Target* target, + int count, const Define_symbol_in_segment* p) +{ + for (int i = 0; i < count; ++i, ++p) + { + Output_segment* os = layout->find_output_segment(p->segment_type, + p->segment_flags_set, + p->segment_flags_clear); + if (os != NULL) + this->define_in_output_segment(target, p->name, NULL, os, p->value, + p->size, p->type, p->binding, + p->visibility, p->nonvis, + p->offset_base, p->only_if_ref); + else + this->define_as_constant(target, p->name, NULL, 0, p->size, p->type, + p->binding, p->visibility, p->nonvis, + p->only_if_ref); + } +} + +// Set the dynamic symbol indexes. INDEX is the index of the first +// global dynamic symbol. Pointers to the symbols are stored into the +// vector SYMS. The names are added to DYNPOOL. This returns an +// updated dynamic symbol index. + +unsigned int +Symbol_table::set_dynsym_indexes(const General_options* options, + const Target* target, + unsigned int index, + std::vector<Symbol*>* syms, + Stringpool* dynpool, + Versions* versions) +{ + for (Symbol_table_type::iterator p = this->table_.begin(); + p != this->table_.end(); + ++p) + { + Symbol* sym = p->second; + + // Note that SYM may already have a dynamic symbol index, since + // some symbols appear more than once in the symbol table, with + // and without a version. + + if (!sym->needs_dynsym_entry()) + sym->set_dynsym_index(-1U); + else if (!sym->has_dynsym_index()) + { + sym->set_dynsym_index(index); + ++index; + syms->push_back(sym); + dynpool->add(sym->name(), NULL); + + // Record any version information. + if (sym->version() != NULL) + versions->record_version(options, dynpool, sym); + } + } + + // Finish up the versions. In some cases this may add new dynamic + // symbols. + index = versions->finalize(target, this, index, syms); + + return index; +} + +// Set the final values for all the symbols. The index of the first +// global symbol in the output file is INDEX. Record the file offset +// OFF. Add their names to POOL. Return the new file offset. + +off_t +Symbol_table::finalize(unsigned int index, off_t off, off_t dynoff, + size_t dyn_global_index, size_t dyncount, + Stringpool* pool) +{ + off_t ret; + + gold_assert(index != 0); + this->first_global_index_ = index; + + this->dynamic_offset_ = dynoff; + this->first_dynamic_global_index_ = dyn_global_index; + this->dynamic_count_ = dyncount; + + if (this->size_ == 32) + ret = this->sized_finalize<32>(index, off, pool); + else if (this->size_ == 64) + ret = this->sized_finalize<64>(index, off, pool); + else + gold_unreachable(); + + // Now that we have the final symbol table, we can reliably note + // which symbols should get warnings. + this->warnings_.note_warnings(this); + + return ret; +} + +// Set the final value for all the symbols. This is called after +// Layout::finalize, so all the output sections have their final +// address. + +template<int size> +off_t +Symbol_table::sized_finalize(unsigned index, off_t off, Stringpool* pool) +{ + off = align_address(off, size >> 3); + this->offset_ = off; + + size_t orig_index = index; + + const int sym_size = elfcpp::Elf_sizes<size>::sym_size; + for (Symbol_table_type::iterator p = this->table_.begin(); + p != this->table_.end(); + ++p) + { + Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(p->second); + + // FIXME: Here we need to decide which symbols should go into + // the output file, based on --strip. + + // The default version of a symbol may appear twice in the + // symbol table. We only need to finalize it once. + if (sym->has_symtab_index()) + continue; + + if (!sym->in_reg()) + { + gold_assert(!sym->has_symtab_index()); + sym->set_symtab_index(-1U); + gold_assert(sym->dynsym_index() == -1U); + continue; + } + + typename Sized_symbol<size>::Value_type value; + + switch (sym->source()) + { + case Symbol::FROM_OBJECT: + { + unsigned int shndx = sym->shndx(); + + // FIXME: We need some target specific support here. + if (shndx >= elfcpp::SHN_LORESERVE + && shndx != elfcpp::SHN_ABS) + { + fprintf(stderr, _("%s: %s: unsupported symbol section 0x%x\n"), + program_name, sym->name(), shndx); + gold_exit(false); + } + + Object* symobj = sym->object(); + if (symobj->is_dynamic()) + { + value = 0; + shndx = elfcpp::SHN_UNDEF; + } + else if (shndx == elfcpp::SHN_UNDEF) + value = 0; + else if (shndx == elfcpp::SHN_ABS) + value = sym->value(); + else + { + Relobj* relobj = static_cast<Relobj*>(symobj); + off_t secoff; + Output_section* os = relobj->output_section(shndx, &secoff); + + if (os == NULL) + { + sym->set_symtab_index(-1U); + gold_assert(sym->dynsym_index() == -1U); + continue; + } + + value = sym->value() + os->address() + secoff; + } + } + break; + + case Symbol::IN_OUTPUT_DATA: + { + Output_data* od = sym->output_data(); + value = sym->value() + od->address(); + if (sym->offset_is_from_end()) + value += od->data_size(); + } + break; + + case Symbol::IN_OUTPUT_SEGMENT: + { + Output_segment* os = sym->output_segment(); + value = sym->value() + os->vaddr(); + switch (sym->offset_base()) + { + case Symbol::SEGMENT_START: + break; + case Symbol::SEGMENT_END: + value += os->memsz(); + break; + case Symbol::SEGMENT_BSS: + value += os->filesz(); + break; + default: + gold_unreachable(); + } + } + break; + + case Symbol::CONSTANT: + value = sym->value(); + break; + + default: + gold_unreachable(); + } + + sym->set_value(value); + sym->set_symtab_index(index); + pool->add(sym->name(), NULL); + ++index; + off += sym_size; + } + + this->output_count_ = index - orig_index; + + return off; +} + +// Write out the global symbols. + +void +Symbol_table::write_globals(const Target* target, const Stringpool* sympool, + const Stringpool* dynpool, Output_file* of) const +{ + if (this->size_ == 32) + { + if (target->is_big_endian()) + this->sized_write_globals<32, true>(target, sympool, dynpool, of); + else + this->sized_write_globals<32, false>(target, sympool, dynpool, of); + } + else if (this->size_ == 64) + { + if (target->is_big_endian()) + this->sized_write_globals<64, true>(target, sympool, dynpool, of); + else + this->sized_write_globals<64, false>(target, sympool, dynpool, of); + } + else + gold_unreachable(); +} + +// Write out the global symbols. + +template<int size, bool big_endian> +void +Symbol_table::sized_write_globals(const Target*, + const Stringpool* sympool, + const Stringpool* dynpool, + Output_file* of) const +{ + const int sym_size = elfcpp::Elf_sizes<size>::sym_size; + unsigned int index = this->first_global_index_; + const off_t oview_size = this->output_count_ * sym_size; + unsigned char* const psyms = of->get_output_view(this->offset_, oview_size); + + unsigned int dynamic_count = this->dynamic_count_; + off_t dynamic_size = dynamic_count * sym_size; + unsigned int first_dynamic_global_index = this->first_dynamic_global_index_; + unsigned char* dynamic_view; + if (this->dynamic_offset_ == 0) + dynamic_view = NULL; + else + dynamic_view = of->get_output_view(this->dynamic_offset_, dynamic_size); + + unsigned char* ps = psyms; + for (Symbol_table_type::const_iterator p = this->table_.begin(); + p != this->table_.end(); + ++p) + { + Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(p->second); + + unsigned int sym_index = sym->symtab_index(); + unsigned int dynsym_index; + if (dynamic_view == NULL) + dynsym_index = -1U; + else + dynsym_index = sym->dynsym_index(); + + if (sym_index == -1U && dynsym_index == -1U) + { + // This symbol is not included in the output file. + continue; + } + + if (sym_index == index) + ++index; + else if (sym_index != -1U) + { + // We have already seen this symbol, because it has a + // default version. + gold_assert(sym_index < index); + if (dynsym_index == -1U) + continue; + sym_index = -1U; + } + + unsigned int shndx; + switch (sym->source()) + { + case Symbol::FROM_OBJECT: + { + unsigned int in_shndx = sym->shndx(); + + // FIXME: We need some target specific support here. + if (in_shndx >= elfcpp::SHN_LORESERVE + && in_shndx != elfcpp::SHN_ABS) + { + fprintf(stderr, _("%s: %s: unsupported symbol section 0x%x\n"), + program_name, sym->name(), in_shndx); + gold_exit(false); + } + + Object* symobj = sym->object(); + if (symobj->is_dynamic()) + { + // FIXME. + shndx = elfcpp::SHN_UNDEF; + } + else if (in_shndx == elfcpp::SHN_UNDEF + || in_shndx == elfcpp::SHN_ABS) + shndx = in_shndx; + else + { + Relobj* relobj = static_cast<Relobj*>(symobj); + off_t secoff; + Output_section* os = relobj->output_section(in_shndx, &secoff); + gold_assert(os != NULL); + shndx = os->out_shndx(); + } + } + break; + + case Symbol::IN_OUTPUT_DATA: + shndx = sym->output_data()->out_shndx(); + break; + + case Symbol::IN_OUTPUT_SEGMENT: + shndx = elfcpp::SHN_ABS; + break; + + case Symbol::CONSTANT: + shndx = elfcpp::SHN_ABS; + break; + + default: + gold_unreachable(); + } + + if (sym_index != -1U) + { + this->sized_write_symbol SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + sym, shndx, sympool, ps + SELECT_SIZE_ENDIAN(size, big_endian)); + ps += sym_size; + } + + if (dynsym_index != -1U) + { + dynsym_index -= first_dynamic_global_index; + gold_assert(dynsym_index < dynamic_count); + unsigned char* pd = dynamic_view + (dynsym_index * sym_size); + this->sized_write_symbol SELECT_SIZE_ENDIAN_NAME(size, big_endian) ( + sym, shndx, dynpool, pd + SELECT_SIZE_ENDIAN(size, big_endian)); + } + } + + gold_assert(ps - psyms == oview_size); + + of->write_output_view(this->offset_, oview_size, psyms); + if (dynamic_view != NULL) + of->write_output_view(this->dynamic_offset_, dynamic_size, dynamic_view); +} + +// Write out the symbol SYM, in section SHNDX, to P. POOL is the +// strtab holding the name. + +template<int size, bool big_endian> +void +Symbol_table::sized_write_symbol(Sized_symbol<size>* sym, + unsigned int shndx, + const Stringpool* pool, + unsigned char* p + ACCEPT_SIZE_ENDIAN) const +{ + elfcpp::Sym_write<size, big_endian> osym(p); + osym.put_st_name(pool->get_offset(sym->name())); + osym.put_st_value(sym->value()); + osym.put_st_size(sym->symsize()); + osym.put_st_info(elfcpp::elf_st_info(sym->binding(), sym->type())); + osym.put_st_other(elfcpp::elf_st_other(sym->visibility(), sym->nonvis())); + osym.put_st_shndx(shndx); +} + +// Write out a section symbol. Return the update offset. + +void +Symbol_table::write_section_symbol(const Target* target, + const Output_section *os, + Output_file* of, + off_t offset) const +{ + if (this->size_ == 32) + { + if (target->is_big_endian()) + this->sized_write_section_symbol<32, true>(os, of, offset); + else + this->sized_write_section_symbol<32, false>(os, of, offset); + } + else if (this->size_ == 64) + { + if (target->is_big_endian()) + this->sized_write_section_symbol<64, true>(os, of, offset); + else + this->sized_write_section_symbol<64, false>(os, of, offset); + } + else + gold_unreachable(); +} + +// Write out a section symbol, specialized for size and endianness. + +template<int size, bool big_endian> +void +Symbol_table::sized_write_section_symbol(const Output_section* os, + Output_file* of, + off_t offset) const +{ + const int sym_size = elfcpp::Elf_sizes<size>::sym_size; + + unsigned char* pov = of->get_output_view(offset, sym_size); + + elfcpp::Sym_write<size, big_endian> osym(pov); + osym.put_st_name(0); + osym.put_st_value(os->address()); + osym.put_st_size(0); + osym.put_st_info(elfcpp::elf_st_info(elfcpp::STB_LOCAL, + elfcpp::STT_SECTION)); + osym.put_st_other(elfcpp::elf_st_other(elfcpp::STV_DEFAULT, 0)); + osym.put_st_shndx(os->out_shndx()); + + of->write_output_view(offset, sym_size, pov); +} + +// Warnings functions. + +// Add a new warning. + +void +Warnings::add_warning(Symbol_table* symtab, const char* name, Object* obj, + unsigned int shndx) +{ + name = symtab->canonicalize_name(name); + this->warnings_[name].set(obj, shndx); +} + +// Look through the warnings and mark the symbols for which we should +// warn. This is called during Layout::finalize when we know the +// sources for all the symbols. + +void +Warnings::note_warnings(Symbol_table* symtab) +{ + for (Warning_table::iterator p = this->warnings_.begin(); + p != this->warnings_.end(); + ++p) + { + Symbol* sym = symtab->lookup(p->first, NULL); + if (sym != NULL + && sym->source() == Symbol::FROM_OBJECT + && sym->object() == p->second.object) + { + sym->set_has_warning(); + + // Read the section contents to get the warning text. It + // would be nicer if we only did this if we have to actually + // issue a warning. Unfortunately, warnings are issued as + // we relocate sections. That means that we can not lock + // the object then, as we might try to issue the same + // warning multiple times simultaneously. + { + Task_locker_obj<Object> tl(*p->second.object); + const unsigned char* c; + off_t len; + c = p->second.object->section_contents(p->second.shndx, &len); + p->second.set_text(reinterpret_cast<const char*>(c), len); + } + } + } +} + +// Issue a warning. This is called when we see a relocation against a +// symbol for which has a warning. + +void +Warnings::issue_warning(const Symbol* sym, const std::string& location) const +{ + gold_assert(sym->has_warning()); + Warning_table::const_iterator p = this->warnings_.find(sym->name()); + gold_assert(p != this->warnings_.end()); + fprintf(stderr, _("%s: %s: warning: %s\n"), program_name, location.c_str(), + p->second.text.c_str()); +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones needed for implemented +// targets. + +template +void +Symbol_table::add_from_relobj<32, true>( + Sized_relobj<32, true>* 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_relobj<32, false>( + Sized_relobj<32, 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_relobj<64, true>( + Sized_relobj<64, true>* 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_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 new file mode 100644 index 000000000000..66e98bc6d25e --- /dev/null +++ b/gold/symtab.h @@ -0,0 +1,1050 @@ +// symtab.h -- the gold symbol table -*- C++ -*- + +// Symbol_table +// The symbol table. + +#include <string> +#include <utility> +#include <vector> + +#include "elfcpp.h" +#include "stringpool.h" +#include "object.h" + +#ifndef GOLD_SYMTAB_H +#define GOLD_SYMTAB_H + +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 Versions; +class Output_data; +class Output_section; +class Output_segment; +class Output_file; +class Target; + +// The base class of an entry in the symbol table. The symbol table +// can have a lot of entries, so we don't want this class to big. +// Size dependent fields can be found in the template class +// Sized_symbol. Targets may support their own derived classes. + +class Symbol +{ + public: + // Because we want the class to be small, we don't use any virtual + // functions. But because symbols can be defined in different + // places, we need to classify them. This enum is the different + // sources of symbols we support. + enum Source + { + // Symbol defined in a relocatable or dynamic input file--this is + // the most common case. + FROM_OBJECT, + // Symbol defined in an Output_data, a special section created by + // the target. + IN_OUTPUT_DATA, + // Symbol defined in an Output_segment, with no associated + // section. + IN_OUTPUT_SEGMENT, + // Symbol value is constant. + CONSTANT + }; + + // When the source is IN_OUTPUT_SEGMENT, we need to describe what + // the offset means. + enum Segment_offset_base + { + // From the start of the segment. + SEGMENT_START, + // From the end of the segment. + SEGMENT_END, + // From the filesz of the segment--i.e., after the loaded bytes + // but before the bytes which are allocated but zeroed. + SEGMENT_BSS + }; + + // Return the symbol name. + const char* + name() const + { return this->name_; } + + // Return the symbol version. This will return NULL for an + // unversioned symbol. + const char* + version() const + { return this->version_; } + + // Return the symbol source. + Source + source() const + { return this->source_; } + + // Return the object with which this symbol is associated. + Object* + object() const + { + gold_assert(this->source_ == FROM_OBJECT); + return this->u_.from_object.object; + } + + // Return the index of the section in the input relocatable or + // dynamic object file. + unsigned int + shndx() const + { + gold_assert(this->source_ == FROM_OBJECT); + return this->u_.from_object.shndx; + } + + // Return the output data section with which this symbol is + // associated, if the symbol was specially defined with respect to + // an output data section. + Output_data* + output_data() const + { + gold_assert(this->source_ == IN_OUTPUT_DATA); + return this->u_.in_output_data.output_data; + } + + // If this symbol was defined with respect to an output data + // section, return whether the value is an offset from end. + bool + offset_is_from_end() const + { + gold_assert(this->source_ == IN_OUTPUT_DATA); + return this->u_.in_output_data.offset_is_from_end; + } + + // Return the output segment with which this symbol is associated, + // if the symbol was specially defined with respect to an output + // segment. + Output_segment* + output_segment() const + { + gold_assert(this->source_ == IN_OUTPUT_SEGMENT); + return this->u_.in_output_segment.output_segment; + } + + // If this symbol was defined with respect to an output segment, + // return the offset base. + Segment_offset_base + offset_base() const + { + gold_assert(this->source_ == IN_OUTPUT_SEGMENT); + return this->u_.in_output_segment.offset_base; + } + + // Return the symbol binding. + elfcpp::STB + binding() const + { return this->binding_; } + + // Return the symbol type. + elfcpp::STT + type() const + { return this->type_; } + + // Return the symbol visibility. + elfcpp::STV + visibility() const + { return this->visibility_; } + + // Return the non-visibility part of the st_other field. + unsigned char + nonvis() const + { return this->nonvis_; } + + // Return whether this symbol is a forwarder. This will never be + // true of a symbol found in the hash table, but may be true of + // symbol pointers attached to object files. + bool + is_forwarder() const + { return this->is_forwarder_; } + + // Mark this symbol as a forwarder. + void + set_forwarder() + { this->is_forwarder_ = true; } + + // Return whether this symbol needs an entry in the dynamic symbol + // table. + bool + needs_dynsym_entry() const + { return this->needs_dynsym_entry_; } + + // Mark this symbol as needing an entry in the dynamic symbol table. + void + set_needs_dynsym_entry() + { this->needs_dynsym_entry_ = true; } + + // Return whether this symbol has been seen in a regular object. + bool + in_reg() const + { return this->in_reg_; } + + // Mark this symbol as having been seen in a regular object. + void + set_in_reg() + { this->in_reg_ = true; } + + // Mark this symbol as having been seen in a dynamic object. + void + set_in_dyn() + { this->in_dyn_ = true; } + + // Return the index of this symbol in the output file symbol table. + // A value of -1U means that this symbol is not going into the + // output file. This starts out as zero, and is set to a non-zero + // value by Symbol_table::finalize. It is an error to ask for the + // symbol table index before it has been set. + unsigned int + symtab_index() const + { + gold_assert(this->symtab_index_ != 0); + return this->symtab_index_; + } + + // Set the index of the symbol in the output file symbol table. + void + set_symtab_index(unsigned int index) + { + gold_assert(index != 0); + this->symtab_index_ = index; + } + + // Return whether this symbol already has an index in the output + // file symbol table. + bool + has_symtab_index() const + { return this->symtab_index_ != 0; } + + // Return the index of this symbol in the dynamic symbol table. A + // value of -1U means that this symbol is not going into the dynamic + // symbol table. This starts out as zero, and is set to a non-zero + // during Layout::finalize. It is an error to ask for the dynamic + // symbol table index before it has been set. + unsigned int + dynsym_index() const + { + gold_assert(this->dynsym_index_ != 0); + return this->dynsym_index_; + } + + // Set the index of the symbol in the dynamic symbol table. + void + set_dynsym_index(unsigned int index) + { + gold_assert(index != 0); + this->dynsym_index_ = index; + } + + // Return whether this symbol already has an index in the dynamic + // symbol table. + bool + has_dynsym_index() const + { return this->dynsym_index_ != 0; } + + // Return whether this symbol has an entry in the GOT section. + bool + has_got_offset() const + { return this->has_got_offset_; } + + // Return the offset into the GOT section of this symbol. + unsigned int + got_offset() const + { + gold_assert(this->has_got_offset()); + return this->got_offset_; + } + + // Set the GOT offset of this symbol. + void + set_got_offset(unsigned int got_offset) + { + this->has_got_offset_ = true; + this->got_offset_ = got_offset; + } + + // Return whether this symbol has an entry in the PLT section. + bool + has_plt_offset() const + { return this->has_plt_offset_; } + + // Return the offset into the PLT section of this symbol. + unsigned int + plt_offset() const + { + gold_assert(this->has_plt_offset()); + return this->plt_offset_; + } + + // Set the PLT offset of this symbol. + void + set_plt_offset(unsigned int plt_offset) + { + this->has_plt_offset_ = true; + this->plt_offset_ = plt_offset; + } + + // Return true if the final value of this symbol is known at link + // time. + bool + final_value_is_known(const General_options* options) const + { + if (options->is_shared()) + return false; + return this->source_ != FROM_OBJECT || !this->object()->is_dynamic(); + } + + // Return whether this is a defined symbol (not undefined or + // common). + bool + is_defined() const + { + return (this->source_ != FROM_OBJECT + || (this->shndx() != elfcpp::SHN_UNDEF + && this->shndx() != elfcpp::SHN_COMMON)); + } + + // Return true if this symbol is from a dynamic object. + bool + is_from_dynobj() const + { + return this->source_ == FROM_OBJECT && this->object()->is_dynamic(); + } + + // Return whether this is an undefined symbol. + bool + is_undefined() const + { + return this->source_ == FROM_OBJECT && this->shndx() == elfcpp::SHN_UNDEF; + } + + // Return whether this is a common symbol. + bool + is_common() const + { + return (this->source_ == FROM_OBJECT + && (this->shndx() == elfcpp::SHN_COMMON + || this->type_ == elfcpp::STT_COMMON)); + } + + // Return whether there should be a warning for references to this + // symbol. + bool + has_warning() const + { return this->has_warning_; } + + // Mark this symbol as having a warning. + void + set_has_warning() + { this->has_warning_ = true; } + + protected: + // Instances of this class should always be created at a specific + // size. + Symbol() + { memset(this, 0, sizeof *this); } + + // Initialize the general fields. + void + init_fields(const char* name, const char* version, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis); + + // Initialize fields from an ELF symbol in OBJECT. + template<int size, bool big_endian> + void + init_base(const char *name, const char* version, Object* object, + const elfcpp::Sym<size, big_endian>&); + + // Initialize fields for an Output_data. + void + init_base(const char* name, Output_data*, elfcpp::STT, elfcpp::STB, + elfcpp::STV, unsigned char nonvis, bool offset_is_from_end); + + // Initialize fields for an Output_segment. + void + init_base(const char* name, Output_segment* os, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis, Segment_offset_base offset_base); + + // Initialize fields for a constant. + void + init_base(const char* name, elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis); + + // Override existing symbol. + template<int size, bool big_endian> + void + override_base(const elfcpp::Sym<size, big_endian>&, Object* object, + const char* version); + + private: + Symbol(const Symbol&); + Symbol& operator=(const Symbol&); + + // Symbol name (expected to point into a Stringpool). + const char* name_; + // Symbol version (expected to point into a Stringpool). This may + // be NULL. + const char* version_; + + union + { + // This struct is used if SOURCE_ == FROM_OBJECT. + struct + { + // Object in which symbol is defined, or in which it was first + // seen. + Object* object; + // Section number in object_ in which symbol is defined. + unsigned int shndx; + } from_object; + + // This struct is used if SOURCE_ == IN_OUTPUT_DATA. + struct + { + // Output_data in which symbol is defined. Before + // Layout::finalize the symbol's value is an offset within the + // Output_data. + Output_data* output_data; + // True if the offset is from the end, false if the offset is + // from the beginning. + bool offset_is_from_end; + } in_output_data; + + // This struct is used if SOURCE_ == IN_OUTPUT_SEGMENT. + struct + { + // Output_segment in which the symbol is defined. Before + // Layout::finalize the symbol's value is an offset. + Output_segment* output_segment; + // The base to use for the offset before Layout::finalize. + Segment_offset_base offset_base; + } in_output_segment; + } u_; + + // The index of this symbol in the output file. If the symbol is + // not going into the output file, this value is -1U. This field + // starts as always holding zero. It is set to a non-zero value by + // Symbol_table::finalize. + unsigned int symtab_index_; + + // The index of this symbol in the dynamic symbol table. If the + // symbol is not going into the dynamic symbol table, this value is + // -1U. This field starts as always holding zero. It is set to a + // non-zero value during Layout::finalize. + unsigned int dynsym_index_; + + // If this symbol has an entry in the GOT section (has_got_offset_ + // is true), this is the offset from the start of the GOT section. + unsigned int got_offset_; + + // If this symbol has an entry in the PLT section (has_plt_offset_ + // is true), then this is the offset from the start of the PLT + // section. + unsigned int plt_offset_; + + // Symbol type. + elfcpp::STT type_ : 4; + // Symbol binding. + elfcpp::STB binding_ : 4; + // Symbol visibility. + elfcpp::STV visibility_ : 2; + // Rest of symbol st_other field. + unsigned int nonvis_ : 6; + // The type of symbol. + Source source_ : 3; + // True if this symbol always requires special target-specific + // handling. + bool is_target_special_ : 1; + // True if this is the default version of the symbol. + bool is_def_ : 1; + // True if this symbol really forwards to another symbol. This is + // used when we discover after the fact that two different entries + // in the hash table really refer to the same symbol. This will + // never be set for a symbol found in the hash table, but may be set + // for a symbol found in the list of symbols attached to an Object. + // It forwards to the symbol found in the forwarders_ map of + // Symbol_table. + bool is_forwarder_ : 1; + // True if this symbol needs to be in the dynamic symbol table. + bool needs_dynsym_entry_ : 1; + // True if we've seen this symbol in a regular object. + bool in_reg_ : 1; + // True if we've seen this symbol in a dynamic object. + bool in_dyn_ : 1; + // True if the symbol has an entry in the GOT section. + bool has_got_offset_ : 1; + // True if the symbol has an entry in the PLT section. + bool has_plt_offset_ : 1; + // True if there is a warning for this symbol. + bool has_warning_ : 1; +}; + +// The parts of a symbol which are size specific. Using a template +// derived class like this helps us use less space on a 32-bit system. + +template<int size> +class Sized_symbol : public Symbol +{ + public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Value_type; + typedef typename elfcpp::Elf_types<size>::Elf_WXword Size_type; + + Sized_symbol() + { } + + // Initialize fields from an ELF symbol in OBJECT. + template<bool big_endian> + void + init(const char *name, const char* version, Object* object, + const elfcpp::Sym<size, big_endian>&); + + // Initialize fields for an Output_data. + void + init(const char* name, Output_data*, Value_type value, Size_type symsize, + elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis, + bool offset_is_from_end); + + // Initialize fields for an Output_segment. + void + init(const char* name, Output_segment*, Value_type value, Size_type symsize, + elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis, + Segment_offset_base offset_base); + + // Initialize fields for a constant. + void + init(const char* name, Value_type value, Size_type symsize, + elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis); + + // Override existing symbol. + template<bool big_endian> + void + override(const elfcpp::Sym<size, big_endian>&, Object* object, + const char* version); + + // Return the symbol's value. + Value_type + value() const + { return this->value_; } + + // Return the symbol's size (we can't call this 'size' because that + // is a template parameter). + Size_type + symsize() const + { return this->symsize_; } + + // Set the symbol size. This is used when resolving common symbols. + void + set_symsize(Size_type symsize) + { this->symsize_ = symsize; } + + // Set the symbol value. This is called when we store the final + // values of the symbols into the symbol table. + void + set_value(Value_type value) + { this->value_ = value; } + + private: + Sized_symbol(const Sized_symbol&); + Sized_symbol& operator=(const Sized_symbol&); + + // Symbol value. Before Layout::finalize this is the offset in the + // input section. This is set to the final value during + // Layout::finalize. + Value_type value_; + // Symbol size. + Size_type symsize_; +}; + +// A struct describing a symbol defined by the linker, where the value +// of the symbol is defined based on an output section. This is used +// for symbols defined by the linker, like "_init_array_start". + +struct Define_symbol_in_section +{ + // The symbol name. + const char* name; + // The name of the output section with which this symbol should be + // associated. If there is no output section with that name, the + // symbol will be defined as zero. + const char* output_section; + // The offset of the symbol within the output section. This is an + // offset from the start of the output section, unless start_at_end + // is true, in which case this is an offset from the end of the + // output section. + uint64_t value; + // The size of the symbol. + uint64_t size; + // The symbol type. + elfcpp::STT type; + // The symbol binding. + elfcpp::STB binding; + // The symbol visibility. + elfcpp::STV visibility; + // The rest of the st_other field. + unsigned char nonvis; + // If true, the value field is an offset from the end of the output + // section. + bool offset_is_from_end; + // If true, this symbol is defined only if we see a reference to it. + bool only_if_ref; +}; + +// A struct describing a symbol defined by the linker, where the value +// of the symbol is defined based on a segment. This is used for +// symbols defined by the linker, like "_end". We describe the +// segment with which the symbol should be associated by its +// characteristics. If no segment meets these characteristics, the +// symbol will be defined as zero. If there is more than one segment +// which meets these characteristics, we will use the first one. + +struct Define_symbol_in_segment +{ + // The symbol name. + const char* name; + // The segment type where the symbol should be defined, typically + // PT_LOAD. + elfcpp::PT segment_type; + // Bitmask of segment flags which must be set. + elfcpp::PF segment_flags_set; + // Bitmask of segment flags which must be clear. + elfcpp::PF segment_flags_clear; + // The offset of the symbol within the segment. The offset is + // calculated from the position set by offset_base. + uint64_t value; + // The size of the symbol. + uint64_t size; + // The symbol type. + elfcpp::STT type; + // The symbol binding. + elfcpp::STB binding; + // The symbol visibility. + elfcpp::STV visibility; + // The rest of the st_other field. + unsigned char nonvis; + // The base from which we compute the offset. + Symbol::Segment_offset_base offset_base; + // If true, this symbol is defined only if we see a reference to it. + bool only_if_ref; +}; + +// This class manages warnings. Warnings are a GNU extension. When +// we see a section named .gnu.warning.SYM in an object file, and if +// we wind using the definition of SYM from that object file, then we +// will issue a warning for any relocation against SYM from a +// different object file. The text of the warning is the contents of +// the section. This is not precisely the definition used by the old +// GNU linker; the old GNU linker treated an occurrence of +// .gnu.warning.SYM as defining a warning symbol. A warning symbol +// would trigger a warning on any reference. However, it was +// inconsistent in that a warning in a dynamic object only triggered +// if there was no definition in a regular object. This linker is +// different in that we only issue a warning if we use the symbol +// definition from the same object file as the warning section. + +class Warnings +{ + public: + Warnings() + : warnings_() + { } + + // Add a warning for symbol NAME in section SHNDX in object OBJ. + void + add_warning(Symbol_table* symtab, const char* name, Object* obj, + unsigned int shndx); + + // For each symbol for which we should give a warning, make a note + // on the symbol. + void + note_warnings(Symbol_table* symtab); + + // Issue a warning for a reference to SYM at LOCATION. + void + issue_warning(const Symbol* sym, const std::string& location) const; + + private: + Warnings(const Warnings&); + Warnings& operator=(const Warnings&); + + // What we need to know to get the warning text. + struct Warning_location + { + // The object the warning is in. + Object* object; + // The index of the warning section. + unsigned int shndx; + // The warning text if we have already loaded it. + std::string text; + + Warning_location() + : object(NULL), shndx(0), text() + { } + + void + set(Object* o, unsigned int s) + { + this->object = o; + this->shndx = s; + } + + void + set_text(const char* t, off_t l) + { this->text.assign(t, l); } + }; + + // A mapping from warning symbol names (canonicalized in + // Symbol_table's namepool_ field) to + typedef Unordered_map<const char*, Warning_location> Warning_table; + + Warning_table warnings_; +}; + +// The main linker symbol table. + +class Symbol_table +{ + public: + Symbol_table(); + + ~Symbol_table(); + + // 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_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 based on an Output_data. It is a + // multiple definition error if this symbol is already defined. + Symbol* + define_in_output_data(const Target*, const char* name, const char* version, + Output_data*, uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool offset_is_from_end, bool only_if_ref); + + // Define a special symbol based on an Output_segment. It is a + // multiple definition error if this symbol is already defined. + Symbol* + define_in_output_segment(const Target*, const char* name, + const char* version, Output_segment*, + uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + Symbol::Segment_offset_base, bool only_if_ref); + + // Define a special symbol with a constant value. It is a multiple + // definition error if this symbol is already defined. + Symbol* + define_as_constant(const Target*, const char* name, const char* version, + uint64_t value, uint64_t symsize, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis, bool only_if_ref); + + // Define a set of symbols in output sections. + void + define_symbols(const Layout*, const Target*, int count, + const Define_symbol_in_section*); + + // Define a set of symbols in output segments. + void + define_symbols(const Layout*, const Target*, int count, + const Define_symbol_in_segment*); + + // Look up a symbol. + Symbol* + lookup(const char*, const char* version = NULL) const; + + // Return the real symbol associated with the forwarder symbol FROM. + Symbol* + resolve_forwards(const Symbol* from) const; + + // Return the bitsize (32 or 64) of the symbols in the table. + int + get_size() const + { return this->size_; } + + // Return the sized version of a symbol in this table. + template<int size> + Sized_symbol<size>* + get_sized_symbol(Symbol* ACCEPT_SIZE) const; + + template<int size> + const Sized_symbol<size>* + get_sized_symbol(const Symbol* ACCEPT_SIZE) const; + + // Return the count of undefined symbols seen. + int + saw_undefined() const + { return this->saw_undefined_; } + + // Allocate the common symbols + void + allocate_commons(const General_options&, Layout*); + + // Add a warning for symbol NAME in section SHNDX in object OBJ. + void + add_warning(const char* name, Object* obj, unsigned int shndx) + { this->warnings_.add_warning(this, name, obj, shndx); } + + // Canonicalize a symbol name for use in the hash table. + const char* + canonicalize_name(const char* name) + { return this->namepool_.add(name, NULL); } + + // Possibly issue a warning for a reference to SYM at LOCATION which + // is in OBJ. + void + issue_warning(const Symbol* sym, const std::string& location) const + { this->warnings_.issue_warning(sym, location); } + + // Set the dynamic symbol indexes. INDEX is the index of the first + // global dynamic symbol. Pointers to the symbols are stored into + // the vector. The names are stored into the Stringpool. This + // returns an updated dynamic symbol index. + unsigned int + set_dynsym_indexes(const General_options*, const Target*, unsigned int index, + std::vector<Symbol*>*, Stringpool*, Versions*); + + // Finalize the symbol table after we have set the final addresses + // of all the input sections. This sets the final symbol indexes, + // values and adds the names to *POOL. INDEX is the index of the + // first global symbol. OFF is the file offset of the global symbol + // table, DYNOFF is the offset of the globals in the dynamic symbol + // table, DYN_GLOBAL_INDEX is the index of the first global dynamic + // symbol, and DYNCOUNT is the number of global dynamic symbols. + // This records the parameters, and returns the new file offset. + off_t + finalize(unsigned int index, off_t off, off_t dynoff, + size_t dyn_global_index, size_t dyncount, Stringpool* pool); + + // Write out the global symbols. + void + write_globals(const Target*, const Stringpool*, const Stringpool*, + Output_file*) const; + + // Write out a section symbol. Return the updated offset. + void + write_section_symbol(const Target*, const Output_section*, Output_file*, + off_t) const; + + private: + Symbol_table(const Symbol_table&); + Symbol_table& operator=(const Symbol_table&); + + // Set the size (32 or 64) of the symbols in the table. + void + set_size(int size) + { this->size_ = size; } + + // Make FROM a forwarder symbol to TO. + void + make_forwarder(Symbol* from, Symbol* to); + + // Add a symbol. + template<int size, bool big_endian> + Symbol* + add_from_object(Object*, const char *name, Stringpool::Key name_key, + const char *version, Stringpool::Key version_key, + bool def, const elfcpp::Sym<size, big_endian>& sym); + + // Resolve symbols. + template<int size, bool big_endian> + static void + resolve(Sized_symbol<size>* to, + const elfcpp::Sym<size, big_endian>& sym, + Object*, const char* version); + + template<int size, bool big_endian> + static void + resolve(Sized_symbol<size>* to, const Sized_symbol<size>* from, + const char* version ACCEPT_SIZE_ENDIAN); + + // Define a special symbol. + template<int size, bool big_endian> + Sized_symbol<size>* + define_special_symbol(const Target* target, const char* name, + const char* version, bool only_if_ref + ACCEPT_SIZE_ENDIAN); + + // Define a symbol in an Output_data, sized version. + template<int size> + Sized_symbol<size>* + do_define_in_output_data(const Target*, const char* name, + const char* version, Output_data*, + typename elfcpp::Elf_types<size>::Elf_Addr value, + typename elfcpp::Elf_types<size>::Elf_WXword ssize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool offset_is_from_end, bool only_if_ref); + + // Define a symbol in an Output_segment, sized version. + template<int size> + Sized_symbol<size>* + do_define_in_output_segment( + const Target*, const char* name, const char* version, Output_segment* os, + typename elfcpp::Elf_types<size>::Elf_Addr value, + typename elfcpp::Elf_types<size>::Elf_WXword ssize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + Symbol::Segment_offset_base offset_base, bool only_if_ref); + + // Define a symbol as a constant, sized version. + template<int size> + Sized_symbol<size>* + do_define_as_constant( + const Target*, const char* name, const char* version, + typename elfcpp::Elf_types<size>::Elf_Addr value, + typename elfcpp::Elf_types<size>::Elf_WXword ssize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool only_if_ref); + + // Allocate the common symbols, sized version. + template<int size> + void + do_allocate_commons(const General_options&, Layout*); + + // Finalize symbols specialized for size. + template<int size> + off_t + sized_finalize(unsigned int, off_t, Stringpool*); + + // Write globals specialized for size and endianness. + template<int size, bool big_endian> + void + sized_write_globals(const Target*, const Stringpool*, const Stringpool*, + Output_file*) const; + + // Write out a symbol to P. + template<int size, bool big_endian> + void + sized_write_symbol(Sized_symbol<size>*, unsigned int shndx, + const Stringpool*, unsigned char* p + ACCEPT_SIZE_ENDIAN) const; + + // Write out a section symbol, specialized for size and endianness. + template<int size, bool big_endian> + void + sized_write_section_symbol(const Output_section*, Output_file*, off_t) const; + + // The type of the symbol hash table. + + typedef std::pair<Stringpool::Key, Stringpool::Key> Symbol_table_key; + + struct Symbol_table_hash + { + size_t + operator()(const Symbol_table_key&) const; + }; + + struct Symbol_table_eq + { + bool + operator()(const Symbol_table_key&, const Symbol_table_key&) const; + }; + + typedef Unordered_map<Symbol_table_key, Symbol*, Symbol_table_hash, + Symbol_table_eq> Symbol_table_type; + + // The type of the list of common symbols. + + typedef std::vector<Symbol*> Commons_type; + + // The size of the symbols in the symbol table (32 or 64). + int size_; + + // We increment this every time we see a new undefined symbol, for + // use in archive groups. + int saw_undefined_; + + // The index of the first global symbol in the output file. + unsigned int first_global_index_; + + // The file offset within the output symtab section where we should + // write the table. + off_t offset_; + + // The number of global symbols we want to write out. + size_t output_count_; + + // The file offset of the global dynamic symbols, or 0 if none. + off_t dynamic_offset_; + + // The index of the first global dynamic symbol. + unsigned int first_dynamic_global_index_; + + // The number of global dynamic symbols, or 0 if none. + off_t dynamic_count_; + + // The symbol hash table. + Symbol_table_type table_; + + // A pool of symbol names. This is used for all global symbols. + // Entries in the hash table point into this pool. + Stringpool namepool_; + + // Forwarding symbols. + Unordered_map<const Symbol*, Symbol*> forwarders_; + + // We don't expect there to be very many common symbols, so we keep + // a list of them. When we find a common symbol we add it to this + // list. It is possible that by the time we process the list the + // symbol is no longer a common symbol. It may also have become a + // forwarder. + Commons_type commons_; + + // Manage symbol warnings. + Warnings warnings_; +}; + +// We inline get_sized_symbol for efficiency. + +template<int size> +Sized_symbol<size>* +Symbol_table::get_sized_symbol(Symbol* sym ACCEPT_SIZE) const +{ + gold_assert(size == this->get_size()); + return static_cast<Sized_symbol<size>*>(sym); +} + +template<int size> +const Sized_symbol<size>* +Symbol_table::get_sized_symbol(const Symbol* sym ACCEPT_SIZE) const +{ + gold_assert(size == this->get_size()); + return static_cast<const Sized_symbol<size>*>(sym); +} + +} // End namespace gold. + +#endif // !defined(GOLD_SYMTAB_H) diff --git a/gold/target-reloc.h b/gold/target-reloc.h new file mode 100644 index 000000000000..d282805dbebb --- /dev/null +++ b/gold/target-reloc.h @@ -0,0 +1,193 @@ +// target-reloc.h -- target specific relocation support -*- C++ -*- + +#ifndef GOLD_TARGET_RELOC_H +#define GOLD_TARGET_RELOC_H + +#include "elfcpp.h" +#include "object.h" +#include "symtab.h" +#include "reloc-types.h" + +namespace gold +{ + +// This function implements the generic part of reloc scanning. This +// is an inline function which takes a class whose operator() +// implements the machine specific part of scanning. We do it this +// way to avoidmaking a function call for each relocation, and to +// avoid repeating the generic code for each target. + +template<int size, bool big_endian, typename Target_type, int sh_type, + typename Scan> +inline void +scan_relocs( + const General_options& options, + Symbol_table* symtab, + Layout* layout, + Target_type* target, + Sized_relobj<size, big_endian>* object, + unsigned int data_shndx, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_count, + const unsigned char* plocal_syms, + Symbol** global_syms) +{ + typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype; + const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size; + const int sym_size = elfcpp::Elf_sizes<size>::sym_size; + Scan scan; + + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) + { + Reltype reloc(prelocs); + + typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info(); + unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); + unsigned int r_type = elfcpp::elf_r_type<size>(r_info); + + if (r_sym < local_count) + { + gold_assert(plocal_syms != NULL); + typename elfcpp::Sym<size, big_endian> lsym(plocal_syms + + r_sym * sym_size); + const unsigned int shndx = lsym.get_st_shndx(); + if (shndx < elfcpp::SHN_LORESERVE + && shndx != elfcpp::SHN_UNDEF + && !object->is_section_included(lsym.get_st_shndx())) + { + // RELOC is a relocation against a local symbol in a + // section we are discarding. We can ignore this + // relocation. It will eventually become a reloc + // against the value zero. + // + // FIXME: We should issue a warning if this is an + // allocated section; is this the best place to do it? + // + // FIXME: The old GNU linker would in some cases look + // for the linkonce section which caused this section to + // be discarded, and, if the other section was the same + // size, change the reloc to refer to the other section. + // That seems risky and weird to me, and I don't know of + // any case where it is actually required. + + continue; + } + + scan.local(options, symtab, layout, target, object, data_shndx, + reloc, r_type, lsym); + } + else + { + Symbol* gsym = global_syms[r_sym - local_count]; + gold_assert(gsym != NULL); + if (gsym->is_forwarder()) + gsym = symtab->resolve_forwards(gsym); + + scan.global(options, symtab, layout, target, object, data_shndx, + reloc, r_type, gsym); + } + } +} + +// This function implements the generic part of relocation processing. +// This is an inline function which take a class whose operator() +// implements the machine specific part of relocation. We do it this +// way to avoid making a function call for each relocation, and to +// avoid repeating the generic relocation handling code for each +// target. + +// SIZE is the ELF size: 32 or 64. BIG_ENDIAN is the endianness of +// the data. SH_TYPE is the section type: SHT_REL or SHT_RELA. +// RELOCATE implements operator() to do a relocation. + +// PRELOCS points to the relocation data. RELOC_COUNT is the number +// of relocs. VIEW is the section data, VIEW_ADDRESS is its memory +// address, and VIEW_SIZE is the size. + +template<int size, bool big_endian, typename Target_type, int sh_type, + typename Relocate> +inline void +relocate_section( + const Relocate_info<size, big_endian>* relinfo, + Target_type* target, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr view_address, + off_t view_size) +{ + typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype; + const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size; + Relocate relocate; + + unsigned int local_count = relinfo->local_symbol_count; + const typename Sized_relobj<size, big_endian>::Local_values* local_values = + relinfo->local_values; + const Symbol* const * global_syms = relinfo->symbols; + + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) + { + Reltype reloc(prelocs); + + off_t offset = reloc.get_r_offset(); + + typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info(); + unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); + unsigned int r_type = elfcpp::elf_r_type<size>(r_info); + + const Sized_symbol<size>* sym; + + Symbol_value<size> symval; + const Symbol_value<size> *psymval; + if (r_sym < local_count) + { + sym = NULL; + psymval = &(*local_values)[r_sym]; + } + else + { + const Symbol* gsym = global_syms[r_sym - local_count]; + gold_assert(gsym != NULL); + if (gsym->is_forwarder()) + gsym = relinfo->symtab->resolve_forwards(gsym); + + sym = static_cast<const Sized_symbol<size>*>(gsym); + if (sym->has_symtab_index()) + symval.set_output_symtab_index(sym->symtab_index()); + else + symval.set_no_output_symtab_entry(); + symval.set_output_value(sym->value()); + psymval = &symval; + } + + if (!relocate.relocate(relinfo, target, i, reloc, r_type, sym, psymval, + view + offset, view_address + offset, view_size)) + continue; + + if (offset < 0 || offset >= view_size) + { + fprintf(stderr, _("%s: %s: reloc has bad offset %zu\n"), + program_name, relinfo->location(i, offset).c_str(), + static_cast<size_t>(offset)); + gold_exit(false); + } + + if (sym != NULL + && sym->is_undefined() + && sym->binding() != elfcpp::STB_WEAK) + { + fprintf(stderr, _("%s: %s: undefined reference to '%s'\n"), + program_name, relinfo->location(i, offset).c_str(), + sym->name()); + // gold_exit(false); + } + + if (sym != NULL && sym->has_warning()) + relinfo->symtab->issue_warning(sym, relinfo->location(i, offset)); + } +} + +} // End namespace gold. + +#endif // !defined(GOLD_TARGET_RELOC_H) diff --git a/gold/target-select.cc b/gold/target-select.cc new file mode 100644 index 000000000000..d15d0e346d1d --- /dev/null +++ b/gold/target-select.cc @@ -0,0 +1,52 @@ +// target-select.cc -- select a target for an object file + +#include "gold.h" + +#include "elfcpp.h" +#include "target-select.h" + +namespace +{ + +// The start of the list of target selectors. + +gold::Target_selector* target_selectors; + +} // End anonymous namespace. + +namespace gold +{ + +// Construct a Target_selector, which means adding it to the linked +// list. This runs at global constructor time, so we want it to be +// fast. + +Target_selector::Target_selector(int machine, int size, bool big_endian) + : machine_(machine), size_(size), big_endian_(big_endian) +{ + this->next_ = target_selectors; + target_selectors = this; +} + +// Find the target for an ELF file. + +extern Target* +select_target(int machine, int size, bool big_endian, int osabi, + int abiversion) +{ + for (Target_selector* p = target_selectors; p != NULL; p = p->next()) + { + int pmach = p->machine(); + if ((pmach == machine || pmach == elfcpp::EM_NONE) + && p->size() == size + && p->big_endian() ? big_endian : !big_endian) + { + Target* ret = p->recognize(machine, osabi, abiversion); + if (ret != NULL) + return ret; + } + } + return NULL; +} + +} // End namespace gold. diff --git a/gold/target-select.h b/gold/target-select.h new file mode 100644 index 000000000000..8ccd75fac757 --- /dev/null +++ b/gold/target-select.h @@ -0,0 +1,69 @@ +// target-select.h -- select a target for an object file -*- C++ -*- + +#ifndef GOLD_TARGET_SELECT_H +#define GOLD_TARGET_SELECT_H + +namespace gold +{ + +class Target; + +// We want to avoid a master list of targets, which implies using a +// global constructor. And we also want the program to start up as +// quickly as possible, which implies avoiding global constructors. +// We compromise on a very simple global constructor. We use a target +// selector, which specifies an ELF machine number and a recognition +// function. We use global constructors to build a linked list of +// target selectors--a simple pointer list, not a std::list. + +class Target_selector +{ + public: + // Create a target selector for a specific machine number, size (32 + // or 64), and endianness. The machine number can be EM_NONE to + // test for any machine number. + Target_selector(int machine, int size, bool big_endian); + + virtual ~Target_selector() + { } + + // If we can handle this target, return a pointer to a target + // structure. The size and endianness are known. + virtual Target* recognize(int machine, int osabi, int abiversion) = 0; + + // Return the next Target_selector in the linked list. + Target_selector* + next() const + { return this->next_; } + + // Return the machine number this selector is looking for, which can + // be EM_NONE to match any machine number. + int + machine() const + { return this->machine_; } + + // Return the size this is looking for (32 or 64). + int + size() const + { return this->size_; } + + // Return the endianness this is looking for. + bool + big_endian() const + { return this->big_endian_; } + + private: + int machine_; + int size_; + bool big_endian_; + Target_selector* next_; +}; + +// Select the target for an ELF file. + +extern Target* select_target(int machine, int size, bool big_endian, + int osabi, int abiversion); + +} // End namespace gold. + +#endif // !defined(GOLD_TARGET_SELECT_H) diff --git a/gold/target.h b/gold/target.h new file mode 100644 index 000000000000..9181a93193d5 --- /dev/null +++ b/gold/target.h @@ -0,0 +1,210 @@ +// target.h -- target support for gold -*- C++ -*- + +// The abstract class Target is the interface for target specific +// support. It defines abstract methods which each target must +// implement. Typically there will be one target per processor, but +// in some cases it may be necessary to have subclasses. + +// For speed and consistency we want to use inline functions to handle +// relocation processing. So besides implementations of the abstract +// methods, each target is expected to define a template +// specialization of the relocation functions. + +#ifndef GOLD_TARGET_H +#define GOLD_TARGET_H + +#include "elfcpp.h" + +namespace gold +{ + +class General_options; +class Object; +template<int size, bool big_endian> +class Sized_relobj; +template<int size, bool big_endian> +struct Relocate_info; +class Symbol; +template<int size> +class Sized_symbol; +class Symbol_table; + +// The abstract class for target specific handling. + +class Target +{ + public: + virtual ~Target() + { } + + // Return the bit size that this target implements. This should + // return 32 or 64. + int + get_size() const + { return this->pti_->size; } + + // Return whether this target is big-endian. + bool + is_big_endian() const + { return this->pti_->is_big_endian; } + + // Machine code to store in e_machine field of ELF header. + elfcpp::EM + machine_code() const + { return this->pti_->machine_code; } + + // Whether this target has a specific make_symbol function. + bool + has_make_symbol() const + { return this->pti_->has_make_symbol; } + + // Whether this target has a specific resolve function. + bool + 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 + { return this->pti_->text_segment_address; } + + // Return the ABI specified page size. + uint64_t + abi_pagesize() const + { return this->pti_->abi_pagesize; } + + // Return the common page size used on actual systems. + uint64_t + common_pagesize() const + { return this->pti_->common_pagesize; } + + // This is called to tell the target to complete any sections it is + // handling. After this all sections must have their final size. + void + finalize_sections(const General_options* options, Layout* layout) + { return this->do_finalize_sections(options, layout); } + + protected: + // This struct holds the constant information for a child class. We + // use a struct to avoid the overhead of virtual function calls for + // simple information. + struct Target_info + { + // Address size (32 or 64). + int size; + // Whether the target is big endian. + bool is_big_endian; + // The code to store in the e_machine field of the ELF header. + elfcpp::EM machine_code; + // Whether this target has a specific make_symbol function. + 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. + uint64_t abi_pagesize; + // The common page size used by actual implementations. + uint64_t common_pagesize; + }; + + Target(const Target_info* pti) + : pti_(pti) + { } + + // Virtual function which may be implemented by the child class. + virtual void + do_finalize_sections(const General_options*, Layout*) + { } + + private: + Target(const Target&); + Target& operator=(const Target&); + + // The target information. + const Target_info* pti_; +}; + +// The abstract class for a specific size and endianness of target. +// Each actual target implementation class should derive from an +// instantiation of Sized_target. + +template<int size, bool big_endian> +class Sized_target : public Target +{ + public: + // Make a new symbol table entry for the target. This should be + // overridden by a target which needs additional information in the + // symbol table. This will only be called if has_make_symbol() + // returns true. + virtual Sized_symbol<size>* + make_symbol() const + { gold_unreachable(); } + + // Resolve a symbol for the target. This should be overridden by a + // target which needs to take special action. TO is the + // pre-existing symbol. SYM is the new symbol, seen in OBJECT. + // VERSION is the version of SYM. This will only be called if + // has_resolve() returns true. + virtual void + resolve(Symbol*, const elfcpp::Sym<size, big_endian>&, Object*, + const char*) + { gold_unreachable(); } + + // Scan the relocs for a section, and record any information + // required for the symbol. OPTIONS is the command line options. + // SYMTAB is the symbol table. OBJECT is the object in which the + // section appears. DATA_SHNDX is the section index that these + // relocs apply to. SH_TYPE is the type of the relocation section, + // SHT_REL or SHT_RELA. PRELOCS points to the relocation data. + // RELOC_COUNT is the number of relocs. LOCAL_SYMBOL_COUNT is the + // number of local symbols. PLOCAL_SYMBOLS points to the local + // symbol data from OBJECT. GLOBAL_SYMBOLS is the array of pointers + // to the global symbol table from OBJECT. + virtual void + scan_relocs(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Sized_relobj<size, big_endian>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_symbol_count, + const unsigned char* plocal_symbols, + Symbol** global_symbols) = 0; + + // Relocate section data. SH_TYPE is the type of the relocation + // section, SHT_REL or SHT_RELA. PRELOCS points to the relocation + // information. RELOC_COUNT is the number of relocs. VIEW is a + // view into the output file holding the section contents, + // VIEW_ADDRESS is the virtual address of the view, and VIEW_SIZE is + // the size of the view. + virtual void + relocate_section(const Relocate_info<size, big_endian>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + typename elfcpp::Elf_types<size>::Elf_Addr view_address, + off_t view_size) = 0; + + protected: + Sized_target(const Target::Target_info* pti) + : Target(pti) + { + gold_assert(pti->size == size); + gold_assert(pti->is_big_endian ? big_endian : !big_endian); + } +}; + +} // End namespace gold. + +#endif // !defined(GOLD_TARGET_H) diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am new file mode 100644 index 000000000000..122be70592b2 --- /dev/null +++ b/gold/testsuite/Makefile.am @@ -0,0 +1,22 @@ +# Process this file with automake to generate Makefile.in + +AUTOMAKE_OPTIONS = + +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CXXFLAGS) + +INCLUDES = -D_GNU_SOURCE \ + -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../../include \ + -I$(srcdir)/../../elfcpp \ + -DLOCALEDIR="\"$(datadir)/locale\"" \ + @INCINTL@ + +TESTS = object_unittest + +check_LIBRARIES = libgoldtest.a +libgoldtest_a_SOURCES = test.cc testmain.cc testfile.cc + +LDADD = libgoldtest.a ../libgold.a + +check_PROGRAMS = object_unittest + +object_unittest_SOURCES = object_unittest.cc diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in new file mode 100644 index 000000000000..4967af6c862c --- /dev/null +++ b/gold/testsuite/Makefile.in @@ -0,0 +1,521 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Process this file with automake to generate Makefile.in +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +check_PROGRAMS = object_unittest$(EXEEXT) +subdir = testsuite +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \ + $(top_srcdir)/../config/lead-dot.m4 \ + $(top_srcdir)/../config/progtest.m4 \ + $(top_srcdir)/../config/po.m4 $(top_srcdir)/../config/nls.m4 \ + $(top_srcdir)/../config/gettext-sister.m4 \ + $(top_srcdir)/../bfd/warning.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +AR = ar +ARFLAGS = cru +libgoldtest_a_AR = $(AR) $(ARFLAGS) +libgoldtest_a_LIBADD = +am_libgoldtest_a_OBJECTS = test.$(OBJEXT) testmain.$(OBJEXT) \ + testfile.$(OBJEXT) +libgoldtest_a_OBJECTS = $(am_libgoldtest_a_OBJECTS) +am_object_unittest_OBJECTS = object_unittest.$(OBJEXT) +object_unittest_OBJECTS = $(am_object_unittest_OBJECTS) +object_unittest_LDADD = $(LDADD) +object_unittest_DEPENDENCIES = libgoldtest.a ../libgold.a +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/../depcomp +am__depfiles_maybe = depfiles +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +SOURCES = $(libgoldtest_a_SOURCES) $(object_unittest_SOURCES) +DIST_SOURCES = $(libgoldtest_a_SOURCES) $(object_unittest_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GENCAT = @GENCAT@ +GMSGFMT = @GMSGFMT@ +INCINTL = @INCINTL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +LDFLAGS = @LDFLAGS@ +LFS_CXXFLAGS = @LFS_CXXFLAGS@ +LIBINTL = @LIBINTL@ +LIBINTL_DEP = @LIBINTL_DEP@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +NO_WERROR = @NO_WERROR@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +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_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +AUTOMAKE_OPTIONS = +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CXXFLAGS) +INCLUDES = -D_GNU_SOURCE \ + -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../../include \ + -I$(srcdir)/../../elfcpp \ + -DLOCALEDIR="\"$(datadir)/locale\"" \ + @INCINTL@ + +TESTS = object_unittest +check_LIBRARIES = libgoldtest.a +libgoldtest_a_SOURCES = test.cc testmain.cc testfile.cc +LDADD = libgoldtest.a ../libgold.a +object_unittest_SOURCES = object_unittest.cc +all: all-am + +.SUFFIXES: +.SUFFIXES: .cc .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign testsuite/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign testsuite/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +clean-checkLIBRARIES: + -test -z "$(check_LIBRARIES)" || rm -f $(check_LIBRARIES) +libgoldtest.a: $(libgoldtest_a_OBJECTS) $(libgoldtest_a_DEPENDENCIES) + -rm -f libgoldtest.a + $(libgoldtest_a_AR) libgoldtest.a $(libgoldtest_a_OBJECTS) $(libgoldtest_a_LIBADD) + $(RANLIB) libgoldtest.a + +clean-checkPROGRAMS: + -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS) +object_unittest$(EXEEXT): $(object_unittest_OBJECTS) $(object_unittest_DEPENDENCIES) + @rm -f object_unittest$(EXEEXT) + $(CXXLINK) $(object_unittest_LDFLAGS) $(object_unittest_OBJECTS) $(object_unittest_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/object_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testfile.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testmain.Po@am__quote@ + +.cc.o: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@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 $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@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) '$<'` +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list='$(TESTS)'; \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *" $$tst "*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + echo "XPASS: $$tst"; \ + ;; \ + *) \ + echo "PASS: $$tst"; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *" $$tst "*) \ + xfail=`expr $$xfail + 1`; \ + echo "XFAIL: $$tst"; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + echo "FAIL: $$tst"; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + echo "SKIP: $$tst"; \ + fi; \ + done; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="All $$all tests passed"; \ + else \ + banner="All $$all tests behaved as expected ($$xfail expected failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all tests failed"; \ + else \ + banner="$$failed of $$all tests did not behave as expected ($$xpass unexpected passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + skipped="($$skip tests were not run)"; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + test -z "$$skipped" || echo "$$skipped"; \ + test -z "$$report" || echo "$$report"; \ + echo "$$dashes"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_LIBRARIES) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-checkLIBRARIES clean-checkPROGRAMS clean-generic \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \ + clean-checkLIBRARIES clean-checkPROGRAMS clean-generic ctags \ + distclean distclean-compile distclean-generic distclean-tags \ + distdir dvi dvi-am html html-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gold/testsuite/object_unittest.cc b/gold/testsuite/object_unittest.cc new file mode 100644 index 000000000000..c15237d822bd --- /dev/null +++ b/gold/testsuite/object_unittest.cc @@ -0,0 +1,41 @@ +// object_unittest.cc -- test Object, Relobj, etc. + +#include "gold.h" + +#include "object.h" + +#include "test.h" +#include "testfile.h" + +namespace gold_testsuite +{ + +using namespace gold; + +// Test basic Object functionality. + +bool +Object_test(Test_report*) +{ + Input_file input_file("test.o", test_file_1, test_file_1_size); + Object* object = make_elf_object("test.o", &input_file, 0, + test_file_1, test_file_1_size); + CHECK(object->name() == "test.o"); + CHECK(!object->is_dynamic()); + CHECK(object->target() == target_test_pointer); + CHECK(object->is_locked()); + object->unlock(); + CHECK(!object->is_locked()); + object->lock(); + CHECK(object->shnum() == 5); + CHECK(object->section_name(0).empty()); + CHECK(object->section_name(1) == ".test"); + CHECK(object->section_flags(0) == 0); + CHECK(object->section_flags(1) == elfcpp::SHF_ALLOC); + object->unlock(); + return true; +} + +Register_test object_register("Object", Object_test); + +} // End namespace gold_testsuite. diff --git a/gold/testsuite/test.cc b/gold/testsuite/test.cc new file mode 100644 index 000000000000..37a4ada9ab06 --- /dev/null +++ b/gold/testsuite/test.cc @@ -0,0 +1,78 @@ +// test.cc -- simplistic test framework for gold. + +#include "gold.h" + +#include <cstdio> + +#include "test.h" + +namespace gold_testsuite +{ + +// Test_framework methods. + +// The current test being run. + +Test_report* Test_framework::current_report; + +// Run a test. + +void +Test_framework::run(const char *name, bool (*pfn)(Test_report*)) +{ + this->testname_ = name; + this->current_fail_ = false; + + Test_report tr(this); + Test_framework::current_report = &tr; + + if ((*pfn)(&tr) && !this->current_fail_) + { + printf("PASS: %s\n", name); + ++this->passes_; + } + else + { + printf("FAIL: %s\n", name); + ++this->failures_; + } + + Test_framework::current_report = NULL; + this->testname_ = NULL; +} + +// Let a test report an error. + +void +Test_framework::error(const char* message) +{ + printf("ERROR: %s: %s\n", this->testname_, message); + this->fail(); +} + +// Register_test methods. + +// Linked list of all registered tests. + +Register_test* Register_test::all_tests; + +// Register a test. + +Register_test::Register_test(const char* name, bool (*pfn)(Test_report*)) + : name_(name), pfn_(pfn), next_(Register_test::all_tests) +{ + Register_test::all_tests = this; +} + +// Run all registered tests. + +void +Register_test::run_tests(Test_framework* tf) +{ + for (Register_test* p = Register_test::all_tests; + p != NULL; + p = p->next_) + tf->run(p->name_, p->pfn_); +} + +} // End namespace gold_testsuite. diff --git a/gold/testsuite/test.h b/gold/testsuite/test.h new file mode 100644 index 000000000000..e0556e267316 --- /dev/null +++ b/gold/testsuite/test.h @@ -0,0 +1,121 @@ +// test.h -- simplistic test framework for gold unittests -*- C++ -*- + +#ifndef GOLD_TESTSUITE_TEST_H +#define GOLD_TESTSUITE_TEST_H + +namespace gold_testsuite +{ + +class Test_report; + +// This class handles basic test framework functionality. + +class Test_framework +{ + public: + Test_framework() + : testname_(NULL), current_fail_(0), passes_(0), failures_(0) + { } + + // Return number of failures. + unsigned int + failures() const + { return this->failures_; } + + // Run a test. + void + run(const char* name, bool (*pfn)(Test_report*)); + + // Get the current Test_report. This is used by the test support + // macros. + static Test_report* + report() + { return Test_framework::current_report; } + + private: + friend class Test_report; + + // Cause the current test to fail. + void + fail() + { ++this->current_fail_ = true; } + + // Report an error from the current test. + void + error(const char* message); + + // Current Test_report. This is a static variable valid while a + // test is being run. + static Test_report* current_report; + + // Current test being run. + const char* testname_; + // Whether the current test is failing. + bool current_fail_; + // Total number of passeed tests. + unsigned int passes_; + // Total number of failed tests. + unsigned int failures_; +}; + +// An instance of this class is passed to each test function. + +class Test_report +{ +public: + Test_report(Test_framework* tf) + : tf_(tf) + { } + + // Mark the test as failing. + void + fail() + { this->tf_->fail(); } + + // Report an error. + void + error(const char* message) + { this->tf_->error(message); } + +private: + Test_framework* tf_; +}; + +// This class registers a test function so that the testsuite runs it. + +class Register_test +{ + public: + Register_test(const char* name, bool (*pfn)(Test_report*)); + + // Run all registered tests. + static void + run_tests(Test_framework*); + + private: + // Linked list of all tests. + static Register_test* all_tests; + + // Test name. + const char* name_; + // Function to call. It should return true if the test passes, + // false if it fails. + bool (*pfn_)(Test_report*); + // Next test in linked list. + Register_test* next_; +}; + +} // End namespace gold_testsuite. + +// These macros are for convenient use in tests. + +// Check that a condition is true. If it is false, report a failure. + +#define CHECK(cond) \ + ((cond) ? 0 : (::gold_testsuite::Test_framework::report()->fail(), 0)) + +// Report an error during a test. + +#define ERROR(msg) (::gold_testsuite::Test_framework::report()->error(msg)) + +#endif // !defined(GOLD_TESTSUITE_TEST_H) diff --git a/gold/testsuite/testfile.cc b/gold/testsuite/testfile.cc new file mode 100644 index 000000000000..bfe3c9d7a18b --- /dev/null +++ b/gold/testsuite/testfile.cc @@ -0,0 +1,261 @@ +// testfile.cc -- Dummy ELF objects for testing purposes. + +#include "gold.h" + +#include "target.h" +#include "target-select.h" + +#include "test.h" +#include "testfile.h" + +namespace gold_testsuite +{ + +using namespace gold; + +// A Target used for testing purposes. + +class Target_test : public Sized_target<32, false> +{ + public: + Target_test() + : Sized_target<32, false>(&test_target_info) + { } + + void + scan_relocs(const General_options&, Symbol_table*, Layout*, + Sized_relobj<32, false>*, unsigned int, unsigned int, + const unsigned char*, size_t, size_t, const unsigned char*, + Symbol**) + { ERROR("call to Target_test::scan_relocs"); } + + void + relocate_section(const Relocate_info<32, false>*, unsigned int, + const unsigned char*, size_t, unsigned char*, + elfcpp::Elf_types<32>::Elf_Addr, off_t) + { ERROR("call to Target_test::relocate_section"); } + + static const Target::Target_info test_target_info; +}; + +const Target::Target_info Target_test::test_target_info = +{ + 32, // size + false, // is_big_endian + static_cast<elfcpp::EM>(0xffff), // machine_code + false, // has_make_symbol + false, // has_resolve + "/dummy", // dynamic_linker + 0x08000000, // text_segment_address + 0x1000, // abi_pagesize + 0x1000 // common_pagesize +}; + +// The single test target. + +Target_test target_test; + +// A pointer to the test target. This is used in CHECKs. + +Target* target_test_pointer = &target_test; + +// Select the test target. + +class Target_selector_test : public Target_selector +{ + public: + Target_selector_test() + : Target_selector(0xffff, 32, false) + { } + + Target* + recognize(int, int, int) + { return &target_test; } +}; + +// Register the test target selector. + +Target_selector_test target_selector_test; + +// A simple ELF object with one empty section, named ".test" and one +// globally visible symbol named "test". + +const unsigned char test_file_1[] = +{ + // Ehdr + // EI_MAG[0-3] + 0x7f, 'E', 'L', 'F', + // EI_CLASS: 32 bit. + 1, + // EI_DATA: little endian + 1, + // EI_VERSION + 1, + // EI_OSABI + 0, + // EI_ABIVERSION + 0, + // EI_PAD + 0, 0, 0, 0, 0, 0, 0, + // e_type: ET_REL + 1, 0, + // e_machine: a magic value used for testing. + 0xff, 0xff, + // e_version + 1, 0, 0, 0, + // e_entry + 0, 0, 0, 0, + // e_phoff + 0, 0, 0, 0, + // e_shoff: starts right after file header + 52, 0, 0, 0, + // e_flags + 0, 0, 0, 0, + // e_ehsize + 52, 0, + // e_phentsize + 32, 0, + // e_phnum + 0, 0, + // e_shentsize + 40, 0, + // e_shnum: dummy, .test, .symtab, .strtab, .shstrtab + 5, 0, + // e_shstrndx + 4, 0, + + // Offset 52 + // Shdr 0: dummy entry + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + // Offset 92 + // Shdr 1: .test + // sh_name: after initial null + 1, 0, 0, 0, + // sh_type: SHT_PROGBITS + 1, 0, 0, 0, + // sh_flags: SHF_ALLOC + 2, 0, 0, 0, + // sh_addr + 0, 0, 0, 0, + // sh_offset: after file header + 5 section headers + 252, 0, 0, 0, + // sh_size + 0, 0, 0, 0, + // sh_link + 0, 0, 0, 0, + // sh_info + 0, 0, 0, 0, + // sh_addralign + 1, 0, 0, 0, + // sh_entsize + 0, 0, 0, 0, + + // Offset 132 + // Shdr 2: .symtab + // sh_name: 1 null byte + ".test\0" + 7, 0, 0, 0, + // sh_type: SHT_SYMTAB + 2, 0, 0, 0, + // sh_flags + 0, 0, 0, 0, + // sh_addr + 0, 0, 0, 0, + // sh_offset: after file header + 5 section headers + empty section + 252, 0, 0, 0, + // sh_size: two symbols: dummy symbol + test symbol + 32, 0, 0, 0, + // sh_link: to .strtab + 3, 0, 0, 0, + // sh_info: one local symbol, the dummy symbol + 1, 0, 0, 0, + // sh_addralign + 4, 0, 0, 0, + // sh_entsize: size of symbol + 16, 0, 0, 0, + + // Offset 172 + // Shdr 3: .strtab + // sh_name: 1 null byte + ".test\0" + ".symtab\0" + 15, 0, 0, 0, + // sh_type: SHT_STRTAB + 3, 0, 0, 0, + // sh_flags + 0, 0, 0, 0, + // sh_addr + 0, 0, 0, 0, + // sh_offset: after .symtab section. 284 == 0x11c + 0x1c, 0x1, 0, 0, + // sh_size: 1 null byte + "test\0" + 6, 0, 0, 0, + // sh_link + 0, 0, 0, 0, + // sh_info + 0, 0, 0, 0, + // sh_addralign + 1, 0, 0, 0, + // sh_entsize + 0, 0, 0, 0, + + // Offset 212 + // Shdr 4: .shstrtab + // sh_name: 1 null byte + ".test\0" + ".symtab\0" + ".strtab\0" + 23, 0, 0, 0, + // sh_type: SHT_STRTAB + 3, 0, 0, 0, + // sh_flags + 0, 0, 0, 0, + // sh_addr + 0, 0, 0, 0, + // sh_offset: after .strtab section. 290 == 0x122 + 0x22, 0x1, 0, 0, + // sh_size: all section names + 33, 0, 0, 0, + // sh_link + 0, 0, 0, 0, + // sh_info + 0, 0, 0, 0, + // sh_addralign + 1, 0, 0, 0, + // sh_entsize + 0, 0, 0, 0, + + // Offset 252 + // Contents of .symtab section + // Symbol 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + // Offset 268 + // Symbol 1 + // st_name + 1, 0, 0, 0, + // st_value + 0, 0, 0, 0, + // st_size + 0, 0, 0, 0, + // st_info: STT_NOTYPE, STB_GLOBAL + 0x10, + // st_other + 0, + // st_shndx: In .test + 1, 0, + + // Offset 284 + // Contents of .strtab section + '\0', + 't', 'e', 's', 't', '\0', + + // Offset 290 + // Contents of .shstrtab section + '\0', + '.', 't', 'e', 's', 't', '\0', + '.', 's', 'y', 'm', 't', 'a', 'b', '\0', + '.', 's', 't', 'r', 't', 'a', 'b', '\0', + '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', '\0' +}; + +const unsigned int test_file_1_size = sizeof test_file_1; + +} // End namespace gold_testsuite. diff --git a/gold/testsuite/testfile.h b/gold/testsuite/testfile.h new file mode 100644 index 000000000000..c123107e4969 --- /dev/null +++ b/gold/testsuite/testfile.h @@ -0,0 +1,20 @@ +// testfile.h -- test input files -*- C++ -*- + +#ifndef GOLD_TESTSUITE_TESTFILE_H +#define GOLD_TESTSUITE_TESTFILE_H + +namespace gold +{ +class Target; +} + +namespace gold_testsuite +{ + +extern gold::Target* target_test_pointer; +extern const unsigned char test_file_1[]; +extern const unsigned int test_file_1_size; + +}; // End namespace gold_testsuite. + +#endif // !defined(GOLD_TESTSUITE_TESTFILE_H) diff --git a/gold/testsuite/testmain.cc b/gold/testsuite/testmain.cc new file mode 100644 index 000000000000..eb46b72d84f6 --- /dev/null +++ b/gold/testsuite/testmain.cc @@ -0,0 +1,18 @@ +// testmain.cc -- main function for simplisitic gold test framework. + +#include "gold.h" + +#include "test.h" + +using namespace gold_testsuite; + +int +main(int, char** argv) +{ + gold::program_name = argv[0]; + + Test_framework tf; + Register_test::run_tests(&tf); + + exit(tf.failures()); +} diff --git a/gold/workqueue.cc b/gold/workqueue.cc new file mode 100644 index 000000000000..9062118aaa00 --- /dev/null +++ b/gold/workqueue.cc @@ -0,0 +1,404 @@ +// workqueue.cc -- the workqueue for gold + +#include "gold.h" + +#include "workqueue.h" + +namespace gold +{ + +// Task_token methods. + +Task_token::Task_token() + : is_blocker_(false), readers_(0), writer_(NULL) +{ +} + +Task_token::~Task_token() +{ + gold_assert(this->readers_ == 0 && this->writer_ == NULL); +} + +bool +Task_token::is_readable() const +{ + gold_assert(!this->is_blocker_); + return this->writer_ == NULL; +} + +void +Task_token::add_reader() +{ + gold_assert(!this->is_blocker_); + gold_assert(this->is_readable()); + ++this->readers_; +} + +void +Task_token::remove_reader() +{ + gold_assert(!this->is_blocker_); + gold_assert(this->readers_ > 0); + --this->readers_; +} + +bool +Task_token::is_writable() const +{ + gold_assert(!this->is_blocker_); + return this->writer_ == NULL && this->readers_ == 0; +} + +void +Task_token::add_writer(const Task* t) +{ + gold_assert(!this->is_blocker_); + gold_assert(this->is_writable()); + this->writer_ = t; +} + +void +Task_token::remove_writer(const Task* t) +{ + gold_assert(!this->is_blocker_); + gold_assert(this->writer_ == t); + this->writer_ = NULL; +} + +bool +Task_token::has_write_lock(const Task* t) +{ + gold_assert(!this->is_blocker_); + return this->writer_ == t; +} + +// For blockers, we just use the readers_ field. + +void +Task_token::add_blocker() +{ + if (this->readers_ == 0 && this->writer_ == NULL) + this->is_blocker_ = true; + else + gold_assert(this->is_blocker_); + ++this->readers_; +} + +bool +Task_token::remove_blocker() +{ + gold_assert(this->is_blocker_ && this->readers_ > 0); + --this->readers_; + return this->readers_ == 0; +} + +bool +Task_token::is_blocked() const +{ + gold_assert(this->is_blocker_ + || (this->readers_ == 0 && this->writer_ == NULL)); + return this->readers_ > 0; +} + +// The Task_block_token class. + +Task_block_token::Task_block_token(Task_token& token, Workqueue* workqueue) + : token_(token), workqueue_(workqueue) +{ + // We must increment the block count when the task is created and + // put on the queue. This object is created when the task is run, + // so we don't increment the block count here. + gold_assert(this->token_.is_blocked()); +} + +Task_block_token::~Task_block_token() +{ + if (this->token_.remove_blocker()) + { + // Tell the workqueue that a blocker was cleared. This is + // always called in the main thread, so no locking is required. + this->workqueue_->cleared_blocker(); + } +} + +// The Workqueue_runner abstract class. + +class Workqueue_runner +{ + public: + Workqueue_runner(Workqueue* workqueue) + : workqueue_(workqueue) + { } + virtual ~Workqueue_runner() + { } + + // Run a task. This is always called in the main thread. + virtual void run(Task*, Task_locker*) = 0; + + protected: + // This is called by an implementation when a task is completed. + void completed(Task* t, Task_locker* tl) + { this->workqueue_->completed(t, tl); } + + Workqueue* get_workqueue() const + { return this->workqueue_; } + + private: + Workqueue* workqueue_; +}; + +// The simple single-threaded implementation of Workqueue_runner. + +class Workqueue_runner_single : public Workqueue_runner +{ + public: + Workqueue_runner_single(Workqueue* workqueue) + : Workqueue_runner(workqueue) + { } + ~Workqueue_runner_single() + { } + + void run(Task*, Task_locker*); +}; + +void +Workqueue_runner_single::run(Task* t, Task_locker* tl) +{ + t->run(this->get_workqueue()); + this->completed(t, tl); +} + +// Workqueue methods. + +Workqueue::Workqueue(const General_options&) + : tasks_lock_(), + tasks_(), + completed_lock_(), + completed_(), + running_(0), + completed_condvar_(this->completed_lock_), + cleared_blockers_(0) +{ + // At some point we will select the specific implementation of + // Workqueue_runner to use based on the command line options. + this->runner_ = new Workqueue_runner_single(this); +} + +Workqueue::~Workqueue() +{ + gold_assert(this->tasks_.empty()); + gold_assert(this->completed_.empty()); + gold_assert(this->running_ == 0); +} + +// Add a task to the queue. + +void +Workqueue::queue(Task* t) +{ + Hold_lock hl(this->tasks_lock_); + this->tasks_.push_back(t); +} + +// Add a task to the front of the queue. + +void +Workqueue::queue_front(Task* t) +{ + Hold_lock hl(this->tasks_lock_); + this->tasks_.push_front(t); +} + +// Clear the list of completed tasks. Return whether we cleared +// anything. The completed_lock_ must be held when this is called. + +bool +Workqueue::clear_completed() +{ + if (this->completed_.empty()) + return false; + do + { + delete this->completed_.front(); + this->completed_.pop_front(); + } + while (!this->completed_.empty()); + return true; +} + +// Find a runnable task in TASKS, which is non-empty. Return NULL if +// none could be found. The tasks_lock_ must be held when this is +// called. Sets ALL_BLOCKED if all non-runnable tasks are waiting on +// a blocker. + +Task* +Workqueue::find_runnable(Task_list& tasks, bool* all_blocked) +{ + Task* tlast = tasks.back(); + *all_blocked = true; + while (true) + { + Task* t = tasks.front(); + tasks.pop_front(); + + Task::Is_runnable_type is_runnable = t->is_runnable(this); + if (is_runnable == Task::IS_RUNNABLE) + return t; + + if (is_runnable != Task::IS_BLOCKED) + *all_blocked = false; + + tasks.push_back(t); + + if (t == tlast) + { + // We couldn't find any runnable task. If there are any + // completed tasks, free their locks and try again. + + { + Hold_lock hl2(this->completed_lock_); + + if (!this->clear_completed()) + { + // There had better be some tasks running, or we will + // never find a runnable task. + gold_assert(this->running_ > 0); + + // We couldn't find any runnable tasks, and we + // couldn't release any locks. + return NULL; + } + } + + // We're going around again, so recompute ALL_BLOCKED. + *all_blocked = true; + } + } +} + +// Process all the tasks on the workqueue. This is the main loop in +// the linker. Note that as we process tasks, new tasks will be +// added. + +void +Workqueue::process() +{ + while (true) + { + Task* t; + bool empty; + bool all_blocked; + + { + Hold_lock hl(this->tasks_lock_); + + if (this->tasks_.empty()) + { + t = NULL; + empty = true; + all_blocked = false; + } + else + { + t = this->find_runnable(this->tasks_, &all_blocked); + empty = false; + } + } + + // If T != NULL, it is a task we can run. + // If T == NULL && empty, then there are no tasks waiting to + // be run at this level. + // If T == NULL && !empty, then there tasks waiting to be + // run at this level, but they are waiting for something to + // unlock. + + if (t != NULL) + this->run(t); + else if (!empty) + { + { + Hold_lock hl(this->completed_lock_); + + // There must be something for us to wait for, or we won't + // be able to make progress. + gold_assert(this->running_ > 0 || !this->completed_.empty()); + + if (all_blocked) + { + this->cleared_blockers_ = 0; + this->clear_completed(); + while (this->cleared_blockers_ == 0) + { + gold_assert(this->running_ > 0); + this->completed_condvar_.wait(); + this->clear_completed(); + } + } + else + { + if (this->running_ > 0) + { + // Wait for a task to finish. + this->completed_condvar_.wait(); + } + this->clear_completed(); + } + } + } + else + { + { + Hold_lock hl(this->completed_lock_); + + // If there are no running tasks, then we are done. + if (this->running_ == 0) + { + this->clear_completed(); + return; + } + + // Wait for a task to finish. Then we have to loop around + // again in case it added any new tasks before finishing. + this->completed_condvar_.wait(); + this->clear_completed(); + } + } + } +} + +// Run a task. This is always called in the main thread. + +void +Workqueue::run(Task* t) +{ + ++this->running_; + this->runner_->run(t, t->locks(this)); +} + +// This is called when a task is completed to put the locks on the +// list to be released. We use a list because we only want the locks +// to be released in the main thread. + +void +Workqueue::completed(Task* t, Task_locker* tl) +{ + { + Hold_lock hl(this->completed_lock_); + gold_assert(this->running_ > 0); + --this->running_; + this->completed_.push_back(tl); + this->completed_condvar_.signal(); + } + delete t; +} + +// This is called when the last task for a blocker has completed. +// This is always called in the main thread. + +void +Workqueue::cleared_blocker() +{ + ++this->cleared_blockers_; +} + +} // End namespace gold. diff --git a/gold/workqueue.h b/gold/workqueue.h new file mode 100644 index 000000000000..ed7a5b00d7dd --- /dev/null +++ b/gold/workqueue.h @@ -0,0 +1,419 @@ +// workqueue.h -- the work queue for gold -*- C++ -*- + +// After processing the command line, everything the linker does is +// driven from a work queue. This permits us to parallelize the +// linker where possible. + +// Task_token +// A simple locking implementation to ensure proper task ordering. +// Task_read_token, Task_write_token +// Lock a Task_token for read or write. +// Task_locker +// Task locking using RAII. +// Task +// An abstract class for jobs to run. + +#ifndef GOLD_WORKQUEUE_H +#define GOLD_WORKQUEUE_H + +#include "gold-threads.h" +#include "fileread.h" + +namespace gold +{ + +class General_options; +class Task; +class Workqueue; + +// Some tasks require access to shared data structures, such as the +// symbol table. Some tasks must be executed in a particular error, +// such as reading input file symbol tables--if we see foo.o -llib, we +// have to read the symbols for foo.o before we read the ones for +// -llib. To implement this safely and efficiently, we use tokens. +// Task_tokens support shared read/exclusive write access to some +// resource. Alternatively, they support blockers: blockers implement +// the requirement that some set of tasks must complete before another +// set of tasks can start. In such a case we increment the block +// count when we create the task, and decrement it when the task +// completes. Task_tokens are only manipulated by the main thread, so +// they do not themselves require any locking. + +class Task_token +{ + public: + Task_token(); + + ~Task_token(); + + // A read/write token uses these methods. + + bool + is_readable() const; + + void + add_reader(); + + void + remove_reader(); + + bool + is_writable() const; + + void + add_writer(const Task*); + + void + remove_writer(const Task*); + + bool + has_write_lock(const Task*); + + // A blocker token uses these methods. + + void + add_blocker(); + + // Returns true if block count drops to zero. + bool + remove_blocker(); + + bool + is_blocked() const; + + private: + // It makes no sense to copy these. + Task_token(const Task_token&); + Task_token& operator=(const Task_token&); + + bool is_blocker_; + int readers_; + const Task* writer_; +}; + +// In order to support tokens more reliably, we provide objects which +// handle them using RAII. + +class Task_read_token +{ + public: + Task_read_token(Task_token& token) + : token_(token) + { this->token_.add_reader(); } + + ~Task_read_token() + { this->token_.remove_reader(); } + + private: + Task_read_token(const Task_read_token&); + Task_read_token& operator=(const Task_read_token&); + + Task_token& token_; +}; + +class Task_write_token +{ + public: + Task_write_token(Task_token& token, const Task* task) + : token_(token), task_(task) + { this->token_.add_writer(this->task_); } + + ~Task_write_token() + { this->token_.remove_writer(this->task_); } + + private: + Task_write_token(const Task_write_token&); + Task_write_token& operator=(const Task_write_token&); + + Task_token& token_; + const Task* task_; +}; + +class Task_block_token +{ + public: + // The blocker count must be incremented when the task is created. + // This object is created when the task is run. When we unblock the + // last task, we notify the workqueue. + Task_block_token(Task_token& token, Workqueue* workqueue); + ~Task_block_token(); + + private: + Task_block_token(const Task_block_token&); + Task_block_token& operator=(const Task_block_token&); + + Task_token& token_; + Workqueue* workqueue_; +}; + +// An object which implements an RAII lock for any object which +// supports lock and unlock methods. + +template<typename Obj> +class Task_lock_obj +{ + public: + Task_lock_obj(Obj& obj) + : obj_(obj) + { this->obj_.lock(); } + + ~Task_lock_obj() + { this->obj_.unlock(); } + + private: + Task_lock_obj(const Task_lock_obj&); + Task_lock_obj& operator=(const Task_lock_obj&); + + Obj& obj_; +}; + +// An abstract class used to lock Task_tokens using RAII. A typical +// implementation would simply have a set of members of type +// Task_read_token, Task_write_token, and Task_block_token. + +class Task_locker +{ + public: + Task_locker() + { } + + virtual ~Task_locker() + { } +}; + +// A version of Task_locker which may be used for a single read lock. + +class Task_locker_read : public Task_locker +{ + public: + Task_locker_read(Task_token& token) + : read_token_(token) + { } + + private: + Task_locker_read(const Task_locker_read&); + Task_locker_read& operator=(const Task_locker_read&); + + Task_read_token read_token_; +}; + +// A version of Task_locker which may be used for a single write lock. + +class Task_locker_write : public Task_locker +{ + public: + Task_locker_write(Task_token& token, const Task* task) + : write_token_(token, task) + { } + + private: + Task_locker_write(const Task_locker_write&); + Task_locker_write& operator=(const Task_locker_write&); + + Task_write_token write_token_; +}; + +// A version of Task_locker which may be used for a single blocker +// lock. + +class Task_locker_block : public Task_locker +{ + public: + Task_locker_block(Task_token& token, Workqueue* workqueue) + : block_token_(token, workqueue) + { } + + private: + Task_locker_block(const Task_locker_block&); + Task_locker_block& operator=(const Task_locker_block&); + + Task_block_token block_token_; +}; + +// A version of Task_locker which may be used to hold a lock on any +// object which supports lock() and unlock() methods. + +template<typename Obj> +class Task_locker_obj : public Task_locker +{ + public: + Task_locker_obj(Obj& obj) + : obj_lock_(obj) + { } + + private: + Task_locker_obj(const Task_locker_obj&); + Task_locker_obj& operator=(const Task_locker_obj&); + + Task_lock_obj<Obj> obj_lock_; +}; + +// The superclass for tasks to be placed on the workqueue. Each +// specific task class will inherit from this one. + +class Task +{ + public: + Task() + { } + virtual ~Task() + { } + + // Type returned by Is_runnable. + enum Is_runnable_type + { + // Task is runnable. + IS_RUNNABLE, + // Task is waiting for a block to clear. + IS_BLOCKED, + // Task is not waiting for a block, but is not runnable--i.e., is + // waiting for a lock. + IS_LOCKED + }; + + // Return whether the task can be run now. This method is only + // called from the main thread. + virtual Is_runnable_type + is_runnable(Workqueue*) = 0; + + // Return a pointer to a Task_locker which locks all the resources + // required by the task. We delete the pointer when the task is + // complete. This method can return NULL if no locks are required. + // This method is only called from the main thread. + virtual Task_locker* + locks(Workqueue*) = 0; + + // Run the task. + virtual void + run(Workqueue*) = 0; + + private: + Task(const Task&); + Task& operator=(const Task&); +}; + +// A simple task which waits for a blocker and then runs a function. + +class Task_function_runner +{ + public: + virtual ~Task_function_runner() + { } + + virtual void + run(Workqueue*) = 0; +}; + +class Task_function : public Task +{ + public: + // Both points should be allocated using new, and will be deleted + // after the task runs. + Task_function(Task_function_runner* runner, Task_token* blocker) + : runner_(runner), blocker_(blocker) + { } + + ~Task_function() + { + delete this->runner_; + delete this->blocker_; + } + + // The standard task methods. + + // Wait until the task is unblocked. + Is_runnable_type + is_runnable(Workqueue*) + { return this->blocker_->is_blocked() ? IS_BLOCKED : IS_RUNNABLE; } + + // This type of task does not normally hold any locks. + virtual Task_locker* + locks(Workqueue*) + { return NULL; } + + // Run the action. + void + run(Workqueue* workqueue) + { this->runner_->run(workqueue); } + + private: + Task_function(const Task_function&); + Task_function& operator=(const Task_function&); + + Task_function_runner* runner_; + Task_token* blocker_; +}; + +// The workqueue + +class Workqueue_runner; + +class Workqueue +{ + public: + Workqueue(const General_options&); + ~Workqueue(); + + // Add a new task to the work queue. + void + queue(Task*); + + // Add a new task to the front of the work queue. It will be the + // next task to run if it is ready. + void + queue_front(Task*); + + // Process all the tasks on the work queue. + void + process(); + + // A complete set of blocking tasks has completed. + void + cleared_blocker(); + + private: + // This class can not be copied. + Workqueue(const Workqueue&); + Workqueue& operator=(const Workqueue&); + + typedef std::list<Task*> Task_list; + + // Run a task. + void run(Task*); + + friend class Workqueue_runner; + + // Find a runnable task. + Task* find_runnable(Task_list&, bool*); + + // Add a lock to the completed queue. + void completed(Task*, Task_locker*); + + // Clear the completed queue. + bool clear_completed(); + + // How to run a task. Only accessed from main thread. + Workqueue_runner* runner_; + + // Lock for access to tasks_ members. + Lock tasks_lock_; + // List of tasks to execute at each link level. + Task_list tasks_; + + // Lock for access to completed_ and running_ members. + Lock completed_lock_; + // List of Task_locker objects for main thread to free. + std::list<Task_locker*> completed_; + // Number of tasks currently running. + int running_; + // Condition variable signalled when a new entry is added to completed_. + Condvar completed_condvar_; + + // Number of blocker tokens which were fully cleared. Only accessed + // from main thread. + int cleared_blockers_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_WORKQUEUE_H) diff --git a/gold/yyscript.y b/gold/yyscript.y new file mode 100644 index 000000000000..0bd2b603b2e5 --- /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 */ + ; + +%% |