From dbe717effbdf31236088837f4686fd5ad5e71893 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 14 Nov 2006 19:21:05 +0000 Subject: [PATCH] More dynamic object support, initial scripting support. --- elfcpp/elfcpp.h | 326 +++++++++++ elfcpp/elfcpp_internal.h | 79 ++- gold/Makefile.am | 16 +- gold/Makefile.in | 91 ++- gold/common.cc | 2 +- gold/configure | 44 +- gold/configure.ac | 1 + gold/dynobj.cc | 670 +++++++++++++++++++++ gold/dynobj.h | 67 ++- gold/gold.cc | 2 +- gold/i386.cc | 20 +- gold/layout.cc | 74 ++- gold/layout.h | 30 +- gold/object.cc | 196 ++++--- gold/object.h | 76 ++- gold/options.cc | 75 ++- gold/options.h | 100 +++- gold/output.cc | 79 ++- gold/output.h | 100 ++-- gold/po/POTFILES.in | 4 + gold/po/gold.pot | 273 ++++++--- gold/readsyms.cc | 44 +- gold/readsyms.h | 6 +- gold/reloc.cc | 1 + gold/script-c.h | 53 ++ gold/script.cc | 1188 ++++++++++++++++++++++++++++++++++++++ gold/script.h | 39 ++ gold/symtab.cc | 213 ++++++- gold/symtab.h | 23 +- gold/target.h | 7 + gold/yyscript.y | 168 ++++++ 31 files changed, 3696 insertions(+), 371 deletions(-) create mode 100644 gold/dynobj.cc create mode 100644 gold/script-c.h create mode 100644 gold/script.cc create mode 100644 gold/script.h create mode 100644 gold/yyscript.y diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h index 322afb47d7..e106fabbb1 100644 --- a/elfcpp/elfcpp.h +++ b/elfcpp/elfcpp.h @@ -530,6 +530,138 @@ elf_r_info<64>(unsigned int s, unsigned int t) return (static_cast(s) << 32) + (t & 0xffffffff); } +// Dynamic tags found in the PT_DYNAMIC segment. + +enum DT +{ + DT_NULL = 0, + DT_NEEDED = 1, + DT_PLTRELSZ = 2, + DT_PLTGOT = 3, + DT_HASH = 4, + DT_STRTAB = 5, + DT_SYMTAB = 6, + DT_RELA = 7, + DT_RELASZ = 8, + DT_RELAENT = 9, + DT_STRSZ = 10, + DT_SYMENT = 11, + DT_INIT = 12, + DT_FINI = 13, + DT_SONAME = 14, + DT_RPATH = 15, + DT_SYMBOLIC = 16, + DT_REL = 17, + DT_RELSZ = 18, + DT_RELENT = 19, + DT_PLTREL = 20, + DT_DEBUG = 21, + DT_TEXTREL = 22, + DT_JMPREL = 23, + DT_BIND_NOW = 24, + DT_INIT_ARRAY = 25, + DT_FINI_ARRAY = 26, + DT_INIT_ARRAYSZ = 27, + DT_FINI_ARRAYSZ = 28, + DT_RUNPATH = 29, + DT_FLAGS = 30, + DT_ENCODING = 32, + DT_PREINIT_ARRAY = 33, + DT_PREINIT_ARRAYSZ = 33, + DT_LOOS = 0x6000000d, + DT_HIOS = 0x6ffff000, + DT_LOPROC = 0x70000000, + DT_HIPROC = 0x7fffffff, + + // The remaining values are extensions used by GNU or Solaris. + DT_VALRNGLO = 0x6ffffd00, + DT_GNU_PRELINKED = 0x6ffffdf5, + DT_GNU_CONFLICTSZ = 0x6ffffdf6, + DT_GNU_LIBLISTSZ = 0x6ffffdf7, + DT_CHECKSUM = 0x6ffffdf8, + DT_PLTPADSZ = 0x6ffffdf9, + DT_MOVEENT = 0x6ffffdfa, + DT_MOVESZ = 0x6ffffdfb, + DT_FEATURE = 0x6ffffdfc, + DT_POSFLAG_1 = 0x6ffffdfd, + DT_SYMINSZ = 0x6ffffdfe, + DT_SYMINENT = 0x6ffffdff, + DT_VALRNGHI = 0x6ffffdff, + + DT_ADDRRNGLO = 0x6ffffe00, + DT_GNU_HASH = 0x6ffffef5, + DT_TLSDESC_PLT = 0x6ffffef6, + DT_TLSDESC_GOT = 0x6ffffef7, + DT_GNU_CONFLICT = 0x6ffffef8, + DT_GNU_LIBLIST = 0x6ffffef9, + DT_CONFIG = 0x6ffffefa, + DT_DEPAUDIT = 0x6ffffefb, + DT_AUDIT = 0x6ffffefc, + DT_PLTPAD = 0x6ffffefd, + DT_MOVETAB = 0x6ffffefe, + DT_SYMINFO = 0x6ffffeff, + DT_ADDRRNGHI = 0x6ffffeff, + + DT_RELACOUNT = 0x6ffffff9, + DT_RELCOUNT = 0x6ffffffa, + DT_FLAGS_1 = 0x6ffffffb, + DT_VERDEF = 0x6ffffffc, + DT_VERDEFNUM = 0x6ffffffd, + DT_VERNEED = 0x6ffffffe, + DT_VERNEEDNUM = 0x6fffffff, + + DT_VERSYM = 0x6ffffff0, + + DT_AUXILIARY = 0x7ffffffd, + DT_USED = 0x7ffffffe, + DT_FILTER = 0x7fffffff +}; + +// Flags found in the DT_FLAGS dynamic element. + +enum DF +{ + DF_ORIGIN = 0x1, + DF_SYMBOLIC = 0x2, + DF_TEXTREL = 0x4, + DF_BIND_NOW = 0x8, + DF_STATIC_TLS = 0x10 +}; + +// Version numbers which appear in the vd_version field of a Verdef +// structure. + +const int VER_DEF_NONE = 0; +const int VER_DEF_CURRENT = 1; + +// Version numbers which appear in the vn_version field of a Verneed +// structure. + +const int VER_NEED_NONE = 0; +const int VER_NEED_CURRENT = 1; + +// Bit flags which appear in vd_flags of Verdef and vna_flags of +// Vernaux. + +const int VER_FLG_BASE = 0x1; +const int VER_FLG_WEAK = 0x2; + +// Special constants found in the SHT_GNU_versym entries. + +const int VER_NDX_LOCAL = 0; +const int VER_NDX_GLOBAL = 1; + +// A SHT_GNU_versym section holds 16-bit words. This bit is set if +// the symbol is hidden and can only be seen when referenced using an +// explicit version number. This is a GNU extension. + +const int VERSYM_HIDDEN = 0x8000; + +// This is the mask for the rest of the data in a word read from a +// SHT_GNU_versym section. + +const int VERSYM_VERSION = 0x7fff; + } // End namespace elfcpp. // Include internal details after defining the types. @@ -558,6 +690,8 @@ struct Elf_sizes // Sizes of ELF reloc entries. static const int rel_size = sizeof(internal::Rel_data); static const int rela_size = sizeof(internal::Rela_data); + // Size of ELF dynamic entry. + static const int dyn_size = sizeof(internal::Dyn_data); }; // Accessor class for the ELF file header. @@ -1087,6 +1221,198 @@ class Rela const internal::Rela_data* p_; }; +// Accessor classes for entries in the ELF SHT_DYNAMIC section aka +// PT_DYNAMIC segment. + +template +class Dyn +{ + public: + Dyn(const unsigned char* p) + : p_(reinterpret_cast*>(p)) + { } + + template + Dyn(File* file, typename File::Location loc) + : p_(reinterpret_cast*>( + file->view(loc.file_offset, loc.data_size).data())) + { } + + typename Elf_types::Elf_Swxword + get_d_tag() const + { return Convert::convert_host(this->p_->d_tag); } + + typename Elf_types::Elf_WXword + get_d_val() const + { return Convert::convert_host(this->p_->d_val); } + + typename Elf_types::Elf_Addr + get_d_ptr() const + { return Convert::convert_host(this->p_->d_val); } + + private: + const internal::Dyn_data* p_; +}; + +// Accessor classes for entries in the ELF SHT_GNU_verdef section. + +template +class Verdef +{ + public: + Verdef(const unsigned char* p) + : p_(reinterpret_cast(p)) + { } + + template + Verdef(File* file, typename File::Location loc) + : p_(reinterpret_cast( + file->view(loc.file_offset, loc.data_size).data())) + { } + + Elf_Half + get_vd_version() const + { return Convert<16, big_endian>::convert_host(this->p_->vd_version); } + + Elf_Half + get_vd_flags() const + { return Convert<16, big_endian>::convert_host(this->p_->vd_flags); } + + Elf_Half + get_vd_ndx() const + { return Convert<16, big_endian>::convert_host(this->p_->vd_ndx); } + + Elf_Half + get_vd_cnt() const + { return Convert<16, big_endian>::convert_host(this->p_->vd_cnt); } + + Elf_Word + get_vd_hash() const + { return Convert<32, big_endian>::convert_host(this->p_->vd_hash); } + + Elf_Word + get_vd_aux() const + { return Convert<32, big_endian>::convert_host(this->p_->vd_aux); } + + Elf_Word + get_vd_next() const + { return Convert<32, big_endian>::convert_host(this->p_->vd_next); } + + private: + const internal::Verdef_data* p_; +}; + +// Accessor classes for auxiliary entries in the ELF SHT_GNU_verdef +// section. + +template +class Verdaux +{ + public: + Verdaux(const unsigned char* p) + : p_(reinterpret_cast(p)) + { } + + template + Verdaux(File* file, typename File::Location loc) + : p_(reinterpret_cast( + file->view(loc.file_offset, loc.data_size).data())) + { } + + Elf_Word + get_vda_name() const + { return Convert<32, big_endian>::convert_host(this->p_->vda_name); } + + Elf_Word + get_vda_next() const + { return Convert<32, big_endian>::convert_host(this->p_->vda_next); } + + private: + const internal::Verdaux_data* p_; +}; + +// Accessor classes for entries in the ELF SHT_GNU_verneed section. + +template +class Verneed +{ + public: + Verneed(const unsigned char* p) + : p_(reinterpret_cast(p)) + { } + + template + Verneed(File* file, typename File::Location loc) + : p_(reinterpret_cast( + file->view(loc.file_offset, loc.data_size).data())) + { } + + Elf_Half + get_vn_version() const + { return Convert<16, big_endian>::convert_host(this->p_->vn_version); } + + Elf_Half + get_vn_cnt() const + { return Convert<16, big_endian>::convert_host(this->p_->vn_cnt); } + + Elf_Word + get_vn_file() const + { return Convert<32, big_endian>::convert_host(this->p_->vn_file); } + + Elf_Word + get_vn_aux() const + { return Convert<32, big_endian>::convert_host(this->p_->vn_aux); } + + Elf_Word + get_vn_next() const + { return Convert<32, big_endian>::convert_host(this->p_->vn_next); } + + private: + const internal::Verneed_data* p_; +}; + +// Accessor classes for auxiliary entries in the ELF SHT_GNU_verneed +// section. + +template +class Vernaux +{ + public: + Vernaux(const unsigned char* p) + : p_(reinterpret_cast(p)) + { } + + template + Vernaux(File* file, typename File::Location loc) + : p_(reinterpret_cast( + file->view(loc.file_offset, loc.data_size).data())) + { } + + Elf_Word + get_vna_hash() const + { return Convert<32, big_endian>::convert_host(this->p_->vna_hash); } + + Elf_Half + get_vna_flags() const + { return Convert<16, big_endian>::convert_host(this->p_->vna_flags); } + + Elf_Half + get_vna_other() const + { return Convert<16, big_endian>::convert_host(this->p_->vna_other); } + + Elf_Word + get_vna_name() const + { return Convert<32, big_endian>::convert_host(this->p_->vna_name); } + + Elf_Word + get_vna_next() const + { return Convert<32, big_endian>::convert_host(this->p_->vna_next); } + + private: + const internal::Vernaux_data* p_; +}; + + } // End namespace elfcpp. #endif // !defined(ELFPCP_H) diff --git a/elfcpp/elfcpp_internal.h b/elfcpp/elfcpp_internal.h index c991535cb0..7a2b5d4836 100644 --- a/elfcpp/elfcpp_internal.h +++ b/elfcpp/elfcpp_internal.h @@ -35,7 +35,7 @@ struct Ehdr_data Elf_Half e_shstrndx; }; -// An Elf section header. +// An ELF section header. template struct Shdr_data @@ -114,7 +114,7 @@ struct Sym_data<64> Elf_Xword st_size; }; -// Elf relocation table entries. +// ELF relocation table entries. template struct Rel_data @@ -131,6 +131,81 @@ struct Rela_data typename Elf_types::Elf_Swxword r_addend; }; +// An entry in the ELF SHT_DYNAMIC section aka PT_DYNAMIC segment. + +template +struct Dyn_data +{ + typename Elf_types::Elf_Swxword d_tag; + typename Elf_types::Elf_WXword d_val; +}; + +// An entry in a SHT_GNU_verdef section. This structure is the same +// in 32-bit and 64-bit ELF files. + +struct Verdef_data +{ + // Version number of structure (VER_DEF_*). + Elf_Half vd_version; + // Bit flags (VER_FLG_*). + Elf_Half vd_flags; + // Version index. + Elf_Half vd_ndx; + // Number of auxiliary Verdaux entries. + Elf_Half vd_cnt; + // Hash of name. + Elf_Word vd_hash; + // Byte offset to first Verdaux entry. + Elf_Word vd_aux; + // Byte offset to next Verdef entry. + Elf_Word vd_next; +}; + +// An auxiliary entry in a SHT_GNU_verdef section. This structure is +// the same in 32-bit and 64-bit ELF files. + +struct Verdaux_data +{ + // Offset in string table of version name. + Elf_Word vda_name; + // Byte offset to next Verdaux entry. + Elf_Word vda_next; +}; + +// An entry in a SHT_GNU_verneed section. This structure is the same +// in 32-bit and 64-bit ELF files. + +struct Verneed_data +{ + // Version number of structure (VER_NEED_*). + Elf_Half vn_version; + // Number of auxiliary Vernaux entries. + Elf_Half vn_cnt; + // Offset in string table of library name. + Elf_Word vn_file; + // Byte offset to first Vernaux entry. + Elf_Word vn_aux; + // Byt eoffset to next Verneed entry. + Elf_Word vn_next; +}; + +// An auxiliary entry in a SHT_GNU_verneed section. This structure is +// the same in 32-bit and 64-bit ELF files. + +struct Vernaux_data +{ + // Hash of dependency name. + Elf_Word vna_hash; + // Bit flags (VER_FLG_*). + Elf_Half vna_flags; + // Version index used in SHT_GNU_versym entries. + Elf_Half vna_other; + // Offset in string table of version name. + Elf_Word vna_name; + // Byte offset to next Vernaux entry. + Elf_Word vna_next; +}; + } // End namespace internal. } // End namespace elfcpp. diff --git a/gold/Makefile.am b/gold/Makefile.am index 13aae2678e..12ec749920 100644 --- a/gold/Makefile.am +++ b/gold/Makefile.am @@ -15,6 +15,8 @@ INCLUDES = -D_GNU_SOURCE \ -DLOCALEDIR="\"$(datadir)/locale\"" \ @INCINTL@ +YFLAGS = -d + noinst_PROGRAMS = ld-new CCFILES = \ @@ -22,6 +24,7 @@ CCFILES = \ common.cc \ defstd.cc \ dirsearch.cc \ + dynobj.cc \ fileread.cc \ gold.cc \ gold-threads.cc \ @@ -32,6 +35,7 @@ CCFILES = \ readsyms.cc \ reloc.cc \ resolve.cc \ + script.cc \ symtab.cc \ stringpool.cc \ target-select.cc \ @@ -42,6 +46,7 @@ HFILES = \ common.h \ defstd.h \ dirsearch.h \ + dynobj.h \ fileread.h \ gold.h \ gold-threads.h \ @@ -51,6 +56,7 @@ HFILES = \ output.h \ readsyms.h \ reloc.h \ + script.h \ stringpool.h \ symtab.h \ target.h \ @@ -61,7 +67,10 @@ HFILES = \ TARGETFILES = \ i386.cc -OFILES = gold.o options.o +YFILES = \ + yyscript.y + +EXTRA_DIST = yyscript.c yyscript.h POTFILES= $(CCFILES) $(HFILES) $(TARGETFILES) @@ -69,10 +78,13 @@ po/POTFILES.in: @MAINT@ Makefile for f in $(POTFILES); do echo $$f; done | LC_COLLATE= sort > tmp \ && mv tmp $(srcdir)/po/POTFILES.in -ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES) +ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES) $(YFILES) ld_new_DEPENDENCIES = $(LIBINTL_DEP) ld_new_LDADD = $(LIBINTL) +# Use an explicit dependency for the bison generated header file. +script.$(OBJEXT): yyscript.h + .PHONY: install-exec-local install-exec-local: ld-new$(EXEEXT) diff --git a/gold/Makefile.in b/gold/Makefile.in index d4c0c67c5a..deef51c43c 100644 --- a/gold/Makefile.in +++ b/gold/Makefile.in @@ -45,7 +45,8 @@ DIST_COMMON = README $(am__configure_deps) $(srcdir)/../config.guess \ $(srcdir)/../install-sh $(srcdir)/../missing \ $(srcdir)/../mkinstalldirs $(srcdir)/Makefile.am \ $(srcdir)/Makefile.in $(srcdir)/config.in \ - $(top_srcdir)/configure $(top_srcdir)/po/Make-in + $(top_srcdir)/configure $(top_srcdir)/po/Make-in yyscript.c \ + yyscript.h subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \ @@ -63,29 +64,32 @@ CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = po/Makefile.in PROGRAMS = $(noinst_PROGRAMS) am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \ - dirsearch.$(OBJEXT) fileread.$(OBJEXT) gold.$(OBJEXT) \ - gold-threads.$(OBJEXT) layout.$(OBJEXT) object.$(OBJEXT) \ - options.$(OBJEXT) output.$(OBJEXT) readsyms.$(OBJEXT) \ - reloc.$(OBJEXT) resolve.$(OBJEXT) symtab.$(OBJEXT) \ - stringpool.$(OBJEXT) target-select.$(OBJEXT) \ - workqueue.$(OBJEXT) + dirsearch.$(OBJEXT) dynobj.$(OBJEXT) fileread.$(OBJEXT) \ + gold.$(OBJEXT) gold-threads.$(OBJEXT) layout.$(OBJEXT) \ + object.$(OBJEXT) options.$(OBJEXT) output.$(OBJEXT) \ + readsyms.$(OBJEXT) reloc.$(OBJEXT) resolve.$(OBJEXT) \ + script.$(OBJEXT) symtab.$(OBJEXT) stringpool.$(OBJEXT) \ + target-select.$(OBJEXT) workqueue.$(OBJEXT) am__objects_2 = am__objects_3 = i386.$(OBJEXT) -am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3) +am__objects_4 = yyscript.$(OBJEXT) +am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3) \ + $(am__objects_4) ld_new_OBJECTS = $(am_ld_new_OBJECTS) am__DEPENDENCIES_1 = DEFAULT_INCLUDES = -I. -I$(srcdir) -I. depcomp = $(SHELL) $(top_srcdir)/../depcomp am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) CXXLD = $(CXX) CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ -COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ - $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -CCLD = $(CC) -LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS) SOURCES = $(ld_new_SOURCES) DIST_SOURCES = $(ld_new_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ @@ -175,6 +179,7 @@ VERSION = @VERSION@ WARN_CFLAGS = @WARN_CFLAGS@ WARN_CXXFLAGS = @WARN_CXXFLAGS@ XGETTEXT = @XGETTEXT@ +YACC = @YACC@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_STRIP = @ac_ct_STRIP@ @@ -229,11 +234,13 @@ INCLUDES = -D_GNU_SOURCE \ -DLOCALEDIR="\"$(datadir)/locale\"" \ @INCINTL@ +YFLAGS = -d CCFILES = \ archive.cc \ common.cc \ defstd.cc \ dirsearch.cc \ + dynobj.cc \ fileread.cc \ gold.cc \ gold-threads.cc \ @@ -244,6 +251,7 @@ CCFILES = \ readsyms.cc \ reloc.cc \ resolve.cc \ + script.cc \ symtab.cc \ stringpool.cc \ target-select.cc \ @@ -254,6 +262,7 @@ HFILES = \ common.h \ defstd.h \ dirsearch.h \ + dynobj.h \ fileread.h \ gold.h \ gold-threads.h \ @@ -263,6 +272,7 @@ HFILES = \ output.h \ readsyms.h \ reloc.h \ + script.h \ stringpool.h \ symtab.h \ target.h \ @@ -273,16 +283,19 @@ HFILES = \ TARGETFILES = \ i386.cc -OFILES = gold.o options.o +YFILES = \ + yyscript.y + +EXTRA_DIST = yyscript.c yyscript.h POTFILES = $(CCFILES) $(HFILES) $(TARGETFILES) -ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES) +ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES) $(YFILES) ld_new_DEPENDENCIES = $(LIBINTL_DEP) ld_new_LDADD = $(LIBINTL) all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: -.SUFFIXES: .cc .o .obj +.SUFFIXES: .c .cc .o .obj .y am--refresh: @: $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) @@ -338,6 +351,11 @@ po/Makefile.in: $(top_builddir)/config.status $(top_srcdir)/po/Make-in clean-noinstPROGRAMS: -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) +yyscript.h: yyscript.c + @if test ! -f $@; then \ + rm -f yyscript.c; \ + $(MAKE) yyscript.c; \ + else :; fi ld-new$(EXEEXT): $(ld_new_OBJECTS) $(ld_new_DEPENDENCIES) @rm -f ld-new$(EXEEXT) $(CXXLINK) $(ld_new_LDFLAGS) $(ld_new_OBJECTS) $(ld_new_LDADD) $(LIBS) @@ -352,6 +370,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defstd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirsearch.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dynobj.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold-threads.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold.Po@am__quote@ @@ -363,10 +382,26 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readsyms.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/reloc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringpool.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/target-select.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/yyscript.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` .cc.o: @am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ @@ -381,6 +416,27 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.y.c: + $(YACCCOMPILE) $< + if test -f y.tab.h; then \ + to=`echo "$*_H" | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`; \ + sed -e "/^#/!b" -e "s/Y_TAB_H/$$to/g" -e "s|y\.tab\.h|$*.h|" \ + y.tab.h >$*.ht; \ + rm -f y.tab.h; \ + if cmp -s $*.ht $*.h; then \ + rm -f $*.ht ;\ + else \ + mv $*.ht $*.h; \ + fi; \ + fi + if test -f y.output; then \ + mv y.output $*.output; \ + fi + sed '/^#/ s|y\.tab\.c|$@|' y.tab.c >$@t && mv $@t $@ + rm -f y.tab.c uninstall-info-am: # This directory's subdirectories are mostly independent; you can cd @@ -686,6 +742,8 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -rm -f yyscript.c + -rm -f yyscript.h clean: clean-recursive clean-am: clean-generic clean-noinstPROGRAMS mostlyclean-am @@ -761,6 +819,9 @@ po/POTFILES.in: @MAINT@ Makefile for f in $(POTFILES); do echo $$f; done | LC_COLLATE= sort > tmp \ && mv tmp $(srcdir)/po/POTFILES.in +# Use an explicit dependency for the bison generated header file. +script.$(OBJEXT): yyscript.h + .PHONY: install-exec-local install-exec-local: ld-new$(EXEEXT) diff --git a/gold/common.cc b/gold/common.cc index 358ed8d0d9..7ba8adc269 100644 --- a/gold/common.cc +++ b/gold/common.cc @@ -171,7 +171,7 @@ Symbol_table::do_allocate_commons(const General_options&, // Place them in a newly allocated .bss section. - Output_section_common *poc = new Output_section_common(addralign); + Output_data_common *poc = new Output_data_common(addralign); layout->add_output_section_data(".bss", elfcpp::SHT_NOBITS, elfcpp::SHF_WRITE | elfcpp::SHF_ALLOC, diff --git a/gold/configure b/gold/configure index a64cd709b5..8be7e913a5 100755 --- a/gold/configure +++ b/gold/configure @@ -309,7 +309,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS CXXCPP EGREP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS CXXCPP EGREP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -3409,6 +3409,47 @@ else fi +for ac_prog in 'bison -y' byacc +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_YACC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$YACC"; then + ac_cv_prog_YACC="$YACC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_YACC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +YACC=$ac_cv_prog_YACC +if test -n "$YACC"; then + echo "$as_me:$LINENO: result: $YACC" >&5 +echo "${ECHO_T}$YACC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$YACC" && break +done +test -n "$YACC" || YACC="yacc" + # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: @@ -5746,6 +5787,7 @@ s,@ac_ct_CXX@,$ac_ct_CXX,;t t s,@CXXDEPMODE@,$CXXDEPMODE,;t t s,@am__fastdepCXX_TRUE@,$am__fastdepCXX_TRUE,;t t s,@am__fastdepCXX_FALSE@,$am__fastdepCXX_FALSE,;t t +s,@YACC@,$YACC,;t t s,@USE_NLS@,$USE_NLS,;t t s,@LIBINTL@,$LIBINTL,;t t s,@LIBINTL_DEP@,$LIBINTL_DEP,;t t diff --git a/gold/configure.ac b/gold/configure.ac index 42cba7f8b4..10e0db98b2 100644 --- a/gold/configure.ac +++ b/gold/configure.ac @@ -12,6 +12,7 @@ AM_CONFIG_HEADER(config.h:config.in) AC_PROG_CC AC_PROG_CXX +AC_PROG_YACC AC_PROG_INSTALL ZW_GNU_GETTEXT_SISTER_DIR AM_PO_SUBDIRS diff --git a/gold/dynobj.cc b/gold/dynobj.cc new file mode 100644 index 0000000000..ba1fb151a3 --- /dev/null +++ b/gold/dynobj.cc @@ -0,0 +1,670 @@ +// dynobj.cc -- dynamic object support for gold + +#include "gold.h" + +#include +#include + +#include "symtab.h" +#include "dynobj.h" + +namespace gold +{ + +// Class Sized_dynobj. + +template +Sized_dynobj::Sized_dynobj( + const std::string& name, + Input_file* input_file, + off_t offset, + const elfcpp::Ehdr& ehdr) + : Dynobj(name, input_file, offset), + elf_file_(this, ehdr), + soname_() +{ +} + +// Set up the object. + +template +void +Sized_dynobj::setup( + const elfcpp::Ehdr& 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 +void +Sized_dynobj::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 +void +Sized_dynobj::read_dynsym_section( + const unsigned char* pshdrs, + unsigned int shndx, + elfcpp::SHT type, + unsigned int link, + File_view** view, + off_t* view_size, + unsigned int* view_info) +{ + if (shndx == -1U) + { + *view = NULL; + *view_size = 0; + *view_info = 0; + return; + } + + typename This::Shdr shdr(pshdrs + shndx * This::shdr_size); + + assert(shdr.get_sh_type() == type); + + if (shdr.get_sh_link() != link) + { + fprintf(stderr, + _("%s: %s: unexpected link in section %u header: %u != %u\n"), + program_name, this->name().c_str(), shndx, + shdr.get_sh_link(), link); + gold_exit(false); + } + + *view = this->get_lasting_view(shdr.get_sh_offset(), shdr.get_sh_size()); + *view_size = shdr.get_sh_size(); + *view_info = shdr.get_sh_info(); +} + +// Set soname_ if this shared object has a DT_SONAME tag. PSHDRS +// points to the section headers. DYNAMIC_SHNDX is the section index +// of the SHT_DYNAMIC section. STRTAB_SHNDX, STRTAB, and STRTAB_SIZE +// are the section index and contents of a string table which may be +// the one associated with the SHT_DYNAMIC section. + +template +void +Sized_dynobj::set_soname(const unsigned char* pshdrs, + unsigned int dynamic_shndx, + unsigned int strtab_shndx, + const unsigned char* strtabu, + off_t strtab_size) +{ + typename This::Shdr dynamicshdr(pshdrs + dynamic_shndx * This::shdr_size); + assert(dynamicshdr.get_sh_type() == elfcpp::SHT_DYNAMIC); + + const off_t dynamic_size = dynamicshdr.get_sh_size(); + const unsigned char* pdynamic = this->get_view(dynamicshdr.get_sh_offset(), + dynamic_size); + + const unsigned int link = dynamicshdr.get_sh_link(); + if (link != strtab_shndx) + { + if (link >= this->shnum()) + { + fprintf(stderr, + _("%s: %s: DYNAMIC section %u link out of range: %u\n"), + program_name, this->name().c_str(), + dynamic_shndx, link); + gold_exit(false); + } + + typename This::Shdr strtabshdr(pshdrs + link * This::shdr_size); + if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB) + { + fprintf(stderr, + _("%s: %s: DYNAMIC section %u link %u is not a strtab\n"), + program_name, this->name().c_str(), + dynamic_shndx, link); + gold_exit(false); + } + + strtab_size = strtabshdr.get_sh_size(); + strtabu = this->get_view(strtabshdr.get_sh_offset(), strtab_size); + } + + for (const unsigned char* p = pdynamic; + p < pdynamic + dynamic_size; + p += This::dyn_size) + { + typename This::Dyn dyn(p); + + if (dyn.get_d_tag() == elfcpp::DT_SONAME) + { + off_t val = dyn.get_d_val(); + if (val >= strtab_size) + { + fprintf(stderr, + _("%s: %s: DT_SONAME value out of range: " + "%lld >= %lld\n"), + program_name, this->name().c_str(), + static_cast(val), + static_cast(strtab_size)); + gold_exit(false); + } + + const char* strtab = reinterpret_cast(strtabu); + this->soname_ = std::string(strtab + val); + return; + } + + if (dyn.get_d_tag() == elfcpp::DT_NULL) + return; + } + + fprintf(stderr, _("%s: %s: missing DT_NULL in dynamic segment\n"), + program_name, this->name().c_str()); + gold_exit(false); +} + +// Read the symbols and sections from a dynamic object. We read the +// dynamic symbols, not the normal symbols. + +template +void +Sized_dynobj::do_read_symbols(Read_symbols_data* sd) +{ + this->read_section_data(&this->elf_file_, sd); + + const unsigned char* const pshdrs = sd->section_headers->data(); + + unsigned int dynsym_shndx; + unsigned int versym_shndx; + unsigned int verdef_shndx; + unsigned int verneed_shndx; + unsigned int dynamic_shndx; + this->find_dynsym_sections(pshdrs, &dynsym_shndx, &versym_shndx, + &verdef_shndx, &verneed_shndx, &dynamic_shndx); + + unsigned int strtab_shndx = -1U; + + if (dynsym_shndx == -1U) + { + sd->symbols = NULL; + sd->symbols_size = 0; + sd->symbol_names = NULL; + sd->symbol_names_size = 0; + } + else + { + // Get the dynamic symbols. + typename This::Shdr dynsymshdr(pshdrs + dynsym_shndx * This::shdr_size); + assert(dynsymshdr.get_sh_type() == elfcpp::SHT_DYNSYM); + + sd->symbols = this->get_lasting_view(dynsymshdr.get_sh_offset(), + dynsymshdr.get_sh_size()); + sd->symbols_size = dynsymshdr.get_sh_size(); + + // Get the symbol names. + strtab_shndx = dynsymshdr.get_sh_link(); + if (strtab_shndx >= this->shnum()) + { + fprintf(stderr, + _("%s: %s: invalid dynamic symbol table name index: %u\n"), + program_name, this->name().c_str(), strtab_shndx); + gold_exit(false); + } + typename This::Shdr strtabshdr(pshdrs + strtab_shndx * This::shdr_size); + if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB) + { + fprintf(stderr, + _("%s: %s: dynamic symbol table name section " + "has wrong type: %u\n"), + program_name, this->name().c_str(), + static_cast(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 +void +Sized_dynobj::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(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(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 +void +Sized_dynobj::set_version_map( + Version_map* version_map, + unsigned int ndx, + const char* name) const +{ + assert(ndx < version_map->size()); + if ((*version_map)[ndx] != NULL) + { + fprintf(stderr, _("%s: %s: duplicate definition for version %u\n"), + program_name, this->name().c_str(), ndx); + gold_exit(false); + } + (*version_map)[ndx] = name; +} + +// Create a vector mapping version numbers to version strings. + +template +void +Sized_dynobj::make_version_map( + Read_symbols_data* sd, + Version_map* version_map) const +{ + if (sd->verdef == NULL && sd->verneed == NULL) + return; + + // First find the largest version index. + unsigned int maxver = 0; + + if (sd->verdef != NULL) + { + const unsigned char* pverdef = sd->verdef->data(); + off_t verdef_size = sd->verdef_size; + const unsigned int count = sd->verdef_info; + + const unsigned char* p = pverdef; + for (unsigned int i = 0; i < count; ++i) + { + elfcpp::Verdef verdef(p); + + const unsigned int vd_ndx = verdef.get_vd_ndx(); + + // The GNU linker clears the VERSYM_HIDDEN bit. I'm not + // sure why. + + if (vd_ndx > maxver) + maxver = vd_ndx; + + const unsigned int vd_next = verdef.get_vd_next(); + if ((p - pverdef) + vd_next >= verdef_size) + { + fprintf(stderr, + _("%s: %s: verdef vd_next field out of range: %u\n"), + program_name, this->name().c_str(), vd_next); + gold_exit(false); + } + + p += vd_next; + } + } + + if (sd->verneed != NULL) + { + const unsigned char* pverneed = sd->verneed->data(); + off_t verneed_size = sd->verneed_size; + const unsigned int count = sd->verneed_info; + + const unsigned char* p = pverneed; + for (unsigned int i = 0; i < count; ++i) + { + elfcpp::Verneed verneed(p); + + const unsigned int vn_aux = verneed.get_vn_aux(); + if ((p - pverneed) + vn_aux >= verneed_size) + { + fprintf(stderr, + _("%s: %s: verneed vn_aux field out of range: %u\n"), + program_name, this->name().c_str(), vn_aux); + gold_exit(false); + } + + const unsigned int vn_cnt = verneed.get_vn_cnt(); + const unsigned char* pvna = p + vn_aux; + for (unsigned int j = 0; j < vn_cnt; ++j) + { + elfcpp::Vernaux vernaux(pvna); + + const unsigned int vna_other = vernaux.get_vna_other(); + if (vna_other > maxver) + maxver = vna_other; + + const unsigned int vna_next = vernaux.get_vna_next(); + if ((pvna - pverneed) + vna_next >= verneed_size) + { + fprintf(stderr, + _("%s: %s: verneed vna_next field " + "out of range: %u\n"), + program_name, this->name().c_str(), vna_next); + gold_exit(false); + } + + pvna += vna_next; + } + + const unsigned int vn_next = verneed.get_vn_next(); + if ((p - pverneed) + vn_next >= verneed_size) + { + fprintf(stderr, + _("%s: %s: verneed vn_next field out of range: %u\n"), + program_name, this->name().c_str(), vn_next); + gold_exit(false); + } + + p += vn_next; + } + } + + // Now MAXVER is the largest version index we have seen. + + version_map->resize(maxver + 1); + + const char* names = reinterpret_cast(sd->symbol_names->data()); + off_t names_size = sd->symbol_names_size; + + if (sd->verdef != NULL) + { + const unsigned char* pverdef = sd->verdef->data(); + off_t verdef_size = sd->verdef_size; + const unsigned int count = sd->verdef_info; + + const unsigned char* p = pverdef; + for (unsigned int i = 0; i < count; ++i) + { + elfcpp::Verdef verdef(p); + + const unsigned int vd_cnt = verdef.get_vd_cnt(); + if (vd_cnt < 1) + { + fprintf(stderr, _("%s: %s: verdef vd_cnt field too small: %u\n"), + program_name, this->name().c_str(), vd_cnt); + gold_exit(false); + } + + const unsigned int vd_aux = verdef.get_vd_aux(); + if ((p - pverdef) + vd_aux >= verdef_size) + { + fprintf(stderr, + _("%s: %s: verdef vd_aux field out of range: %u\n"), + program_name, this->name().c_str(), vd_aux); + gold_exit(false); + } + + const unsigned char* pvda = p + vd_aux; + elfcpp::Verdaux verdaux(pvda); + + const unsigned int vda_name = verdaux.get_vda_name(); + if (vda_name >= names_size) + { + fprintf(stderr, + _("%s: %s: verdaux vda_name field out of range: %u\n"), + program_name, this->name().c_str(), vda_name); + gold_exit(false); + } + + this->set_version_map(version_map, verdef.get_vd_ndx(), + names + vda_name); + + const unsigned int vd_next = verdef.get_vd_next(); + if ((p - pverdef) + vd_next >= verdef_size) + { + fprintf(stderr, + _("%s: %s: verdef vd_next field out of range: %u\n"), + program_name, this->name().c_str(), vd_next); + gold_exit(false); + } + + p += vd_next; + } + } + + if (sd->verneed != NULL) + { + const unsigned char* pverneed = sd->verneed->data(); + const unsigned int count = sd->verneed_info; + + const unsigned char* p = pverneed; + for (unsigned int i = 0; i < count; ++i) + { + elfcpp::Verneed verneed(p); + + const unsigned int vn_aux = verneed.get_vn_aux(); + const unsigned int vn_cnt = verneed.get_vn_cnt(); + const unsigned char* pvna = p + vn_aux; + for (unsigned int j = 0; j < vn_cnt; ++j) + { + elfcpp::Vernaux vernaux(pvna); + + const unsigned int vna_name = vernaux.get_vna_name(); + if (vna_name >= names_size) + { + fprintf(stderr, + _("%s: %s: vernaux vna_name field " + "out of range: %u\n"), + program_name, this->name().c_str(), vna_name); + gold_exit(false); + } + + this->set_version_map(version_map, vernaux.get_vna_other(), + names + vna_name); + + pvna += vernaux.get_vna_next(); + } + + p += verneed.get_vn_next(); + } + } +} + +// Add the dynamic symbols to the symbol table. + +template +void +Sized_dynobj::do_add_symbols(Symbol_table* symtab, + Read_symbols_data* sd) +{ + if (sd->symbols == NULL) + { + assert(sd->symbol_names == NULL); + assert(sd->versym == NULL && sd->verdef == NULL && sd->verneed == NULL); + return; + } + + const int sym_size = This::sym_size; + const size_t symcount = sd->symbols_size / sym_size; + if (symcount * sym_size != sd->symbols_size) + { + fprintf(stderr, + _("%s: %s: size of dynamic symbols is not " + "multiple of symbol size\n"), + program_name, this->name().c_str()); + gold_exit(false); + } + + Version_map version_map; + this->make_version_map(sd, &version_map); + + const char* sym_names = + reinterpret_cast(sd->symbol_names->data()); + symtab->add_from_dynobj(this, sd->symbols->data(), symcount, + sym_names, sd->symbol_names_size, + (sd->versym == NULL + ? NULL + : sd->versym->data()), + sd->versym_size, + &version_map); + + delete sd->symbols; + sd->symbols = NULL; + delete sd->symbol_names; + sd->symbol_names = NULL; + if (sd->versym != NULL) + { + delete sd->versym; + sd->versym = NULL; + } + if (sd->verdef != NULL) + { + delete sd->verdef; + sd->verdef = NULL; + } + if (sd->verneed != NULL) + { + delete sd->verneed; + sd->verneed = NULL; + } +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones for implemented targets. + +template +class Sized_dynobj<32, false>; + +template +class Sized_dynobj<32, true>; + +template +class Sized_dynobj<64, false>; + +template +class Sized_dynobj<64, true>; + +} // End namespace gold. diff --git a/gold/dynobj.h b/gold/dynobj.h index 99e78830d1..04a2c631a3 100644 --- a/gold/dynobj.h +++ b/gold/dynobj.h @@ -3,6 +3,8 @@ #ifndef GOLD_DYNOBJ_H #define GOLD_DYNOBJ_H +#include + #include "object.h" namespace gold @@ -28,6 +30,10 @@ class Sized_dynobj : public Dynobj Sized_dynobj(const std::string& name, Input_file* input_file, off_t offset, const typename elfcpp::Ehdr&); + // Set up the object file based on the ELF header. + void + setup(const typename elfcpp::Ehdr&); + // Read the symbols. void do_read_symbols(Read_symbols_data*); @@ -41,14 +47,65 @@ class Sized_dynobj : public Dynobj void do_add_symbols(Symbol_table*, Read_symbols_data*); + // Get the name of a section. + std::string + do_section_name(unsigned int shndx) + { return this->elf_file_.section_name(shndx); } + // Return a view of the contents of a section. Set *PLEN to the // size. - const unsigned char* - do_section_contents(unsigned int shnum, off_t* plen) = 0; + Object::Location + do_section_contents(unsigned int shndx) + { return this->elf_file_.section_contents(shndx); } - // Get the name of a section. - std::string - do_section_name(unsigned int shnum); + private: + // For convenience. + typedef Sized_dynobj This; + static const int shdr_size = elfcpp::Elf_sizes::shdr_size; + static const int sym_size = elfcpp::Elf_sizes::sym_size; + static const int dyn_size = elfcpp::Elf_sizes::dyn_size; + typedef elfcpp::Shdr Shdr; + typedef elfcpp::Dyn 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 Version_map; + + // Create the version map. + void + make_version_map(Read_symbols_data* sd, Version_map*) const; + + // Add an entry to the version map. + void + set_version_map(Version_map*, unsigned int ndx, const char* name) const; + + // General access to the ELF file. + elfcpp::Elf_file elf_file_; + // The DT_SONAME name, if any. + std::string soname_; }; } // End namespace gold. diff --git a/gold/gold.cc b/gold/gold.cc index c2372adf52..3073b1869e 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -113,7 +113,7 @@ queue_initial_tasks(const General_options& options, Task_token* next_blocker = new Task_token(); next_blocker->add_blocker(); workqueue->queue(new Read_symbols(options, input_objects, symtab, layout, - search_path, *p, NULL, this_blocker, + search_path, &*p, NULL, this_blocker, next_blocker)); this_blocker = next_blocker; } diff --git a/gold/i386.cc b/gold/i386.cc index 15376f9598..dc13dca1fb 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -149,7 +149,7 @@ class Target_i386 : public Sized_target<32, false> optimize_tls_reloc(const General_options*, bool is_local, int r_type); // Get the GOT section, creating it if necessary. - Output_section_got<32, false>* + Output_data_got<32, false>* got_section(Symbol_table*, Layout*); // Information about this specific target which we pass to the @@ -157,7 +157,7 @@ class Target_i386 : public Sized_target<32, false> static const Target::Target_info i386_info; // The GOT section. - Output_section_got<32, false>* got_; + Output_data_got<32, false>* got_; }; const Target::Target_info Target_i386::i386_info = @@ -166,20 +166,21 @@ const Target::Target_info Target_i386::i386_info = false, // is_big_endian elfcpp::EM_386, // machine_code false, // has_make_symbol - false, // has_resolve, - 0x08048000, // text_segment_address, + false, // has_resolve + "/usr/lib/libc.so.1", // dynamic_linker + 0x08048000, // text_segment_address 0x1000, // abi_pagesize 0x1000 // common_pagesize }; // Get the GOT section, creating it if necessary. -Output_section_got<32, false>* +Output_data_got<32, false>* Target_i386::got_section(Symbol_table* symtab, Layout* layout) { if (this->got_ == NULL) { - this->got_ = new Output_section_got<32, false>(); + this->got_ = new Output_data_got<32, false>(); assert(symtab != NULL && layout != NULL); layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, @@ -393,13 +394,8 @@ Target_i386::Scan::global(const General_options& options, case elfcpp::R_386_GOT32: // The symbol requires a GOT entry. - if (!gsym->has_got_offset()) + if (target->got_section(symtab, layout)->add_global(gsym)) { - Output_section_got<32, false>* got = target->got_section(symtab, - layout); - const unsigned int got_offset = got->add_global(gsym); - gsym->set_got_offset(got_offset); - // If this symbol is not resolved locally, we need to add a // dynamic relocation for it. if (!gsym->is_resolved_locally()) diff --git a/gold/layout.cc b/gold/layout.cc index e969a806a0..bcb1029537 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -355,13 +355,30 @@ Layout::find_first_load_seg() off_t Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) { + const int size = input_objects->target()->get_size(); + + Output_segment* phdr_seg = NULL; if (input_objects->any_dynamic()) { - // If there are any dynamic objects in the link, then we need - // some additional segments: PT_PHDRS, PT_INTERP, and - // PT_DYNAMIC. We also need to finalize the dynamic symbol - // table and create the dynamic hash table. - abort(); + // There was a dynamic object in the link. We need to create + // some information for the dynamic linker. + + // Create the PT_PHDR segment which will hold the program + // headers. + phdr_seg = new Output_segment(elfcpp::PT_PHDR, elfcpp::PF_R); + this->segment_list_.push_back(phdr_seg); + + // Create the dynamic symbol table, including the hash table, + // the dynamic relocations, and the version sections. + this->create_dynamic_symtab(size, symtab); + + // Create the .dynamic section to hold the dynamic data, and put + // it in a PT_DYNAMIC segment. + this->create_dynamic_section(); + + // Create the .interp section to hold the name of the + // interpreter, and put it in a PT_INTERP segment. + this->create_interp(input_objects->target()); } // FIXME: Handle PT_GNU_STACK. @@ -369,14 +386,14 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) Output_segment* load_seg = this->find_first_load_seg(); // Lay out the segment headers. - int size = input_objects->target()->get_size(); bool big_endian = input_objects->target()->is_big_endian(); Output_segment_headers* segment_headers; segment_headers = new Output_segment_headers(size, big_endian, this->segment_list_); load_seg->add_initial_output_data(segment_headers); this->special_output_list_.push_back(segment_headers); - // FIXME: Attach them to PT_PHDRS if necessary. + if (phdr_seg != NULL) + phdr_seg->add_initial_output_data(segment_headers); // Lay out the file header. Output_file_header* file_header; @@ -736,6 +753,49 @@ Layout::create_shdrs(int size, bool big_endian, off_t* poff) return oshdrs; } +// Create the dynamic symbol table. + +void +Layout::create_dynamic_symtab(int, Symbol_table*) +{ + abort(); +} + +// Create the .dynamic section and PT_DYNAMIC segment. + +void +Layout::create_dynamic_section() +{ + abort(); +} + +// Create the .interp section and PT_INTERP segment. + +void +Layout::create_interp(const Target* target) +{ + const char* interp = this->options_.dynamic_linker(); + if (interp == NULL) + { + interp = target->dynamic_linker(); + assert(interp != NULL); + } + + size_t len = strlen(interp) + 1; + + Output_section_data* odata = new Output_data_const(interp, len, 1); + + const char* interp_name = this->namepool_.add(".interp", NULL); + Output_section* osec = this->make_output_section(interp_name, + elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC); + osec->add_output_section_data(odata); + + Output_segment* oseg = new Output_segment(elfcpp::PT_INTERP, elfcpp::PF_R); + this->segment_list_.push_back(oseg); + oseg->add_initial_output_section(osec, elfcpp::PF_R); +} + // The mapping of .gnu.linkonce section names to real section names. #define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t, sizeof(t) - 1 } diff --git a/gold/layout.h b/gold/layout.h index fa34617a3b..759fd85f84 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -154,15 +154,6 @@ class Layout Output_segment* find_first_load_seg(); - // Set the final file offsets of all the segments. - off_t - set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx); - - // Set the final file offsets and section indices of all the - // sections not associated with a segment. - off_t - set_section_offsets(off_t, unsigned int *pshndx); - // Create the output sections for the symbol table. void create_symtab_sections(int size, const Input_objects*, Symbol_table*, off_t*, @@ -177,6 +168,18 @@ class Layout Output_section_headers* create_shdrs(int size, bool big_endian, off_t*); + // Create the dynamic symbol table. + void + create_dynamic_symtab(int size, Symbol_table*); + + // Create the .dynamic section and PT_DYNAMIC segment. + void + create_dynamic_section(); + + // Create the .interp section and PT_INTERP segment. + void + create_interp(const Target* target); + // Return whether to include this section in the link. template bool @@ -204,6 +207,15 @@ class Layout make_output_section(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags); + // Set the final file offsets of all the segments. + off_t + set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx); + + // Set the final file offsets and section indices of all the + // sections not associated with a segment. + off_t + set_section_offsets(off_t, unsigned int *pshndx); + // Return whether SEG1 comes before SEG2 in the output file. static bool segment_precedes(const Output_segment* seg1, const Output_segment* seg2); diff --git a/gold/object.cc b/gold/object.cc index 677c731fb7..1bfd9698e0 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -19,6 +19,22 @@ namespace gold // Class Object. +// Set the target based on fields in the ELF file header. + +void +Object::set_target(int machine, int size, bool big_endian, int osabi, + int abiversion) +{ + Target* target = select_target(machine, size, big_endian, osabi, abiversion); + if (target == NULL) + { + fprintf(stderr, _("%s: %s: unsupported ELF machine number %d\n"), + program_name, this->name().c_str(), machine); + gold_exit(false); + } + this->target_ = target; +} + // Report an error for the elfcpp::Elf_file interface. void @@ -45,6 +61,58 @@ Object::section_contents(unsigned int shndx, off_t* plen) return this->get_view(loc.file_offset, loc.data_size); } +// Read the section data into SD. This is code common to Sized_relobj +// and Sized_dynobj, so we put it into Object. + +template +void +Object::read_section_data(elfcpp::Elf_file* elf_file, + Read_symbols_data* sd) +{ + const int shdr_size = elfcpp::Elf_sizes::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 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(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 @@ -55,8 +123,7 @@ Sized_relobj::Sized_relobj( const elfcpp::Ehdr& ehdr) : Relobj(name, input_file, offset), elf_file_(this, ehdr), - section_headers_(NULL), - symtab_shndx_(0), + symtab_shndx_(-1U), local_symbol_count_(0), output_local_symbol_count_(0), symbols_(NULL), @@ -78,43 +145,41 @@ void Sized_relobj::setup( const elfcpp::Ehdr& ehdr) { - int machine = ehdr.get_e_machine(); - Target* target = select_target(machine, size, big_endian, - ehdr.get_e_ident()[elfcpp::EI_OSABI], - ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]); - if (target == NULL) - { - fprintf(stderr, _("%s: %s: unsupported ELF machine number %d\n"), - program_name, this->name().c_str(), machine); - gold_exit(false); - } - this->set_target(target); + this->set_target(ehdr.get_e_machine(), size, big_endian, + ehdr.get_e_ident()[elfcpp::EI_OSABI], + ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]); - unsigned int shnum = this->elf_file_.shnum(); + const unsigned int shnum = this->elf_file_.shnum(); this->set_shnum(shnum); - if (shnum == 0) - return; - - // We store the section headers in a File_view until do_read_symbols. - off_t shoff = this->elf_file_.shoff(); - this->section_headers_ = this->get_lasting_view(shoff, - shnum * This::shdr_size); +} - // Find the SHT_SYMTAB section. The ELF standard says that maybe in - // the future there can be more than one SHT_SYMTAB section. Until - // somebody figures out how that could work, we assume there is only - // one. - const unsigned char* p = this->section_headers_->data(); +// Find the SHT_SYMTAB section, given the section headers. The ELF +// standard says that maybe in the future there can be more than one +// SHT_SYMTAB section. Until somebody figures out how that could +// work, we assume there is only one. - // Skip the first section, which is always empty. - p += This::shdr_size; - for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) +template +void +Sized_relobj::find_symtab(const unsigned char* pshdrs) +{ + const unsigned int shnum = this->shnum(); + this->symtab_shndx_ = 0; + if (shnum > 0) { - typename This::Shdr shdr(p); - if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB) + // Look through the sections in reverse order, since gas tends + // to put the symbol table at the end. + const unsigned char* p = pshdrs + shnum * This::shdr_size; + unsigned int i = shnum; + while (i > 0) { - this->symtab_shndx_ = i; - break; + --i; + p -= This::shdr_size; + typename This::Shdr shdr(p); + if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB) + { + this->symtab_shndx_ = i; + break; + } } } } @@ -125,19 +190,11 @@ template void Sized_relobj::do_read_symbols(Read_symbols_data* sd) { - // Transfer our view of the section headers to SD. - sd->section_headers = this->section_headers_; - this->section_headers_ = NULL; + this->read_section_data(&this->elf_file_, sd); - // Read the section names. - const unsigned char* pshdrs = sd->section_headers->data(); - const unsigned char* pshdrnames = (pshdrs - + (this->elf_file_.shstrndx() - * This::shdr_size)); - typename This::Shdr shdrnames(pshdrnames); - sd->section_names_size = shdrnames.get_sh_size(); - sd->section_names = this->get_lasting_view(shdrnames.get_sh_offset(), - sd->section_names_size); + const unsigned char* const pshdrs = sd->section_headers->data(); + + this->find_symtab(pshdrs); if (this->symtab_shndx_ == 0) { @@ -166,15 +223,14 @@ Sized_relobj::do_read_symbols(Read_symbols_data* sd) File_view* fvsymtab = this->get_lasting_view(extoff, extsize); // Read the section header for the symbol names. - unsigned int shnum = this->shnum(); - unsigned int strtab_shnum = symtabshdr.get_sh_link(); - if (strtab_shnum == 0 || strtab_shnum >= shnum) + unsigned int strtab_shndx = symtabshdr.get_sh_link(); + if (strtab_shndx >= this->shnum()) { fprintf(stderr, _("%s: %s: invalid symbol table name index: %u\n"), - program_name, this->name().c_str(), strtab_shnum); + program_name, this->name().c_str(), strtab_shndx); gold_exit(false); } - typename This::Shdr strtabshdr(pshdrs + strtab_shnum * This::shdr_size); + typename This::Shdr strtabshdr(pshdrs + strtab_shndx * This::shdr_size); if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB) { fprintf(stderr, @@ -336,7 +392,7 @@ Sized_relobj::do_layout(const General_options& options, Layout* layout, Read_symbols_data* sd) { - unsigned int shnum = this->shnum(); + const unsigned int shnum = this->shnum(); if (shnum == 0) return; @@ -353,9 +409,6 @@ Sized_relobj::do_layout(const General_options& options, // Keep track of which sections to omit. std::vector omit(shnum, false); - const char warn_prefix[] = ".gnu.warning."; - const int warn_prefix_len = sizeof warn_prefix - 1; - // Skip the first, dummy, section. pshdrs += This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size) @@ -373,9 +426,8 @@ Sized_relobj::do_layout(const General_options& options, const char* name = pnames + shdr.get_sh_name(); - if (strncmp(name, warn_prefix, warn_prefix_len) == 0) + if (this->handle_gnu_warning_section(name, i, symtab)) { - symtab->add_warning(name + warn_prefix_len, this, i); if (!options.is_relocatable()) omit[i] = true; } @@ -442,10 +494,8 @@ Sized_relobj::do_add_symbols(Symbol_table* symtab, const char* sym_names = reinterpret_cast(sd->symbol_names->data()); - symtab->add_from_object(this, sd->symbols->data(), - symcount, sym_names, - sd->symbol_names_size, - this->symbols_); + symtab->add_from_relobj(this, sd->symbols->data(), symcount, sym_names, + sd->symbol_names_size, this->symbols_); delete sd->symbols; sd->symbols = NULL; @@ -464,6 +514,7 @@ off_t Sized_relobj::do_finalize_local_symbols(off_t off, Stringpool* pool) { + assert(this->symtab_shndx_ != -1U); if (this->symtab_shndx_ == 0) { // This object has no symbols. Weird but legal. @@ -577,6 +628,7 @@ void Sized_relobj::write_local_symbols(Output_file* of, const Stringpool* sympool) { + assert(this->symtab_shndx_ != -1U); if (this->symtab_shndx_ == 0) { // This object has no symbols. Weird but legal. @@ -711,13 +763,6 @@ make_elf_sized_object(const std::string& name, Input_file* input_file, off_t offset, const elfcpp::Ehdr& ehdr) { int et = ehdr.get_e_type(); - if (et != elfcpp::ET_REL && et != elfcpp::ET_DYN) - { - fprintf(stderr, "%s: %s: unsupported ELF type %d\n", - program_name, name.c_str(), static_cast(et)); - gold_exit(false); - } - if (et == elfcpp::ET_REL) { Sized_relobj* obj = @@ -725,17 +770,18 @@ make_elf_sized_object(const std::string& name, Input_file* input_file, obj->setup(ehdr); return obj; } + else if (et == elfcpp::ET_DYN) + { + Sized_dynobj* obj = + new Sized_dynobj(name, input_file, offset, ehdr); + obj->setup(ehdr); + return obj; + } else { - // elfcpp::ET_DYN - fprintf(stderr, _("%s: %s: dynamic objects are not yet supported\n"), - program_name, name.c_str()); + fprintf(stderr, _("%s: %s: unsupported ELF file type %d\n"), + program_name, name.c_str(), et); gold_exit(false); -// Sized_dynobj* obj = -// new Sized_dynobj(this->input_.name(), input_file, -// offset, ehdr); -// obj->setup(ehdr); -// return obj; } } diff --git a/gold/object.h b/gold/object.h index 54f0350016..6361356949 100644 --- a/gold/object.h +++ b/gold/object.h @@ -40,6 +40,19 @@ struct Read_symbols_data File_view* symbol_names; // Size of symbol name data in bytes. off_t symbol_names_size; + + // Version information. This is only used on dynamic objects. + // Version symbol data (from SHT_GNU_versym section). + File_view* versym; + off_t versym_size; + // Version definition data (from SHT_GNU_verdef section). + File_view* verdef; + off_t verdef_size; + unsigned int verdef_info; + // Needed version data (from SHT_GNU_verneed section). + File_view* verneed; + off_t verneed_size; + unsigned int verneed_info; }; // Data about a single relocation section. This is read in @@ -84,7 +97,7 @@ class Object // file--0 for a .o or .so file, something else for a .a file. Object(const std::string& name, Input_file* input_file, bool is_dynamic, off_t offset = 0) - : name_(name), input_file_(input_file), offset_(offset), + : name_(name), input_file_(input_file), offset_(offset), shnum_(-1U), is_dynamic_(is_dynamic), target_(NULL) { } @@ -256,8 +269,33 @@ class Object // Set the target. void - set_target(Target* target) - { this->target_ = target; } + set_target(int machine, int size, bool big_endian, int osabi, + int abiversion); + + // Get the number of sections. + unsigned int + shnum() const + { return this->shnum_; } + + // Set the number of sections. + void + set_shnum(int shnum) + { this->shnum_ = shnum; } + + // Functions used by both Sized_relobj and Sized_dynobj. + + // Read the section data into a Read_symbols_data object. + template + void + read_section_data(elfcpp::Elf_file*, + Read_symbols_data*); + + // If NAME is the name of a special .gnu.warning section, arrange + // for the warning to be issued. SHNDX is the section index. + // Return whether it is a warning section. + bool + handle_gnu_warning_section(const char* name, unsigned int shndx, + Symbol_table*); private: // This class may not be copied. @@ -271,6 +309,8 @@ class Object // Offset within the file--0 for an object file, non-0 for an // archive. off_t offset_; + // Number of input sections. + unsigned int shnum_; // Whether this is a dynamic object. bool is_dynamic_; // Target functions--may be NULL if the target is not known. @@ -377,24 +417,12 @@ class Relobj : public Object do_relocate(const General_options& options, const Symbol_table* symtab, const Layout*, Output_file* of) = 0; - // Get the number of sections. - unsigned int - shnum() const - { return this->shnum_; } - - // Set the number of sections. - void - set_shnum(int shnum) - { this->shnum_ = shnum; } - // Return the vector mapping input sections to output sections. std::vector& map_to_output() { return this->map_to_output_; } private: - // Number of input sections. - unsigned int shnum_; // Mapping from input sections to output section. std::vector map_to_output_; }; @@ -428,6 +456,11 @@ class Sized_relobj : public Relobj void do_read_symbols(Read_symbols_data*); + // Lay out the input sections. + void + do_layout(const General_options&, Symbol_table*, Layout*, + Read_symbols_data*); + // Add the symbols to the symbol table. void do_add_symbols(Symbol_table*, Read_symbols_data*); @@ -441,11 +474,6 @@ class Sized_relobj : public Relobj do_scan_relocs(const General_options&, Symbol_table*, Layout*, Read_relocs_data*); - // Lay out the input sections. - void - do_layout(const General_options&, Symbol_table*, Layout*, - Read_symbols_data*); - // Finalize the local symbols. off_t do_finalize_local_symbols(off_t, Stringpool*); @@ -461,7 +489,7 @@ class Sized_relobj : public Relobj { return this->elf_file_.section_name(shndx); } // Return the location of the contents of a section. - Location + Object::Location do_section_contents(unsigned int shndx) { return this->elf_file_.section_contents(shndx); } @@ -482,6 +510,10 @@ class Sized_relobj : public Relobj static const int sym_size = elfcpp::Elf_sizes::sym_size; typedef elfcpp::Shdr Shdr; + // Find the SHT_SYMTAB section, given the section headers. + void + find_symtab(const unsigned char* pshdrs); + // Whether to include a section group in the link. bool include_section_group(Layout*, unsigned int, @@ -520,8 +552,6 @@ class Sized_relobj : public Relobj // General access to the ELF file. elfcpp::Elf_file elf_file_; - // If non-NULL, a view of the section header data. - File_view* section_headers_; // Index of SHT_SYMTAB section. unsigned int symtab_shndx_; // The number of local symbols. diff --git a/gold/options.cc b/gold/options.cc index 2fea87da39..6b88429a88 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -231,6 +231,9 @@ options::Command_line_options::options[] = TWO_DASHES, &start_group), SPECIAL(')', "end-group", N_("End a library search group"), NULL, TWO_DASHES, &end_group), + GENERAL_ARG('I', "dynamic-linker", N_("Set dynamic linker path"), + N_("-I PROGRAM, --dynamic-linker PROGRAM"), TWO_DASHES, + &General_options::set_dynamic_linker), GENERAL_ARG('L', "library-path", N_("Add directory to search path"), N_("-L DIR, --library-path DIR"), TWO_DASHES, &General_options::add_to_search_path), @@ -245,6 +248,12 @@ options::Command_line_options::options[] = NULL, ONE_DASH, &General_options::set_shared), GENERAL_NOARG('\0', "static", N_("Do not link against shared libraries"), NULL, ONE_DASH, &General_options::set_static), + POSDEP_NOARG('\0', "as-needed", + N_("Only set DT_NEEDED for following dynamic libs if used"), + NULL, TWO_DASHES, &Position_dependent_options::set_as_needed), + POSDEP_NOARG('\0', "no-as-needed", + N_("Always DT_NEEDED for following dynamic libs (default)"), + NULL, TWO_DASHES, &Position_dependent_options::clear_as_needed), SPECIAL('\0', "help", N_("Report usage information"), NULL, TWO_DASHES, &help) }; @@ -255,7 +264,8 @@ const int options::Command_line_options::options_size = // The default values for the general options. General_options::General_options() - : search_path_(), + : dynamic_linker_(NULL), + search_path_(), output_file_name_("a.out"), is_relocatable_(false), is_shared_(false), @@ -270,10 +280,47 @@ Position_dependent_options::Position_dependent_options() { } +// Input_arguments methods. + +// Add a file to the list. + +void +Input_arguments::add_file(const Input_file_argument& file) +{ + if (!this->in_group_) + this->input_argument_list_.push_back(Input_argument(file)); + else + { + assert(!this->input_argument_list_.empty()); + assert(this->input_argument_list_.back().is_group()); + this->input_argument_list_.back().group()->add_file(file); + } +} + +// Start a group. + +void +Input_arguments::start_group() +{ + assert(!this->in_group_); + Input_file_group* group = new Input_file_group(); + this->input_argument_list_.push_back(Input_argument(group)); + this->in_group_ = true; +} + +// End a group. + +void +Input_arguments::end_group() +{ + assert(this->in_group_); + this->in_group_ = false; +} + // Command_line options. Command_line::Command_line() - : options_(), position_options_(), inputs_(), in_group_(false) + : options_(), position_options_(), inputs_() { } @@ -409,7 +456,7 @@ Command_line::process(int argc, char** argv) } } - if (this->in_group_) + if (this->inputs_.in_group()) { fprintf(stderr, _("%s: missing group end"), program_name); this->usage(); @@ -452,14 +499,7 @@ void Command_line::add_file(const char* name, bool is_lib) { Input_file_argument file(name, is_lib, this->position_options_); - if (!this->in_group_) - this->inputs_.push_back(Input_argument(file)); - else - { - assert(!this->inputs_.empty()); - assert(this->inputs_.back().is_group()); - this->inputs_.back().group()->add_file(file); - } + this->inputs_.add_file(file); } // Handle the -l option, which requires special treatment. @@ -492,14 +532,9 @@ Command_line::process_l_option(int argc, char** argv, char* arg) void Command_line::start_group(const char* arg) { - if (this->in_group_) + if (this->inputs_.in_group()) this->usage(_("may not nest groups"), arg); - - // This object is leaked. - Input_file_group* group = new Input_file_group(); - this->inputs_.push_back(Input_argument(group)); - - this->in_group_ = true; + this->inputs_.start_group(); } // Handle the --end-group option. @@ -507,9 +542,9 @@ Command_line::start_group(const char* arg) void Command_line::end_group(const char* arg) { - if (!this->in_group_) + if (!this->inputs_.in_group()) this->usage(_("group end without group start"), arg); - this->in_group_ = false; + this->inputs_.end_group(); } // Report a usage error. */ diff --git a/gold/options.h b/gold/options.h index 27db787edc..ac51524623 100644 --- a/gold/options.h +++ b/gold/options.h @@ -38,6 +38,11 @@ class General_options public: General_options(); + // -I: dynamic linker name. + const char* + dynamic_linker() const + { return this->dynamic_linker_; } + // -L: Library search path. typedef std::list Dir_list; @@ -66,9 +71,17 @@ class General_options { return this->is_static_; } private: + // Don't copy this structure. + General_options(const General_options&); + General_options& operator=(const General_options&); + friend class Command_line; friend class options::Command_line_options; + void + set_dynamic_linker(const char* arg) + { this->dynamic_linker_ = arg; } + void add_to_search_path(const char* arg) { this->search_path_.push_back(arg); } @@ -93,15 +106,12 @@ class General_options ignore(const char*) { } + const char* dynamic_linker_; Dir_list search_path_; const char* output_file_name_; bool is_relocatable_; bool is_shared_; bool is_static_; - - // Don't copy this structure. - General_options(const General_options&); - General_options& operator=(const General_options&); }; // The current state of the position dependent options. @@ -112,14 +122,16 @@ class Position_dependent_options Position_dependent_options(); // -Bstatic: Whether we are searching for a static archive rather - // -than a shared object. + // than a shared object. bool - do_static_search() + do_static_search() const { return this->do_static_search_; } - private: - friend class Command_line; - friend class options::Command_line_options; + // --as-needed: Whether to add a DT_NEEDED argument only if the + // dynamic object is used. + bool + as_needed() const + { return this->as_needed_; } void set_static_search() @@ -129,7 +141,17 @@ class Position_dependent_options set_dynamic_search() { this->do_static_search_ = false; } + void + set_as_needed() + { this->as_needed_ = true; } + + void + clear_as_needed() + { this->as_needed_ = false; } + + private: bool do_static_search_; + bool as_needed_; }; // A single file or library argument from the command line. @@ -138,7 +160,7 @@ class Input_file_argument { public: Input_file_argument() - : name_(NULL), is_lib_(false), options_() + : name_(), is_lib_(false), options_() { } Input_file_argument(const char* name, bool is_lib, @@ -148,7 +170,7 @@ class Input_file_argument const char* name() const - { return this->name_; } + { return this->name_.c_str(); } const Position_dependent_options& options() const @@ -159,7 +181,10 @@ class Input_file_argument { return this->is_lib_; } private: - const char* name_; + // We use std::string, not const char*, here for convenience when + // using script files, so that we do not have to preserve the string + // in that case. + std::string name_; bool is_lib_; Position_dependent_options options_; }; @@ -250,12 +275,60 @@ class Input_file_group Files files_; }; +// A list of files from the command line or a script. + +class Input_arguments +{ + public: + typedef std::vector Input_argument_list; + typedef Input_argument_list::const_iterator const_iterator; + + Input_arguments() + : input_argument_list_(), in_group_(false) + { } + + // Add a file. + void + add_file(const Input_file_argument& arg); + + // Start a group (the --start-group option). + void + start_group(); + + // End a group (the --end-group option). + void + end_group(); + + // Return whether we are currently in a group. + bool + in_group() const + { return this->in_group_; } + + // Iterators to iterate over the list of input files. + + const_iterator + begin() const + { return this->input_argument_list_.begin(); } + + const_iterator + end() const + { return this->input_argument_list_.end(); } + + // Return whether the list is empty. + bool + empty() const + { return this->input_argument_list_.empty(); } + + private: + Input_argument_list input_argument_list_; + bool in_group_; +}; + // All the information read from the command line. class Command_line { public: - typedef std::vector Input_arguments; typedef Input_arguments::const_iterator const_iterator; Command_line(); @@ -315,7 +388,6 @@ class Command_line General_options options_; Position_dependent_options position_options_; Input_arguments inputs_; - bool in_group_; }; } // End namespace gold. diff --git a/gold/output.cc b/gold/output.cc index 40d58668c0..aba8ee1705 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -48,14 +48,6 @@ Output_data::default_alignment(int size) abort(); } -// Output_data_const methods. - -void -Output_data_const::do_write(Output_file* output) -{ - output->write(this->offset(), data_.data(), data_.size()); -} - // Output_section_header methods. This currently assumes that the // segment and section lists are complete at construction time. @@ -357,13 +349,30 @@ Output_file_header::do_sized_write(Output_file* of) of->write_output_view(0, ehdr_size, view); } -// Output_section_got::Got_entry methods. +// Output_data_const methods. + +void +Output_data_const::do_write(Output_file* output) +{ + output->write(this->offset(), data_.data(), data_.size()); +} + +// Output_section_data methods. + +unsigned int +Output_section_data::do_out_shndx() const +{ + assert(this->output_section_ != NULL); + return this->output_section_->out_shndx(); +} + +// Output_data_got::Got_entry methods. // Write out the entry. template void -Output_section_got::Got_entry::write(unsigned char* pov) +Output_data_got::Got_entry::write(unsigned char* pov) const { Valtype val = 0; @@ -402,22 +411,30 @@ Output_section_got::Got_entry::write(unsigned char* pov) elfcpp::Swap::writeval(povv, val); } -// Output_section_data methods. +// Output_data_got methods. -unsigned int -Output_section_data::do_out_shndx() const +// Add an entry for a global symbol to the GOT. This returns true if +// this is a new GOT entry, false if the symbol already had a GOT +// entry. + +template +bool +Output_data_got::add_global(Symbol* gsym) { - assert(this->output_section_ != NULL); - return this->output_section_->out_shndx(); -} + if (gsym->has_got_offset()) + return false; -// Output_section_got methods. + this->entries_.push_back(Got_entry(gsym)); + this->set_got_size(); + gsym->set_got_offset(this->last_got_offset()); + return true; +} // Write out the GOT. template void -Output_section_got::do_write(Output_file* of) +Output_data_got::do_write(Output_file* of) { const int add = size / 8; @@ -655,7 +672,8 @@ Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags) void Output_segment::add_output_section(Output_section* os, - elfcpp::Elf_Word seg_flags) + elfcpp::Elf_Word seg_flags, + bool front) { assert((os->flags() & elfcpp::SHF_ALLOC) != 0); assert(!this->is_align_known_); @@ -690,6 +708,7 @@ Output_segment::add_output_section(Output_section* os, --p; if ((*p)->is_section_type(elfcpp::SHT_NOTE)) { + // We don't worry about the FRONT parameter. ++p; pdl->insert(p, os); return; @@ -730,6 +749,7 @@ Output_segment::add_output_section(Output_section* os, if (insert) { + // We don't worry about the FRONT parameter. ++p; pdl->insert(p, os); return; @@ -737,11 +757,14 @@ Output_segment::add_output_section(Output_section* os, } while (p != pdl->begin()); - // There are no TLS sections yet; put this one at the end of the - // section list. + // There are no TLS sections yet; put this one at the requested + // location in the section list. } - pdl->push_back(os); + if (front) + pdl->push_front(os); + else + pdl->push_back(os); } // Add an Output_data (which is not an Output_section) to the start of @@ -1121,19 +1144,15 @@ Output_section::add_input_section<64, true>( const elfcpp::Shdr<64, true>& shdr); template -void -Output_section_got<32, false>::do_write(Output_file* of); +class Output_data_got<32, false>; template -void -Output_section_got<32, true>::do_write(Output_file* of); +class Output_data_got<32, true>; template -void -Output_section_got<64, false>::do_write(Output_file* of); +class Output_data_got<64, false>; template -void -Output_section_got<64, true>::do_write(Output_file* of); +class Output_data_got<64, true>; } // End namespace gold. diff --git a/gold/output.h b/gold/output.h index dc1653a987..9763d74901 100644 --- a/gold/output.h +++ b/gold/output.h @@ -160,34 +160,6 @@ class Output_data off_t offset_; }; -// A simple case of Output_data in which we have constant data to -// output. - -class Output_data_const : public Output_data -{ - public: - Output_data_const(const std::string& data, uint64_t addralign) - : Output_data(data.size()), data_(data), addralign_(addralign) - { } - - Output_data_const(const char* p, off_t len, uint64_t addralign) - : Output_data(len), data_(p, len), addralign_(addralign) - { } - - // Write the data to the file. - void - do_write(Output_file* output); - - // Return the required alignment. - uint64_t - do_addralign() const - { return this->addralign_; } - - private: - std::string data_; - uint64_t addralign_; -}; - // Output the section headers. class Output_section_headers : public Output_data @@ -340,13 +312,40 @@ class Output_section_data : public Output_data uint64_t addralign_; }; -// Output_section_common is used to handle the common symbols. This -// is quite simple. +// A simple case of Output_data in which we have constant data to +// output. -class Output_section_common : public Output_section_data +class Output_data_const : public Output_section_data { public: - Output_section_common(uint64_t addralign) + Output_data_const(const std::string& data, uint64_t addralign) + : Output_section_data(data.size(), addralign), data_(data) + { } + + Output_data_const(const char* p, off_t len, uint64_t addralign) + : Output_section_data(len, addralign), data_(p, len) + { } + + Output_data_const(const unsigned char* p, off_t len, uint64_t addralign) + : Output_section_data(len, addralign), + data_(reinterpret_cast(p), len) + { } + + // Write the data to the file. + void + do_write(Output_file* output); + + private: + std::string data_; +}; + +// Output_data_common is used to handle the common symbols. This is +// quite simple. + +class Output_data_common : public Output_section_data +{ + public: + Output_data_common(uint64_t addralign) : Output_section_data(addralign) { } @@ -362,32 +361,26 @@ class Output_section_common : public Output_section_data { } }; -// Output_section_got is used to manage a GOT. Each entry in the GOT -// is for one symbol--either a global symbol or a local symbol in an +// Output_data_got is used to manage a GOT. Each entry in the GOT is +// for one symbol--either a global symbol or a local symbol in an // object. The target specific code adds entries to the GOT as -// needed. The GOT code is then responsible for writing out the data -// and for generating relocs as required. +// needed. template -class Output_section_got : public Output_section_data +class Output_data_got : public Output_section_data { public: typedef typename elfcpp::Elf_types::Elf_Addr Valtype; - Output_section_got() + Output_data_got() : Output_section_data(Output_data::default_alignment(size)), entries_() { } - // Add an entry for a global symbol to the GOT. This returns the - // offset of the new entry from the start of the GOT. - unsigned int - add_global(Symbol* gsym) - { - this->entries_.push_back(Got_entry(gsym)); - this->set_got_size(); - return this->last_got_offset(); - } + // Add an entry for a global symbol to the GOT. Return true if this + // is a new GOT entry, false if the symbol was already in the GOT. + bool + add_global(Symbol* gsym); // Add an entry for a local symbol to the GOT. This returns the // offset of the new entry from the start of the GOT. @@ -789,7 +782,13 @@ class Output_segment // Add an Output_section to this segment. void - add_output_section(Output_section*, elfcpp::Elf_Word seg_flags); + add_output_section(Output_section* os, elfcpp::Elf_Word seg_flags) + { this->add_output_section(os, seg_flags, false); } + + // Add an Output_section to the start of this segment. + void + add_initial_output_section(Output_section* os, elfcpp::Elf_Word seg_flags) + { this->add_output_section(os, seg_flags, true); } // Add an Output_data (which is not an Output_section) to the start // of this segment. @@ -832,6 +831,11 @@ class Output_segment typedef std::list Output_data_list; + // Add an Output_section to this segment, specifying front or back. + void + add_output_section(Output_section*, elfcpp::Elf_Word seg_flags, + bool front); + // Find the maximum alignment in an Output_data_list. static uint64_t maximum_alignment(const Output_data_list*); diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in index 5d4a4a092b..d13ddf5ab0 100644 --- a/gold/po/POTFILES.in +++ b/gold/po/POTFILES.in @@ -6,6 +6,8 @@ defstd.cc defstd.h dirsearch.cc dirsearch.h +dynobj.cc +dynobj.h fileread.cc fileread.h gold.cc @@ -26,6 +28,8 @@ readsyms.h reloc.cc reloc.h resolve.cc +script.cc +script.h stringpool.cc stringpool.h symtab.cc diff --git a/gold/po/gold.pot b/gold/po/gold.pot index a6c2aa2172..c002c5c341 100644 --- a/gold/po/gold.pot +++ b/gold/po/gold.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2006-11-07 10:50-0800\n" +"POT-Creation-Date: 2006-11-14 11:17-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -61,6 +61,101 @@ msgstr "" msgid "can not read directory %s" msgstr "" +#: dynobj.cc:97 +#, c-format +msgid "%s: %s: unexpected duplicate type %u section: %u, %u\n" +msgstr "" + +#: dynobj.cc:138 +#, c-format +msgid "%s: %s: unexpected link in section %u header: %u != %u\n" +msgstr "" + +#: dynobj.cc:176 +#, c-format +msgid "%s: %s: DYNAMIC section %u link out of range: %u\n" +msgstr "" + +#: dynobj.cc:186 +#, c-format +msgid "%s: %s: DYNAMIC section %u link %u is not a strtab\n" +msgstr "" + +#: dynobj.cc:208 +#, c-format +msgid "%s: %s: DT_SONAME value out of range: %lld >= %lld\n" +msgstr "" + +#: dynobj.cc:225 +#, c-format +msgid "%s: %s: missing DT_NULL in dynamic segment\n" +msgstr "" + +#: dynobj.cc:273 +#, c-format +msgid "%s: %s: invalid dynamic symbol table name index: %u\n" +msgstr "" + +#: dynobj.cc:281 +#, c-format +msgid "%s: %s: dynamic symbol table name section has wrong type: %u\n" +msgstr "" + +#: dynobj.cc:356 object.cc:421 +#, c-format +msgid "%s: %s: bad section name offset for section %u: %lu\n" +msgstr "" + +#: dynobj.cc:386 +#, c-format +msgid "%s: %s: duplicate definition for version %u\n" +msgstr "" + +#: dynobj.cc:430 dynobj.cc:549 +#, c-format +msgid "%s: %s: verdef vd_next field out of range: %u\n" +msgstr "" + +#: dynobj.cc:454 +#, c-format +msgid "%s: %s: verneed vn_aux field out of range: %u\n" +msgstr "" + +#: dynobj.cc:473 +#, c-format +msgid "%s: %s: verneed vna_next field out of range: %u\n" +msgstr "" + +#: dynobj.cc:486 +#, c-format +msgid "%s: %s: verneed vn_next field out of range: %u\n" +msgstr "" + +#: dynobj.cc:516 +#, c-format +msgid "%s: %s: verdef vd_cnt field too small: %u\n" +msgstr "" + +#: dynobj.cc:525 +#, c-format +msgid "%s: %s: verdef vd_aux field out of range: %u\n" +msgstr "" + +#: dynobj.cc:537 +#, c-format +msgid "%s: %s: verdaux vda_name field out of range: %u\n" +msgstr "" + +#: dynobj.cc:579 +#, c-format +msgid "%s: %s: vernaux vna_name field out of range: %u\n" +msgstr "" + +#: dynobj.cc:615 +#, c-format +msgid "%s: %s: size of dynamic symbols is not multiple of symbol size\n" +msgstr "" + #: fileread.cc:56 #, c-format msgid "%s: warning: close(%s) failed: %s" @@ -145,148 +240,147 @@ msgstr "" msgid "%s: missing expected TLS relocation\n" msgstr "" -#: i386.cc:306 i386.cc:434 i386.cc:627 +#: i386.cc:307 i386.cc:430 i386.cc:623 #, c-format msgid "%s: %s: unexpected reloc %u in object file\n" msgstr "" -#: i386.cc:339 i386.cc:358 +#: i386.cc:340 i386.cc:359 #, c-format msgid "%s: %s: unsupported reloc %u against local symbol\n" msgstr "" -#: i386.cc:415 i386.cc:469 i386.cc:487 +#: i386.cc:411 i386.cc:465 i386.cc:483 #, c-format msgid "%s: %s: unsupported reloc %u against global symbol %s\n" msgstr "" -#: i386.cc:509 +#: i386.cc:505 #, c-format msgid "%s: %s: unsupported RELA reloc section\n" msgstr "" -#: i386.cc:548 +#: i386.cc:544 #, c-format msgid "%s: %s: missing expected TLS relocation\n" msgstr "" -#: i386.cc:594 i386.cc:659 i386.cc:732 i386.cc:743 +#: i386.cc:590 i386.cc:655 i386.cc:728 i386.cc:739 #, c-format msgid "%s: %s: unsupported reloc %u\n" msgstr "" -#: i386.cc:686 +#: i386.cc:682 #, c-format msgid "%s: %s: TLS reloc but no TLS segment\n" msgstr "" -#: i386.cc:717 +#: i386.cc:713 #, c-format msgid "%s: %s: unsupported reloc type %u\n" msgstr "" -#: i386.cc:926 +#: i386.cc:922 #, c-format msgid "%s: %s: TLS relocation out of range\n" msgstr "" -#: i386.cc:944 +#: i386.cc:940 #, c-format msgid "%s: %s: TLS relocation against invalid instruction\n" msgstr "" -#: object.cc:87 +#: object.cc:31 #, c-format msgid "%s: %s: unsupported ELF machine number %d\n" msgstr "" -#: object.cc:173 +#: object.cc:87 +#, c-format +msgid "%s: %s: section name section has wrong type: %u\n" +msgstr "" + +#: object.cc:229 #, c-format msgid "%s: %s: invalid symbol table name index: %u\n" msgstr "" -#: object.cc:181 +#: object.cc:237 #, c-format msgid "%s: %s: symbol table name section has wrong type: %u\n" msgstr "" -#: object.cc:237 +#: object.cc:293 #, c-format msgid "%s: %s: section group %u info %u out of range\n" msgstr "" -#: object.cc:254 +#: object.cc:310 #, c-format msgid "%s: %s: symbol %u name offset %u out of range\n" msgstr "" -#: object.cc:288 +#: object.cc:344 #, c-format msgid "%s: %s: section %u in section group %u out of range" msgstr "" -#: object.cc:368 -#, c-format -msgid "%s: %s: bad section name offset for section %u: %lu\n" -msgstr "" - -#: object.cc:436 +#: object.cc:488 #, c-format msgid "%s: %s: size of symbols is not multiple of symbol size\n" msgstr "" -#: object.cc:521 +#: object.cc:572 #, c-format msgid "%s: %s: unknown section index %u for local symbol %u\n" msgstr "" -#: object.cc:532 +#: object.cc:583 #, c-format msgid "%s: %s: local symbol %u section index %u out of range\n" msgstr "" -#: object.cc:554 +#: object.cc:605 #, c-format msgid "%s: %s: local symbol %u section name out of range: %u >= %u\n" msgstr "" -#. elfcpp::ET_DYN -#: object.cc:731 +#: object.cc:782 #, c-format -msgid "%s: %s: dynamic objects are not yet supported\n" +msgid "%s: %s: unsupported ELF file type %d\n" msgstr "" -#: object.cc:755 object.cc:808 object.cc:829 +#: object.cc:801 object.cc:854 object.cc:875 #, c-format msgid "%s: %s: ELF file too short\n" msgstr "" -#: object.cc:764 +#: object.cc:810 #, c-format msgid "%s: %s: invalid ELF version 0\n" msgstr "" -#: object.cc:767 +#: object.cc:813 #, c-format msgid "%s: %s: unsupported ELF version %d\n" msgstr "" -#: object.cc:775 +#: object.cc:821 #, c-format msgid "%s: %s: invalid ELF class 0\n" msgstr "" -#: object.cc:782 +#: object.cc:828 #, c-format msgid "%s: %s: unsupported ELF class %d\n" msgstr "" -#: object.cc:790 +#: object.cc:836 #, c-format msgid "%s: %s: invalid ELF data encoding\n" msgstr "" -#: object.cc:797 +#: object.cc:843 #, c-format msgid "%s: %s: unsupported ELF data encoding %d\n" msgstr "" @@ -315,139 +409,160 @@ msgid "End a library search group" msgstr "" #: options.cc:234 -msgid "Add directory to search path" +msgid "Set dynamic linker path" msgstr "" #: options.cc:235 -msgid "-L DIR, --library-path DIR" +msgid "-I PROGRAM, --dynamic-linker PROGRAM" msgstr "" #: options.cc:237 +msgid "Add directory to search path" +msgstr "" + +#: options.cc:238 +msgid "-L DIR, --library-path DIR" +msgstr "" + +#: options.cc:240 msgid "Ignored for compatibility" msgstr "" -#: options.cc:239 +#: options.cc:242 msgid "Set output file name" msgstr "" -#: options.cc:240 +#: options.cc:243 msgid "-o FILE, --output FILE" msgstr "" -#: options.cc:242 +#: options.cc:245 msgid "Generate relocatable output" msgstr "" -#: options.cc:244 +#: options.cc:247 msgid "Generate shared library" msgstr "" -#: options.cc:246 +#: options.cc:249 msgid "Do not link against shared libraries" msgstr "" -#: options.cc:248 +#: options.cc:252 +msgid "Only set DT_NEEDED for following dynamic libs if used" +msgstr "" + +#: options.cc:255 +msgid "Always DT_NEEDED for following dynamic libs (default)" +msgstr "" + +#: options.cc:257 msgid "Report usage information" msgstr "" -#: options.cc:346 options.cc:397 options.cc:483 +#: options.cc:393 options.cc:444 options.cc:523 msgid "missing argument" msgstr "" -#: options.cc:359 options.cc:406 +#: options.cc:406 options.cc:453 msgid "unknown option" msgstr "" -#: options.cc:414 +#: options.cc:461 #, c-format msgid "%s: missing group end" msgstr "" -#: options.cc:496 +#: options.cc:536 msgid "may not nest groups" msgstr "" -#: options.cc:511 +#: options.cc:546 msgid "group end without group start" msgstr "" -#: options.cc:521 +#: options.cc:556 #, c-format msgid "%s: use the --help option for usage information\n" msgstr "" -#: options.cc:530 +#: options.cc:565 script.cc:1128 #, c-format msgid "%s: %s: %s\n" msgstr "" -#: options.cc:539 +#: options.cc:574 #, c-format msgid "%s: -%c: %s\n" msgstr "" -#: output.cc:521 +#: output.cc:538 #, c-format msgid "%s: %s: invalid alignment %lu for section \"%s\"\n" msgstr "" -#: output.cc:1033 +#: output.cc:1056 #, c-format msgid "%s: %s: open: %s\n" msgstr "" -#: output.cc:1042 +#: output.cc:1065 #, c-format msgid "%s: %s: lseek: %s\n" msgstr "" -#: output.cc:1049 +#: output.cc:1072 #, c-format msgid "%s: %s: write: %s\n" msgstr "" -#: output.cc:1059 +#: output.cc:1082 #, c-format msgid "%s: %s: mmap: %s\n" msgstr "" -#: output.cc:1073 +#: output.cc:1096 #, c-format msgid "%s: %s: munmap: %s\n" msgstr "" -#: output.cc:1081 +#: output.cc:1104 #, c-format msgid "%s: %s: close: %s\n" msgstr "" -#: readsyms.cc:85 +#: readsyms.cc:93 #, c-format msgid "%s: %s: ordinary object found in input group\n" msgstr "" +#: readsyms.cc:136 +#, c-format +msgid "%s: %s: file is empty\n" +msgstr "" + #. Here we have to handle any other input file types we need. -#: readsyms.cc:129 +#: readsyms.cc:149 #, c-format msgid "%s: %s: not an object or archive\n" msgstr "" -#: reloc.cc:168 reloc.cc:408 +#: reloc.cc:168 reloc.cc:409 #, c-format msgid "%s: %s: relocation section %u has bad info %u\n" msgstr "" -#: reloc.cc:187 reloc.cc:425 +#: reloc.cc:187 reloc.cc:426 #, c-format msgid "%s: %s: relocation section %u uses unexpected symbol table %u\n" msgstr "" -#: reloc.cc:203 reloc.cc:444 +#: reloc.cc:203 reloc.cc:445 #, c-format msgid "%s: %s: unexpected entsize for reloc section %u: %lu != %u" msgstr "" -#: reloc.cc:214 reloc.cc:455 +#: reloc.cc:214 reloc.cc:456 #, c-format msgid "%s: %s: reloc section %u size %lu uneven" msgstr "" @@ -462,22 +577,42 @@ msgstr "" msgid "%s: %s: unsupported symbol binding %d for symbol %s\n" msgstr "" -#: symtab.cc:441 +#: symtab.cc:443 symtab.cc:540 #, c-format msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n" msgstr "" -#: symtab.cc:458 +#: symtab.cc:460 #, c-format msgid "%s: %s: bad global symbol name offset %u at %lu\n" msgstr "" -#: symtab.cc:883 symtab.cc:1022 +#: symtab.cc:547 +#, c-format +msgid "%s: %s: too few symbol versions\n" +msgstr "" + +#: symtab.cc:567 +#, c-format +msgid "%s: %s: bad symbol name offset %u at %lu\n" +msgstr "" + +#: symtab.cc:611 +#, c-format +msgid "%s: %s: versym for symbol %zu out of range: %u\n" +msgstr "" + +#: symtab.cc:619 +#, c-format +msgid "%s: %s: versym for symbol %zu has no name: %u\n" +msgstr "" + +#: symtab.cc:1010 symtab.cc:1149 #, c-format msgid "%s: %s: unsupported symbol section 0x%x\n" msgstr "" -#: symtab.cc:1135 +#: symtab.cc:1262 #, c-format msgid "%s: %s: warning: %s\n" msgstr "" diff --git a/gold/readsyms.cc b/gold/readsyms.cc index c120fcb7cc..c301d16040 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -10,6 +10,7 @@ #include "symtab.h" #include "object.h" #include "archive.h" +#include "script.h" #include "readsyms.h" namespace gold @@ -30,8 +31,8 @@ Read_symbols::~Read_symbols() Task::Is_runnable_type Read_symbols::is_runnable(Workqueue*) { - if (this->input_.is_file() - && this->input_.file().is_lib() + if (this->input_argument_->is_file() + && this->input_argument_->file().is_lib() && this->dirpath_.token().is_blocked()) return IS_BLOCKED; @@ -53,14 +54,14 @@ Read_symbols::locks(Workqueue*) void Read_symbols::run(Workqueue* workqueue) { - if (this->input_.is_group()) + if (this->input_argument_->is_group()) { assert(this->input_group_ == NULL); this->do_group(workqueue); return; } - Input_file* input_file = new Input_file(this->input_.file()); + Input_file* input_file = new Input_file(this->input_argument_->file()); input_file->open(this->options_, this->dirpath_); // Read enough of the file to pick up the entire ELF header. @@ -79,7 +80,14 @@ Read_symbols::run(Workqueue* workqueue) { // This is an ELF object. - if (this->input_group_ != NULL) + Object* obj = make_elf_object(input_file->filename(), + input_file, 0, p, bytes); + + // We don't have a way to record a non-archive in an input + // group. If this is an ordinary object file, we can't + // include it more than once anyhow. If this is a dynamic + // object, then including it a second time changes nothing. + if (this->input_group_ != NULL && !obj->is_dynamic()) { fprintf(stderr, _("%s: %s: ordinary object found in input group\n"), @@ -87,9 +95,6 @@ Read_symbols::run(Workqueue* workqueue) gold_exit(false); } - Object* obj = make_elf_object(this->input_.file().name(), - input_file, 0, p, bytes); - Read_symbols_data* sd = new Read_symbols_data; obj->read_symbols(sd); workqueue->queue_front(new Add_symbols(this->options_, @@ -111,7 +116,8 @@ Read_symbols::run(Workqueue* workqueue) if (memcmp(p, Archive::armag, Archive::sarmag) == 0) { // This is an archive. - Archive* arch = new Archive(this->input_.file().name(), input_file); + Archive* arch = new Archive(this->input_argument_->file().name(), + input_file); arch->setup(); workqueue->queue(new Add_archive_symbols(this->options_, this->symtab_, @@ -125,6 +131,20 @@ Read_symbols::run(Workqueue* workqueue) } } + if (bytes == 0) + { + fprintf(stderr, _("%s: %s: file is empty\n"), + program_name, input_file->file().filename().c_str()); + gold_exit(false); + } + + // Try to parse this file as a script. + if (read_input_script(workqueue, this->options_, this->symtab_, + this->layout_, this->dirpath_, this->input_objects_, + this->input_group_, this->input_argument_, input_file, + p, bytes, this->this_blocker_, this->next_blocker_)) + return; + // Here we have to handle any other input file types we need. fprintf(stderr, _("%s: %s: not an object or archive\n"), program_name, input_file->file().filename().c_str()); @@ -143,14 +163,14 @@ Read_symbols::do_group(Workqueue* workqueue) { Input_group* input_group = new Input_group(); - const Input_file_group* group = this->input_.group(); + const Input_file_group* group = this->input_argument_->group(); Task_token* this_blocker = this->this_blocker_; for (Input_file_group::const_iterator p = group->begin(); p != group->end(); ++p) { - const Input_argument& arg(*p); - assert(arg.is_file()); + const Input_argument* arg = &*p; + assert(arg->is_file()); Task_token* next_blocker = new Task_token(); next_blocker->add_blocker(); diff --git a/gold/readsyms.h b/gold/readsyms.h index 6631011eb4..d5ada61d25 100644 --- a/gold/readsyms.h +++ b/gold/readsyms.h @@ -36,10 +36,10 @@ class Read_symbols : public Task // symbols. Read_symbols(const General_options& options, Input_objects* input_objects, Symbol_table* symtab, Layout* layout, const Dirsearch& dirpath, - const Input_argument& input, Input_group* input_group, + const Input_argument* input_argument, Input_group* input_group, Task_token* this_blocker, Task_token* next_blocker) : options_(options), input_objects_(input_objects), symtab_(symtab), - layout_(layout), dirpath_(dirpath), input_(input), + layout_(layout), dirpath_(dirpath), input_argument_(input_argument), input_group_(input_group), this_blocker_(this_blocker), next_blocker_(next_blocker) { } @@ -67,7 +67,7 @@ class Read_symbols : public Task Symbol_table* symtab_; Layout* layout_; const Dirsearch& dirpath_; - const Input_argument& input_; + const Input_argument* input_argument_; Input_group* input_group_; Task_token* this_blocker_; Task_token* next_blocker_; diff --git a/gold/reloc.cc b/gold/reloc.cc index bc38904094..dd3eef12fe 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -227,6 +227,7 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) } // Read the local symbols. + assert(this->symtab_shndx_ != -1U); if (this->symtab_shndx_ == 0 || this->local_symbol_count_ == 0) rd->local_symbols = NULL; else diff --git a/gold/script-c.h b/gold/script-c.h new file mode 100644 index 0000000000..e40488985d --- /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 0000000000..b22611fab8 --- /dev/null +++ b/gold/script.cc @@ -0,0 +1,1188 @@ +// script.cc -- handle linker scripts for gold. + +#include "gold.h" + +#include +#include +#include +#include +#include + +#include "options.h" +#include "fileread.h" +#include "workqueue.h" +#include "readsyms.h" +#include "yyscript.h" +#include "script.h" +#include "script-c.h" + +namespace gold +{ + +// A token read from a script file. We don't implement keywords here; +// all keywords are simply represented as a string. + +class Token +{ + public: + // Token classification. + enum Classification + { + // Token is invalid. + TOKEN_INVALID, + // Token indicates end of input. + TOKEN_EOF, + // Token is a string of characters. + TOKEN_STRING, + // Token is an operator. + TOKEN_OPERATOR, + // Token is a number (an integer). + TOKEN_INTEGER + }; + + // We need an empty constructor so that we can put this STL objects. + Token() + : classification_(TOKEN_INVALID), value_(), opcode_(0), + lineno_(0), charpos_(0) + { } + + // A general token with no value. + Token(Classification classification, int lineno, int charpos) + : classification_(classification), value_(), opcode_(0), + lineno_(lineno), charpos_(charpos) + { assert(classification == TOKEN_INVALID || classification == TOKEN_EOF); } + + // A general token with a value. + Token(Classification classification, const std::string& value, + int lineno, int charpos) + : classification_(classification), value_(value), opcode_(0), + lineno_(lineno), charpos_(charpos) + { assert(classification != TOKEN_INVALID && classification != TOKEN_EOF); } + + // A token representing a string of characters. + Token(const std::string& s, int lineno, int charpos) + : classification_(TOKEN_STRING), value_(s), opcode_(0), + lineno_(lineno), charpos_(charpos) + { } + + // A token representing an operator. + Token(int opcode, int lineno, int charpos) + : classification_(TOKEN_OPERATOR), value_(), opcode_(opcode), + lineno_(lineno), charpos_(charpos) + { } + + // Return whether the token is invalid. + bool + is_invalid() const + { return this->classification_ == TOKEN_INVALID; } + + // Return whether this is an EOF token. + bool + is_eof() const + { return this->classification_ == TOKEN_EOF; } + + // Return the token classification. + Classification + classification() const + { return this->classification_; } + + // Return the line number at which the token starts. + int + lineno() const + { return this->lineno_; } + + // Return the character position at this the token starts. + int + charpos() const + { return this->charpos_; } + + // Get the value of a token. + + const std::string& + string_value() const + { + assert(this->classification_ == TOKEN_STRING); + return this->value_; + } + + int + operator_value() const + { + assert(this->classification_ == TOKEN_OPERATOR); + return this->opcode_; + } + + int64_t + integer_value() const + { + assert(this->classification_ == TOKEN_INTEGER); + return strtoll(this->value_.c_str(), NULL, 0); + } + + private: + // The token classification. + Classification classification_; + // The token value, for TOKEN_STRING or TOKEN_INTEGER. + std::string value_; + // The token value, for TOKEN_OPERATOR. + int opcode_; + // The line number where this token started (one based). + int lineno_; + // The character position within the line where this token started + // (one based). + int charpos_; +}; + +// This class handles lexing a file into a sequence of tokens. We +// don't expect linker scripts to be large, so we just read them and +// tokenize them all at once. + +class Lex +{ + public: + Lex(Input_file* input_file) + : input_file_(input_file), tokens_() + { } + + // Tokenize the file. Return the final token, which will be either + // an invalid token or an EOF token. An invalid token indicates + // that tokenization failed. + Token + tokenize(); + + // A token sequence. + typedef std::vector Token_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(&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(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(keyv); + const Keyword_to_parsecode::Keyword_parsecode* ktt = + static_cast(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(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(closurev); + + if (closure->at_eof()) + return 0; + + const Token* token = closure->next_token(); + + switch (token->classification()) + { + default: + case Token::TOKEN_INVALID: + case Token::TOKEN_EOF: + abort(); + + case Token::TOKEN_STRING: + { + const char* str = token->string_value().c_str(); + int parsecode = Keyword_to_parsecode::keyword_to_parsecode(str); + if (parsecode != 0) + return parsecode; + lvalp->string = str; + return STRING; + } + + case Token::TOKEN_OPERATOR: + return token->operator_value(); + + case Token::TOKEN_INTEGER: + lvalp->integer = token->integer_value(); + return INTEGER; + } +} + +// This function is called by the bison parser to report an error. + +extern "C" void +yyerror(void* closurev, const char* message) +{ + Parser_closure* closure = static_cast(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(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(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(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(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(closurev); + closure->position_dependent_options().clear_as_needed(); +} diff --git a/gold/script.h b/gold/script.h new file mode 100644 index 0000000000..de2e5af1a6 --- /dev/null +++ b/gold/script.h @@ -0,0 +1,39 @@ +// script.h -- handle linker scripts for gold -*- C++ -*- + +// We implement a subset of the original GNU ld linker script language +// for compatibility. The goal is not to implement the entire +// language. It is merely to implement enough to handle common uses. +// In particular we need to handle /usr/lib/libc.so on a typical +// GNU/Linux system, and we want to handle linker scripts used by the +// Linux kernel build. + +#ifndef GOLD_SCRIPT_H +#define GOLD_SCRIPT_H + +namespace gold +{ + +class General_options; +class Symbol_table; +class Layout; +class Input_objects; +class Input_group; +class Input_file; +class Task_token; + +// FILE was found as an argument on the command line, but was not +// recognized as an ELF file. Try to read it as a script. We've +// already read BYTES of data into P. Return true if the file was +// handled. This has to handle /usr/lib/libc.so on a GNU/Linux +// system. + +bool +read_input_script(Workqueue*, const General_options&, Symbol_table*, Layout*, + const Dirsearch&, Input_objects*, Input_group*, + const Input_argument*, Input_file*, const unsigned char* p, + off_t bytes, Task_token* this_blocker, + Task_token* next_blocker); + +} // End namespace gold. + +#endif // !defined(GOLD_SCRIPT_H) diff --git a/gold/symtab.cc b/gold/symtab.cc index ac14c0121a..9279d26567 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -8,6 +8,7 @@ #include #include "object.h" +#include "dynobj.h" #include "output.h" #include "target.h" #include "workqueue.h" @@ -194,6 +195,7 @@ Symbol_table::Symbol_table_eq::operator()(const Symbol_table_key& k1, void Symbol_table::make_forwarder(Symbol* from, Symbol* to) { + assert(from != to); assert(!from->is_forwarder() && !to->is_forwarder()); this->forwarders_[from] = to; from->set_forwarder(); @@ -334,7 +336,7 @@ Symbol_table::add_from_object(Object* object, // NAME/NULL point to NAME/VERSION. insdef.first->second = ret; } - else + else if (insdef.first->second != ret) { // This is the unfortunate case where we already have // entries for both NAME/VERSION and NAME/NULL. @@ -424,8 +426,8 @@ Symbol_table::add_from_object(Object* object, template void -Symbol_table::add_from_object( - Relobj* object, +Symbol_table::add_from_relobj( + Sized_relobj* relobj, const unsigned char* syms, size_t count, const char* sym_names, @@ -436,10 +438,10 @@ Symbol_table::add_from_object( if (this->get_size() == 0) this->set_size(size); - if (size != this->get_size() || size != object->target()->get_size()) + if (size != this->get_size() || size != relobj->target()->get_size()) { fprintf(stderr, _("%s: %s: mixing 32-bit and 64-bit ELF objects\n"), - program_name, object->name().c_str()); + program_name, relobj->name().c_str()); gold_exit(false); } @@ -456,11 +458,13 @@ Symbol_table::add_from_object( { fprintf(stderr, _("%s: %s: bad global symbol name offset %u at %lu\n"), - program_name, object->name().c_str(), st_name, + program_name, relobj->name().c_str(), st_name, static_cast(i)); gold_exit(false); } + const char* name = sym_names + st_name; + // A symbol defined in a section which we are not including must // be treated as an undefined symbol. unsigned char symbuf[sym_size]; @@ -468,7 +472,7 @@ Symbol_table::add_from_object( unsigned int st_shndx = psym->get_st_shndx(); if (st_shndx != elfcpp::SHN_UNDEF && st_shndx < elfcpp::SHN_LORESERVE - && !object->is_section_included(st_shndx)) + && !relobj->is_section_included(st_shndx)) { memcpy(symbuf, p, sym_size); elfcpp::Sym_write sw(symbuf); @@ -476,8 +480,6 @@ Symbol_table::add_from_object( psym = &sym2; } - const char* name = sym_names + st_name; - // In an object file, an '@' in the name separates the symbol // name from the version name. If there are two '@' characters, // this is the default version. @@ -488,7 +490,7 @@ Symbol_table::add_from_object( { Stringpool::Key name_key; name = this->namepool_.add(name, &name_key); - res = this->add_from_object(object, name, name_key, NULL, 0, + res = this->add_from_object(relobj, name, name_key, NULL, 0, false, *psym); } else @@ -507,7 +509,7 @@ Symbol_table::add_from_object( Stringpool::Key ver_key; ver = this->namepool_.add(ver, &ver_key); - res = this->add_from_object(object, name, name_key, ver, ver_key, + res = this->add_from_object(relobj, name, name_key, ver, ver_key, def, *psym); } @@ -515,6 +517,131 @@ Symbol_table::add_from_object( } } +// Add all the symbols in a dynamic object to the hash table. + +template +void +Symbol_table::add_from_dynobj( + Sized_dynobj* 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* 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::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 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(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(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(elfcpp::VER_NDX_GLOBAL)) + { + // This symbol does not have a version. + this->add_from_object(dynobj, name, name_key, NULL, 0, false, sym); + continue; + } + + if (v >= version_map->size()) + { + fprintf(stderr, + _("%s: %s: versym for symbol %zu out of range: %u\n"), + program_name, dynobj->name().c_str(), i, v); + gold_exit(false); + } + + const char* version = (*version_map)[v]; + if (version == NULL) + { + fprintf(stderr, _("%s: %s: versym for symbol %zu has no name: %u\n"), + program_name, dynobj->name().c_str(), i, v); + gold_exit(false); + } + + Stringpool::Key version_key; + version = this->namepool_.add(version, &version_key); + + // If this is an absolute symbol, and the version name and + // symbol name are the same, then this is the version definition + // symbol. These symbols exist to support using -u to pull in + // particular versions. We do not want to record a version for + // them. + if (sym.get_st_shndx() == elfcpp::SHN_ABS && name_key == version_key) + { + this->add_from_object(dynobj, name, name_key, NULL, 0, false, sym); + continue; + } + + const bool def = !hidden && sym.get_st_shndx() != elfcpp::SHN_UNDEF; + + this->add_from_object(dynobj, name, name_key, version, version_key, + def, sym); + } +} + // Create and return a specially defined symbol. If ONLY_IF_REF is // true, then only create the symbol if there is a reference to it. @@ -1142,8 +1269,8 @@ Warnings::issue_warning(Symbol* sym, const std::string& location) const template void -Symbol_table::add_from_object<32, true>( - Relobj* object, +Symbol_table::add_from_relobj<32, true>( + Sized_relobj<32, true>* relobj, const unsigned char* syms, size_t count, const char* sym_names, @@ -1152,8 +1279,8 @@ Symbol_table::add_from_object<32, true>( template void -Symbol_table::add_from_object<32, false>( - Relobj* object, +Symbol_table::add_from_relobj<32, false>( + Sized_relobj<32, false>* relobj, const unsigned char* syms, size_t count, const char* sym_names, @@ -1162,8 +1289,8 @@ Symbol_table::add_from_object<32, false>( template void -Symbol_table::add_from_object<64, true>( - Relobj* object, +Symbol_table::add_from_relobj<64, true>( + Sized_relobj<64, true>* relobj, const unsigned char* syms, size_t count, const char* sym_names, @@ -1172,12 +1299,60 @@ Symbol_table::add_from_object<64, true>( template void -Symbol_table::add_from_object<64, false>( - Relobj* object, +Symbol_table::add_from_relobj<64, false>( + Sized_relobj<64, false>* relobj, const unsigned char* syms, size_t count, const char* sym_names, size_t sym_name_size, Symbol** sympointers); +template +void +Symbol_table::add_from_dynobj<32, true>( + Sized_dynobj<32, true>* dynobj, + const unsigned char* syms, + size_t count, + const char* sym_names, + size_t sym_name_size, + const unsigned char* versym, + size_t versym_size, + const std::vector* 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* 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* 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* version_map); + } // End namespace gold. diff --git a/gold/symtab.h b/gold/symtab.h index 65898990c8..e972600249 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -20,7 +20,11 @@ namespace gold class Object; class Relobj; +template +class Sized_relobj; class Dynobj; +template +class Sized_dynobj; class Output_data; class Output_segment; class Output_file; @@ -592,16 +596,29 @@ class Symbol_table ~Symbol_table(); - // Add COUNT external symbols from the relocatable object OBJECT to + // Add COUNT external symbols from the relocatable object RELOBJ to // the symbol table. SYMS is the symbols, SYM_NAMES is their names, // SYM_NAME_SIZE is the size of SYM_NAMES. This sets SYMPOINTERS to // point to the symbols in the symbol table. template void - add_from_object(Relobj* object, const unsigned char* syms, - size_t count, const char* sym_names, size_t sym_name_size, + add_from_relobj(Sized_relobj* 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 + void + add_from_dynobj(Sized_dynobj* 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*); + // Define a special symbol. template Sized_symbol* diff --git a/gold/target.h b/gold/target.h index 8e00a4da5a..5a86c357da 100644 --- a/gold/target.h +++ b/gold/target.h @@ -65,6 +65,11 @@ class Target has_resolve() const { return this->pti_->has_resolve; } + // Return the default name of the dynamic linker. + const char* + dynamic_linker() const + { return this->pti_->dynamic_linker; } + // Return the default address to use for the text segment. uint64_t text_segment_address() const @@ -96,6 +101,8 @@ class Target bool has_make_symbol; // Whether this target has a specific resolve function. bool has_resolve; + // The default dynamic linker name. + const char* dynamic_linker; // The default text segment address. uint64_t text_segment_address; // The ABI specified page size. diff --git a/gold/yyscript.y b/gold/yyscript.y new file mode 100644 index 0000000000..0bd2b603b2 --- /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 +#include + +#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 +%token 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 */ + ; + +%% -- 2.34.1