From 494e05f4405228561f0af4d424136128ff8830d2 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 23 Jan 2008 01:31:13 +0000 Subject: [PATCH] Parse a SECTIONS clause in a linker script. --- gold/Makefile.am | 7 +- gold/Makefile.in | 15 +- gold/configure | 6 +- gold/configure.ac | 4 +- gold/debug.h | 3 +- gold/expression.cc | 145 +++++- gold/gold.cc | 4 + gold/options.cc | 3 +- gold/script-c.h | 202 +++++++- gold/script-sections.cc | 738 +++++++++++++++++++++++++++++ gold/script-sections.h | 113 +++++ gold/script.cc | 935 +++++++++++++++++++++++++------------ gold/script.h | 154 ++++-- gold/testsuite/Makefile.am | 2 +- gold/testsuite/Makefile.in | 4 +- gold/yyscript.y | 326 ++++++++++++- 16 files changed, 2296 insertions(+), 365 deletions(-) create mode 100644 gold/script-sections.cc create mode 100644 gold/script-sections.h diff --git a/gold/Makefile.am b/gold/Makefile.am index d1c564c3f53..199c75a363f 100644 --- a/gold/Makefile.am +++ b/gold/Makefile.am @@ -8,7 +8,8 @@ tooldir = $(exec_prefix)/$(target_alias) ACLOCAL_AMFLAGS = -I ../bfd -I ../config -AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CXXFLAGS) +AM_CFLAGS = $(WARN_CFLAGS) $(LFS_CFLAGS) +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CFLAGS) INCLUDES = \ -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../elfcpp \ @@ -50,6 +51,7 @@ CCFILES = \ readsyms.cc \ reloc.cc \ resolve.cc \ + script-sections.cc \ script.cc \ stringpool.cc \ symtab.cc \ @@ -80,8 +82,9 @@ HFILES = \ readsyms.h \ reloc.h \ reloc-types.h \ - script.h \ script-c.h \ + script-sections.h \ + script.h \ stringpool.h \ symtab.h \ target.h \ diff --git a/gold/Makefile.in b/gold/Makefile.in index aa399ba4096..512345e469d 100644 --- a/gold/Makefile.in +++ b/gold/Makefile.in @@ -78,8 +78,9 @@ am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) \ layout.$(OBJEXT) merge.$(OBJEXT) object.$(OBJEXT) \ options.$(OBJEXT) output.$(OBJEXT) parameters.$(OBJEXT) \ readsyms.$(OBJEXT) reloc.$(OBJEXT) resolve.$(OBJEXT) \ - script.$(OBJEXT) stringpool.$(OBJEXT) symtab.$(OBJEXT) \ - target-select.$(OBJEXT) version.$(OBJEXT) workqueue.$(OBJEXT) \ + script-sections.$(OBJEXT) script.$(OBJEXT) \ + stringpool.$(OBJEXT) symtab.$(OBJEXT) target-select.$(OBJEXT) \ + version.$(OBJEXT) workqueue.$(OBJEXT) \ workqueue-threads.$(OBJEXT) am__objects_2 = am__objects_3 = yyscript.$(OBJEXT) @@ -181,7 +182,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INSTOBJEXT = @INSTOBJEXT@ LDFLAGS = @LDFLAGS@ -LFS_CXXFLAGS = @LFS_CXXFLAGS@ +LFS_CFLAGS = @LFS_CFLAGS@ LIBINTL = @LIBINTL@ LIBINTL_DEP = @LIBINTL_DEP@ LIBOBJS = @LIBOBJS@ @@ -275,7 +276,8 @@ AUTOMAKE_OPTIONS = SUBDIRS = po testsuite tooldir = $(exec_prefix)/$(target_alias) ACLOCAL_AMFLAGS = -I ../bfd -I ../config -AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CXXFLAGS) +AM_CFLAGS = $(WARN_CFLAGS) $(LFS_CFLAGS) +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CFLAGS) INCLUDES = \ -I$(srcdir) -I$(srcdir)/../include -I$(srcdir)/../elfcpp \ -DLOCALEDIR="\"$(datadir)/locale\"" \ @@ -309,6 +311,7 @@ CCFILES = \ readsyms.cc \ reloc.cc \ resolve.cc \ + script-sections.cc \ script.cc \ stringpool.cc \ symtab.cc \ @@ -339,8 +342,9 @@ HFILES = \ readsyms.h \ reloc.h \ reloc-types.h \ - script.h \ script-c.h \ + script-sections.h \ + script.h \ stringpool.h \ symtab.h \ target.h \ @@ -495,6 +499,7 @@ 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-sections.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@ diff --git a/gold/configure b/gold/configure index deccea59335..ef56ef5e3b3 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 THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE OBJDUMP_AND_CPPFILT_TRUE OBJDUMP_AND_CPPFILT_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CXXFLAGS LIBOBJS CPP EGREP CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT 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 THREADS_TRUE THREADS_FALSE TARGETOBJS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CXX CXXFLAGS ac_ct_CXX CXXDEPMODE am__fastdepCXX_TRUE am__fastdepCXX_FALSE YACC RANLIB ac_ct_RANLIB LN_S USE_NLS LIBINTL LIBINTL_DEP INCINTL XGETTEXT GMSGFMT POSUB CATALOGS DATADIRNAME INSTOBJEXT GENCAT CATOBJEXT MKINSTALLDIRS MSGFMT MSGMERGE NATIVE_LINKER_TRUE NATIVE_LINKER_FALSE GCC_TRUE GCC_FALSE OBJDUMP_AND_CPPFILT_TRUE OBJDUMP_AND_CPPFILT_FALSE FN_PTRS_IN_SO_WITHOUT_PIC_TRUE FN_PTRS_IN_SO_WITHOUT_PIC_FALSE TLS_TRUE TLS_FALSE STATIC_TLS_TRUE STATIC_TLS_FALSE WARN_CFLAGS NO_WERROR WARN_CXXFLAGS LFS_CFLAGS LIBOBJS CPP EGREP CXXCPP MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -4654,7 +4654,7 @@ fi WARN_CXXFLAGS=`echo ${WARN_CFLAGS} | sed -e 's/-Wstrict-prototypes//' -e 's/-Wmissing-prototypes//'` -LFS_CXXFLAGS="-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" +LFS_CFLAGS="-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" @@ -7068,7 +7068,7 @@ s,@STATIC_TLS_FALSE@,$STATIC_TLS_FALSE,;t t s,@WARN_CFLAGS@,$WARN_CFLAGS,;t t s,@NO_WERROR@,$NO_WERROR,;t t s,@WARN_CXXFLAGS@,$WARN_CXXFLAGS,;t t -s,@LFS_CXXFLAGS@,$LFS_CXXFLAGS,;t t +s,@LFS_CFLAGS@,$LFS_CFLAGS,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@CPP@,$CPP,;t t s,@EGREP@,$EGREP,;t t diff --git a/gold/configure.ac b/gold/configure.ac index cad5e02a81c..62fdaab4de9 100644 --- a/gold/configure.ac +++ b/gold/configure.ac @@ -199,8 +199,8 @@ AC_SUBST(WARN_CXXFLAGS) dnl Force support for large files by default. This may need to be dnl host dependent. If build == host, we can check getconf LFS_CFLAGS. -LFS_CXXFLAGS="-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" -AC_SUBST(LFS_CXXFLAGS) +LFS_CFLAGS="-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64" +AC_SUBST(LFS_CFLAGS) AC_REPLACE_FUNCS(pread) diff --git a/gold/debug.h b/gold/debug.h index c6bfb7ae678..e37e2f157a3 100644 --- a/gold/debug.h +++ b/gold/debug.h @@ -32,8 +32,9 @@ namespace gold // The different types of debugging we support. These are bitflags. const int DEBUG_TASK = 1; +const int DEBUG_SCRIPT = 2; -const int DEBUG_ALL = DEBUG_TASK; +const int DEBUG_ALL = DEBUG_TASK | DEBUG_SCRIPT; // Print a debug message if TYPE is enabled. This is a macro so that // we only evaluate the arguments if necessary. diff --git a/gold/expression.cc b/gold/expression.cc index 393328031f0..533b0255fc0 100644 --- a/gold/expression.cc +++ b/gold/expression.cc @@ -27,6 +27,7 @@ #include "parameters.h" #include "symtab.h" #include "layout.h" +#include "output.h" #include "script.h" #include "script-c.h" @@ -69,6 +70,10 @@ class Integer_expression : public Expression value(const Expression_eval_info*) { return this->val_; } + void + print(FILE* f) const + { fprintf(f, "0x%llx", static_cast(this->val_)); } + private: uint64_t val_; }; @@ -91,6 +96,10 @@ class Symbol_expression : public Expression uint64_t value(const Expression_eval_info*); + void + print(FILE* f) const + { fprintf(f, "%s", this->name_.c_str()); } + private: std::string name_; }; @@ -125,6 +134,10 @@ class Dot_expression : public Expression uint64_t value(const Expression_eval_info*); + + void + print(FILE* f) const + { fprintf(f, "."); } }; uint64_t @@ -162,6 +175,10 @@ class Unary_expression : public Expression arg_value(const Expression_eval_info* eei) const { return this->arg_->value(eei); } + void + arg_print(FILE* f) const + { this->arg_->print(f); } + private: Expression* arg_; }; @@ -180,6 +197,14 @@ class Unary_expression : public Expression uint64_t \ value(const Expression_eval_info* eei) \ { return OPERATOR this->arg_value(eei); } \ + \ + void \ + print(FILE* f) const \ + { \ + fprintf(f, "(%s ", #OPERATOR); \ + this->arg_print(f); \ + fprintf(f, ")"); \ + } \ }; \ \ extern "C" Expression* \ @@ -216,6 +241,26 @@ class Binary_expression : public Expression right_value(const Expression_eval_info* eei) const { return this->right_->value(eei); } + void + left_print(FILE* f) const + { this->left_->print(f); } + + void + right_print(FILE* f) const + { this->right_->print(f); } + + // This is a call to function FUNCTION_NAME. Print it. This is for + // debugging. + void + print_function(FILE* f, const char *function_name) const + { + fprintf(f, "%s(", function_name); + this->left_print(f); + fprintf(f, ", "); + this->right_print(f); + fprintf(f, ")"); + } + private: Expression* left_; Expression* right_; @@ -237,6 +282,16 @@ class Binary_expression : public Expression { \ return (this->left_value(eei) \ OPERATOR this->right_value(eei)); \ + } \ + \ + void \ + print(FILE* f) const \ + { \ + fprintf(f, "("); \ + this->left_print(f); \ + fprintf(f, " %s ", #OPERATOR); \ + this->right_print(f); \ + fprintf(f, ")"); \ } \ }; \ \ @@ -294,6 +349,18 @@ class Trinary_expression : public Expression arg3_value(const Expression_eval_info* eei) const { return this->arg3_->value(eei); } + void + arg1_print(FILE* f) const + { this->arg1_->print(f); } + + void + arg2_print(FILE* f) const + { this->arg2_->print(f); } + + void + arg3_print(FILE* f) const + { this->arg3_->print(f); } + private: Expression* arg1_; Expression* arg2_; @@ -316,6 +383,18 @@ class Trinary_cond : public Trinary_expression ? this->arg2_value(eei) : this->arg3_value(eei)); } + + void + print(FILE* f) const + { + fprintf(f, "("); + this->arg1_print(f); + fprintf(f, " ? "); + this->arg2_print(f); + fprintf(f, " : "); + this->arg3_print(f); + fprintf(f, ")"); + } }; extern "C" Expression* @@ -336,6 +415,10 @@ class Max_expression : public Binary_expression uint64_t value(const Expression_eval_info* eei) { return std::max(this->left_value(eei), this->right_value(eei)); } + + void + print(FILE* f) const + { this->print_function(f, "MAX"); } }; extern "C" Expression* @@ -356,6 +439,10 @@ class Min_expression : public Binary_expression uint64_t value(const Expression_eval_info* eei) { return std::min(this->left_value(eei), this->right_value(eei)); } + + void + print(FILE* f) const + { this->print_function(f, "MIN"); } }; extern "C" Expression* @@ -382,6 +469,10 @@ class Align_expression : public Binary_expression return value; return ((value + align - 1) / align) * align; } + + void + print(FILE* f) const + { this->print_function(f, "ALIGN"); } }; extern "C" Expression* @@ -408,6 +499,14 @@ class Assert_expression : public Unary_expression return value; } + void + print(FILE* f) const + { + fprintf(f, "ASSERT("); + this->arg_print(f); + fprintf(f, ", %s)", this->message_.c_str()); + } + private: std::string message_; }; @@ -419,6 +518,46 @@ script_exp_function_assert(Expression* expr, const char* message, return new Assert_expression(expr, message, length); } +// Addr function. + +class Addr_expression : public Expression +{ + public: + Addr_expression(const char* section_name, size_t section_name_len) + : section_name_(section_name, section_name_len) + { } + + uint64_t + value(const Expression_eval_info*); + + void + print(FILE* f) const + { fprintf(f, "ADDR(%s)", this->section_name_.c_str()); } + + private: + std::string section_name_; +}; + +uint64_t +Addr_expression::value(const Expression_eval_info* eei) +{ + const char* section_name = this->section_name_.c_str(); + Output_section* os = eei->layout->find_output_section(section_name); + if (os == NULL) + { + gold_error("ADDR called on nonexistent output section '%s'", + section_name); + return 0; + } + return os->address(); +} + +extern "C" Expression* +script_exp_function_addr(const char* section_name, size_t section_name_len) +{ + return new Addr_expression(section_name, section_name_len); +} + // Functions. extern "C" Expression* @@ -445,12 +584,6 @@ script_exp_function_sizeof(const char*, size_t) gold_fatal(_("SIZEOF not implemented")); } -extern "C" Expression* -script_exp_function_addr(const char*, size_t) -{ - gold_fatal(_("ADDR not implemented")); -} - extern "C" Expression* script_exp_function_loadaddr(const char*, size_t) { diff --git a/gold/gold.cc b/gold/gold.cc index d1e544afdf3..9a4304330a9 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -29,6 +29,7 @@ #include "libiberty.h" #include "options.h" +#include "debug.h" #include "workqueue.h" #include "dirsearch.h" #include "readsyms.h" @@ -182,6 +183,9 @@ queue_middle_tasks(const General_options& options, (*input_objects->dynobj_begin())->name().c_str()); } + if (is_debugging_enabled(DEBUG_SCRIPT)) + layout->script_options()->print(stderr); + // For each dynamic object, record whether we've seen all the // dynamic objects that it depends upon. input_objects->check_dynamic_dependencies(); diff --git a/gold/options.cc b/gold/options.cc index 91981361725..b1b428f3b23 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -520,7 +520,7 @@ options::Command_line_options::options[] = TWO_DASHES, &help), SPECIAL('v', "version", N_("Report version information"), NULL, TWO_DASHES, &version), - GENERAL_ARG('\0', "debug", N_("Turn on debugging (all,task)"), + GENERAL_ARG('\0', "debug", N_("Turn on debugging (all,task,script)"), N_("--debug=TYPE"), TWO_DASHES, &General_options::handle_debug_option) }; @@ -547,6 +547,7 @@ options::Command_line_options::debug_options[] = { { "all", DEBUG_ALL }, { "task", DEBUG_TASK }, + { "script", DEBUG_SCRIPT } }; const int options::Command_line_options::debug_options_size = diff --git a/gold/script-c.h b/gold/script-c.h index da8b558c298..496e18b5f24 100644 --- a/gold/script-c.h +++ b/gold/script-c.h @@ -27,7 +27,18 @@ #define GOLD_SCRIPT_C_H #ifdef __cplusplus -extern "C" { +#include +#include +#endif + +#ifdef __cplusplus + +// For the C++ code we declare the various supporting structures in +// the gold namespace. For the C code we declare it at the top level. +// The namespace level should not affect the layout of the structure. + +namespace gold +{ #endif /* A string value for the bison parser. */ @@ -44,15 +55,107 @@ struct Parser_string alike. */ #ifdef __cplusplus -namespace gold -{ class Expression; -} -typedef gold::Expression* Expression_ptr; +typedef Expression* Expression_ptr; #else typedef void* Expression_ptr; #endif +/* The information we store for an output section header in the bison + parser. */ + +struct Parser_output_section_header +{ + /* The address. This may be NULL. */ + Expression_ptr address; + /* The load address, from the AT specifier. This may be NULL. */ + Expression_ptr load_address; + /* The alignment, from the ALIGN specifier. This may be NULL. */ + Expression_ptr align; + /* The input section alignment, from the SUBALIGN specifier. This + may be NULL. */ + Expression_ptr subalign; +}; + +/* The information we store for an output section trailer in the bison + parser. */ + +struct Parser_output_section_trailer +{ + /* The fill value. This may be NULL. */ + Expression_ptr fill; +}; + +/* We keep vectors of strings. In order to manage this in both C and + C++, we use a pointer to a vector. This assumes that all pointers + look the same. */ + +#ifdef __cplusplus +typedef std::vector String_list; +typedef String_list* String_list_ptr; +#else +typedef void* String_list_ptr; +#endif + +/* The different sorts we can find in a linker script. */ + +enum Sort_wildcard +{ + SORT_WILDCARD_NONE, + SORT_WILDCARD_BY_NAME, + SORT_WILDCARD_BY_ALIGNMENT, + SORT_WILDCARD_BY_NAME_BY_ALIGNMENT, + SORT_WILDCARD_BY_ALIGNMENT_BY_NAME +}; + +/* The information we build for a single wildcard specification. */ + +struct Wildcard_section +{ + /* The wildcard spec itself. */ + struct Parser_string name; + /* How the entries should be sorted. */ + enum Sort_wildcard sort; +}; + +/* A vector of Wildcard_section entries. */ + +#ifdef __cplusplus +typedef std::vector String_sort_list; +typedef String_sort_list* String_sort_list_ptr; +#else +typedef void* String_sort_list_ptr; +#endif + +/* A list of wildcard specifications, which may include EXCLUDE_FILE + clauses. */ + +struct Wildcard_sections +{ + /* Wildcard specs. */ + String_sort_list_ptr sections; + /* Exclusions. */ + String_list_ptr exclude; +}; + +/* A complete input section specification. */ + +struct Input_section_spec +{ + /* The file name. */ + struct Wildcard_section file; + /* The list of sections. */ + struct Wildcard_sections input_sections; +}; + +struct Version_dependency_list; +struct Version_expression_list; +struct Version_tree; + +#ifdef __cplusplus +extern "C" { +#endif + /* The bison parser definitions. */ #include "yyscript.h" @@ -127,6 +230,83 @@ extern void script_set_symbol(void* closure, const char*, size_t, Expression_ptr, int provide, int hidden); +/* Called by the bison parser to add an assertion. */ + +extern void +script_add_assertion(void* closure, Expression_ptr, const char* message, + size_t messagelen); + +/* Called by the bison parser to start a SECTIONS clause. */ + +extern void +script_start_sections(void* closure); + +/* Called by the bison parser to finish a SECTIONS clause. */ + +extern void +script_finish_sections(void* closure); + +/* Called by the bison parser to start handling input section + specifications for an output section. */ + +extern void +script_start_output_section(void* closure, const char* name, size_t namelen, + const struct Parser_output_section_header*); + +/* Called by the bison parser when done handling input section + specifications for an output section. */ + +extern void +script_finish_output_section(void* closure, + const struct Parser_output_section_trailer*); + +/* Called by the bison parser to handle a data statement (LONG, BYTE, + etc.) in an output section. */ + +extern void +script_add_data(void* closure, int data_token, Expression_ptr val); + +/* Called by the bison parser to set the fill value in an output + section. */ + +extern void +script_add_fill(void* closure, Expression_ptr val); + +/* Called by the bison parser to add an input section specification to + an output section. The KEEP parameter is non-zero if this is + within a KEEP clause, meaning that the garbage collector should not + discard it. */ + +extern void +script_add_input_section(void* closure, const struct Input_section_spec*, + int keep); + +/* Create a new list of string and sort entries. */ + +extern String_sort_list_ptr +script_new_string_sort_list(const struct Wildcard_section*); + +/* Add an entry to a list of string and sort entries. */ + +extern String_sort_list_ptr +script_string_sort_list_add(String_sort_list_ptr, + const struct Wildcard_section*); + +/* Create a new list of strings. */ + +extern String_list_ptr +script_new_string_list(const char*, size_t); + +/* Add an element to a list of strings. */ + +extern String_list_ptr +script_string_list_push_back(String_list_ptr, const char*, size_t); + +/* Concatenate two string lists. */ + +extern String_list_ptr +script_string_list_append(String_list_ptr, String_list_ptr); + /* Called by the bison parser for expressions. */ extern Expression_ptr @@ -184,7 +364,7 @@ script_exp_function_min(Expression_ptr, Expression_ptr); extern Expression_ptr script_exp_function_defined(const char*, size_t); extern Expression_ptr -script_exp_function_sizeof_headers(); +script_exp_function_sizeof_headers(void); extern Expression_ptr script_exp_function_alignof(const char*, size_t); extern Expression_ptr @@ -214,10 +394,6 @@ script_exp_function_segment_start(const char*, size_t, Expression_ptr); extern Expression_ptr script_exp_function_assert(Expression_ptr, const char*, size_t); -struct Version_dependency_list; -struct Version_expression_list; -struct Version_tree; - extern void script_register_vers_node(void* closure, const char* tag, @@ -251,7 +427,11 @@ extern void version_script_pop_lang(void* closure); #ifdef __cplusplus -} +} // End extern "C" +#endif + +#ifdef __cplusplus +} // End namespace gold. #endif #endif /* !defined(GOLD_SCRIPT_C_H) */ diff --git a/gold/script-sections.cc b/gold/script-sections.cc new file mode 100644 index 00000000000..de6da3059dc --- /dev/null +++ b/gold/script-sections.cc @@ -0,0 +1,738 @@ +// script-sections.cc -- linker script SECTIONS for gold + +// Copyright 2008 Free Software Foundation, Inc. +// Written by Ian Lance Taylor . + +// This file is part of gold. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +// MA 02110-1301, USA. + +#include "gold.h" + +#include +#include + +#include "script-c.h" +#include "script.h" +#include "script-sections.h" + +// Support for the SECTIONS clause in linker scripts. + +namespace gold +{ + +// An element in a SECTIONS clause. + +class Sections_element +{ + public: + Sections_element() + { } + + virtual ~Sections_element() + { } + + virtual void + print(FILE* f) const = 0; +}; + +// An assignment in a SECTIONS clause outside of an output section. + +class Sections_element_assignment : public Sections_element +{ + public: + Sections_element_assignment(const char* name, size_t namelen, + Expression* val, bool provide, bool hidden) + : assignment_(name, namelen, val, provide, hidden) + { } + + void + print(FILE* f) const + { + fprintf(f, " "); + this->assignment_.print(f); + } + + private: + Symbol_assignment assignment_; +}; + +// An assertion in a SECTIONS clause outside of an output section. + +class Sections_element_assertion : public Sections_element +{ + public: + Sections_element_assertion(Expression* check, const char* message, + size_t messagelen) + : assertion_(check, message, messagelen) + { } + + void + print(FILE* f) const + { + fprintf(f, " "); + this->assertion_.print(f); + } + + private: + Script_assertion assertion_; +}; + +// An element in an output section in a SECTIONS clause. + +class Output_section_element +{ + public: + Output_section_element() + { } + + virtual ~Output_section_element() + { } + + virtual void + print(FILE* f) const = 0; +}; + +// A symbol assignment in an output section. + +class Output_section_element_assignment : public Output_section_element +{ + public: + Output_section_element_assignment(const char* name, size_t namelen, + Expression* val, bool provide, + bool hidden) + : assignment_(name, namelen, val, provide, hidden) + { } + + void + print(FILE* f) const + { + fprintf(f, " "); + this->assignment_.print(f); + } + + private: + Symbol_assignment assignment_; +}; + +// An assertion in an output section. + +class Output_section_element_assertion : public Output_section_element +{ + public: + Output_section_element_assertion(Expression* check, const char* message, + size_t messagelen) + : assertion_(check, message, messagelen) + { } + + void + print(FILE* f) const + { + fprintf(f, " "); + this->assertion_.print(f); + } + + private: + Script_assertion assertion_; +}; + +// A data item in an output section. + +class Output_section_element_data : public Output_section_element +{ + public: + Output_section_element_data(int size, bool is_signed, Expression* val) + : size_(size), is_signed_(is_signed), val_(val) + { } + + void + print(FILE*) const; + + private: + // The size in bytes. + int size_; + // Whether the value is signed. + bool is_signed_; + // The value. + Expression* val_; +}; + +// Print for debugging. + +void +Output_section_element_data::print(FILE* f) const +{ + const char* s; + switch (this->size_) + { + case 1: + s = "BYTE"; + break; + case 2: + s = "SHORT"; + break; + case 4: + s = "LONG"; + break; + case 8: + if (this->is_signed_) + s = "SQUAD"; + else + s = "QUAD"; + break; + default: + gold_unreachable(); + } + fprintf(f, " %s(", s); + this->val_->print(f); + fprintf(f, ")\n"); +} + +// A fill value setting in an output section. + +class Output_section_element_fill : public Output_section_element +{ + public: + Output_section_element_fill(Expression* val) + : val_(val) + { } + + void + print(FILE* f) const + { + fprintf(f, " FILL("); + this->val_->print(f); + fprintf(f, ")\n"); + } + + private: + // The new fill value. + Expression* val_; +}; + +// An input section specification in an output section + +class Output_section_element_input : public Output_section_element +{ + public: + // Note that an Input_section_spec holds some pointers to vectors. + // This constructor takes ownership of them. The parser is + // implemented such that this works. + Output_section_element_input(const Input_section_spec* spec, bool keep); + + void + print(FILE* f) const; + + private: + // An input section pattern. + struct Input_section_pattern + { + std::string pattern; + Sort_wildcard sort; + + Input_section_pattern(const char* patterna, size_t patternlena, + Sort_wildcard sorta) + : pattern(patterna, patternlena), sort(sorta) + { } + }; + + typedef std::vector Input_section_patterns; + + typedef std::vector Filename_exclusions; + + // The file name pattern. + std::string filename_pattern_; + // How the file names should be sorted. This may only be + // SORT_WILDCARD_NONE or SORT_WILDCARD_BY_NAME. + Sort_wildcard filename_sort_; + // The list of file names to exclude. + Filename_exclusions filename_exclusions_; + // The list of input section patterns. + Input_section_patterns input_section_patterns_; + // Whether to keep this section when garbage collecting. + bool keep_; +}; + +// Construct Output_section_element_input. The parser records strings +// as pointers into a copy of the script file, which will go away when +// parsing is complete. We make sure they are in std::string objects. + +Output_section_element_input::Output_section_element_input( + const Input_section_spec* spec, + bool keep) + : filename_pattern_(spec->file.name.value, spec->file.name.length), + filename_sort_(spec->file.sort), + filename_exclusions_(), + input_section_patterns_(), + keep_(keep) +{ + if (spec->input_sections.exclude != NULL) + { + for (String_list::const_iterator p = + spec->input_sections.exclude->begin(); + p != spec->input_sections.exclude->end(); + ++p) + this->filename_exclusions_.push_back(*p); + } + + if (spec->input_sections.sections != NULL) + { + Input_section_patterns& isp(this->input_section_patterns_); + for (String_sort_list::const_iterator p = + spec->input_sections.sections->begin(); + p != spec->input_sections.sections->end(); + ++p) + isp.push_back(Input_section_pattern(p->name.value, p->name.length, + p->sort)); + } +} + +// Print for debugging. + +void +Output_section_element_input::print(FILE* f) const +{ + fprintf(f, " "); + + if (this->keep_) + fprintf(f, "KEEP("); + + if (!this->filename_pattern_.empty()) + { + bool need_close_paren = false; + switch (this->filename_sort_) + { + case SORT_WILDCARD_NONE: + break; + case SORT_WILDCARD_BY_NAME: + fprintf(f, "SORT_BY_NAME("); + need_close_paren = true; + break; + default: + gold_unreachable(); + } + + fprintf(f, "%s", this->filename_pattern_.c_str()); + + if (need_close_paren) + fprintf(f, ")"); + } + + if (!this->input_section_patterns_.empty() + || !this->filename_exclusions_.empty()) + { + fprintf(f, "("); + + bool need_space = false; + if (!this->filename_exclusions_.empty()) + { + fprintf(f, "EXCLUDE_FILE("); + bool need_comma = false; + for (Filename_exclusions::const_iterator p = + this->filename_exclusions_.begin(); + p != this->filename_exclusions_.end(); + ++p) + { + if (need_comma) + fprintf(f, ", "); + fprintf(f, "%s", p->c_str()); + need_comma = true; + } + fprintf(f, ")"); + need_space = true; + } + + for (Input_section_patterns::const_iterator p = + this->input_section_patterns_.begin(); + p != this->input_section_patterns_.end(); + ++p) + { + if (need_space) + fprintf(f, " "); + + int close_parens = 0; + switch (p->sort) + { + case SORT_WILDCARD_NONE: + break; + case SORT_WILDCARD_BY_NAME: + fprintf(f, "SORT_BY_NAME("); + close_parens = 1; + break; + case SORT_WILDCARD_BY_ALIGNMENT: + fprintf(f, "SORT_BY_ALIGNMENT("); + close_parens = 1; + break; + case SORT_WILDCARD_BY_NAME_BY_ALIGNMENT: + fprintf(f, "SORT_BY_NAME(SORT_BY_ALIGNMENT("); + close_parens = 2; + break; + case SORT_WILDCARD_BY_ALIGNMENT_BY_NAME: + fprintf(f, "SORT_BY_ALIGNMENT(SORT_BY_NAME("); + close_parens = 2; + break; + default: + gold_unreachable(); + } + + fprintf(f, "%s", p->pattern.c_str()); + + for (int i = 0; i < close_parens; ++i) + fprintf(f, ")"); + + need_space = true; + } + + fprintf(f, ")"); + } + + if (this->keep_) + fprintf(f, ")"); + + fprintf(f, "\n"); +} + +// An output section. + +class Output_section_definition : public Sections_element +{ + public: + Output_section_definition(const char* name, size_t namelen, + const Parser_output_section_header* header); + + // Finish the output section with the information in the trailer. + void + finish(const Parser_output_section_trailer* trailer); + + // Add a symbol to be defined. + void + add_symbol_assignment(const char* name, size_t length, Expression* value, + bool provide, bool hidden); + // Add an assertion. + void + add_assertion(Expression* check, const char* message, size_t messagelen); + + // Add a data item to the current output section. + void + add_data(int size, bool is_signed, Expression* val); + + // Add a setting for the fill value. + void + add_fill(Expression* val); + + // Add an input section specification. + void + add_input_section(const Input_section_spec* spec, bool keep); + + // Print the contents to the FILE. This is for debugging. + void + print(FILE*) const; + + private: + typedef std::vector Output_section_elements; + + // The output section name. + std::string name_; + // The address. This may be NULL. + Expression* address_; + // The load address. This may be NULL. + Expression* load_address_; + // The alignment. This may be NULL. + Expression* align_; + // The input section alignment. This may be NULL. + Expression* subalign_; + // The fill value. This may be NULL. + Expression* fill_; + // The list of elements defining the section. + Output_section_elements elements_; +}; + +// Constructor. + +Output_section_definition::Output_section_definition( + const char* name, + size_t namelen, + const Parser_output_section_header* header) + : name_(name, namelen), + address_(header->address), + load_address_(header->load_address), + align_(header->align), + subalign_(header->subalign), + fill_(NULL), + elements_() +{ +} + +// Finish an output section. + +void +Output_section_definition::finish(const Parser_output_section_trailer* trailer) +{ + this->fill_ = trailer->fill; +} + +// Add a symbol to be defined. + +void +Output_section_definition::add_symbol_assignment(const char* name, + size_t length, + Expression* value, + bool provide, + bool hidden) +{ + Output_section_element* p = new Output_section_element_assignment(name, + length, + value, + provide, + hidden); + this->elements_.push_back(p); +} + +// Add an assertion. + +void +Output_section_definition::add_assertion(Expression* check, + const char* message, + size_t messagelen) +{ + Output_section_element* p = new Output_section_element_assertion(check, + message, + messagelen); + this->elements_.push_back(p); +} + +// Add a data item to the current output section. + +void +Output_section_definition::add_data(int size, bool is_signed, Expression* val) +{ + Output_section_element* p = new Output_section_element_data(size, is_signed, + val); + this->elements_.push_back(p); +} + +// Add a setting for the fill value. + +void +Output_section_definition::add_fill(Expression* val) +{ + Output_section_element* p = new Output_section_element_fill(val); + this->elements_.push_back(p); +} + +// Add an input section specification. + +void +Output_section_definition::add_input_section(const Input_section_spec* spec, + bool keep) +{ + Output_section_element* p = new Output_section_element_input(spec, keep); + this->elements_.push_back(p); +} + +// Print for debugging. + +void +Output_section_definition::print(FILE* f) const +{ + fprintf(f, " %s ", this->name_.c_str()); + + if (this->address_ != NULL) + { + this->address_->print(f); + fprintf(f, " "); + } + + fprintf(f, ": "); + + if (this->load_address_ != NULL) + { + fprintf(f, "AT("); + this->load_address_->print(f); + fprintf(f, ") "); + } + + if (this->align_ != NULL) + { + fprintf(f, "ALIGN("); + this->align_->print(f); + fprintf(f, ") "); + } + + if (this->subalign_ != NULL) + { + fprintf(f, "SUBALIGN("); + this->subalign_->print(f); + fprintf(f, ") "); + } + + fprintf(f, "{\n"); + + for (Output_section_elements::const_iterator p = this->elements_.begin(); + p != this->elements_.end(); + ++p) + (*p)->print(f); + + fprintf(f, " }"); + + if (this->fill_ != NULL) + { + fprintf(f, " = "); + this->fill_->print(f); + } + + fprintf(f, "\n"); +} + +// Class Script_sections. + +Script_sections::Script_sections() + : saw_sections_clause_(false), + in_sections_clause_(false), + sections_elements_(NULL), + output_section_(NULL) +{ +} + +// Start a SECTIONS clause. + +void +Script_sections::start_sections() +{ + gold_assert(!this->in_sections_clause_ && this->output_section_ == NULL); + this->saw_sections_clause_ = true; + this->in_sections_clause_ = true; + if (this->sections_elements_ == NULL) + this->sections_elements_ = new Sections_elements; +} + +// Finish a SECTIONS clause. + +void +Script_sections::finish_sections() +{ + gold_assert(this->in_sections_clause_ && this->output_section_ == NULL); + this->in_sections_clause_ = false; +} + +// Add a symbol to be defined. + +void +Script_sections::add_symbol_assignment(const char* name, size_t length, + Expression* val, bool provide, + bool hidden) +{ + if (this->output_section_ != NULL) + this->output_section_->add_symbol_assignment(name, length, val, + provide, hidden); + else + { + Sections_element* p = new Sections_element_assignment(name, length, + val, provide, + hidden); + this->sections_elements_->push_back(p); + } +} + +// Add an assertion. + +void +Script_sections::add_assertion(Expression* check, const char* message, + size_t messagelen) +{ + if (this->output_section_ != NULL) + this->output_section_->add_assertion(check, message, messagelen); + else + { + Sections_element* p = new Sections_element_assertion(check, message, + messagelen); + this->sections_elements_->push_back(p); + } +} + +// Start processing entries for an output section. + +void +Script_sections::start_output_section( + const char* name, + size_t namelen, + const Parser_output_section_header *header) +{ + Output_section_definition* posd = new Output_section_definition(name, + namelen, + header); + this->sections_elements_->push_back(posd); + gold_assert(this->output_section_ == NULL); + this->output_section_ = posd; +} + +// Stop processing entries for an output section. + +void +Script_sections::finish_output_section( + const Parser_output_section_trailer* trailer) +{ + gold_assert(this->output_section_ != NULL); + this->output_section_->finish(trailer); + this->output_section_ = NULL; +} + +// Add a data item to the current output section. + +void +Script_sections::add_data(int size, bool is_signed, Expression* val) +{ + gold_assert(this->output_section_ != NULL); + this->output_section_->add_data(size, is_signed, val); +} + +// Add a fill value setting to the current output section. + +void +Script_sections::add_fill(Expression* val) +{ + gold_assert(this->output_section_ != NULL); + this->output_section_->add_fill(val); +} + +// Add an input section specification to the current output section. + +void +Script_sections::add_input_section(const Input_section_spec* spec, bool keep) +{ + gold_assert(this->output_section_ != NULL); + this->output_section_->add_input_section(spec, keep); +} + +// Print the SECTIONS clause to F for debugging. + +void +Script_sections::print(FILE* f) const +{ + if (!this->saw_sections_clause_) + return; + + fprintf(f, "SECTIONS {\n"); + + for (Sections_elements::const_iterator p = this->sections_elements_->begin(); + p != this->sections_elements_->end(); + ++p) + (*p)->print(f); + + fprintf(f, "}\n"); +} + +} // End namespace gold. diff --git a/gold/script-sections.h b/gold/script-sections.h new file mode 100644 index 00000000000..434432508d4 --- /dev/null +++ b/gold/script-sections.h @@ -0,0 +1,113 @@ +// script-sections.h -- linker script SECTIONS for gold -*- C++ -*- + +// Copyright 2008 Free Software Foundation, Inc. +// Written by Ian Lance Taylor . + +// This file is part of gold. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +// MA 02110-1301, USA. + +// This is for the support of the SECTIONS clause in linker scripts. + +#ifndef GOLD_SCRIPT_SECTIONS_H +#define GOLD_SCRIPT_SECTIONS_H + +#include +#include + +namespace gold +{ + +struct Parser_output_section_header; +struct Parser_output_section_trailer; +struct Input_section_spec; +class Expression; +class Sections_element; +class Output_section_definition; + +class Script_sections +{ + public: + Script_sections(); + + // Start a SECTIONS clause. + void + start_sections(); + + // Finish a SECTIONS clause. + void + finish_sections(); + + // Return whether we ever saw a SECTIONS clause. If we did, then + // all section layout needs to go through this class. + bool + saw_sections_clause() const + { return this->saw_sections_clause_; } + + // Return whether we are currently processing a SECTIONS clause. + bool + in_sections_clause() const + { return this->in_sections_clause_; } + + // Start processing entries for an output section. + void + start_output_section(const char* name, size_t namelen, + const Parser_output_section_header*); + + // Finish processing entries for an output section. + void + finish_output_section(const Parser_output_section_trailer*); + + // Add a data item to the current output section. + void + add_data(int size, bool is_signed, Expression* val); + + // Add a symbol to be defined. + void + add_symbol_assignment(const char* name, size_t length, Expression* value, + bool provide, bool hidden); + // Add an assertion. + void + add_assertion(Expression* check, const char* message, size_t messagelen); + + // Add a setting for the fill value. + void + add_fill(Expression* val); + + // Add an input section specification. + void + add_input_section(const Input_section_spec* spec, bool keep); + + // Print the contents to the FILE. This is for debugging. + void + print(FILE*) const; + + private: + typedef std::vector Sections_elements; + + // True if we ever saw a SECTIONS clause. + bool saw_sections_clause_; + // True if we are currently processing a SECTIONS clause. + bool in_sections_clause_; + // The list of elements in the SECTIONS clause. + Sections_elements* sections_elements_; + // The current output section, if there is one. + Output_section_definition* output_section_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_SCRIPT_SECTIONS_H diff --git a/gold/script.cc b/gold/script.cc index 3822c244a5c..16617012461 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -885,26 +885,20 @@ class Script_unblock : public Task Task_token* next_blocker_; }; -// Class Script_options. - -Script_options::Script_options() - : entry_(), symbol_assignments_() -{ -} +// class Symbol_assignment. -// Add any symbols we are defining to the symbol table. +// Add the symbol to the symbol table. This makes sure the symbol is +// there and defined. The actual value is stored later. We can't +// determine the actual value at this point, because we can't +// necessarily evaluate the expression until all ordinary symbols have +// been finalized. void -Script_options::add_symbols_to_table(Symbol_table* symtab, - const Target* target) +Symbol_assignment::add_to_table(Symbol_table* symtab, const Target* target) { - for (Symbol_assignments::iterator p = this->symbol_assignments_.begin(); - p != this->symbol_assignments_.end(); - ++p) - { - elfcpp::STV vis = p->hidden ? elfcpp::STV_HIDDEN : elfcpp::STV_DEFAULT; - p->sym = symtab->define_as_constant(target, - p->name.c_str(), + elfcpp::STV vis = this->hidden_ ? elfcpp::STV_HIDDEN : elfcpp::STV_DEFAULT; + this->sym_ = symtab->define_as_constant(target, + this->name_.c_str(), NULL, // version 0, // value 0, // size @@ -912,19 +906,26 @@ Script_options::add_symbols_to_table(Symbol_table* symtab, elfcpp::STB_GLOBAL, vis, 0, // nonvis - p->provide); - } + this->provide_); } -// Finalize symbol values. +// Finalize a symbol value. void -Script_options::finalize_symbols(Symbol_table* symtab, const Layout* layout) +Symbol_assignment::finalize(Symbol_table* symtab, const Layout* layout) { + // If we were only supposed to provide this symbol, the sym_ field + // will be NULL if the symbol was not referenced. + if (this->sym_ == NULL) + { + gold_assert(this->provide_); + return; + } + if (parameters->get_size() == 32) { #if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG) - this->sized_finalize_symbols<32>(symtab, layout); + this->sized_finalize<32>(symtab, layout); #else gold_unreachable(); #endif @@ -932,7 +933,7 @@ Script_options::finalize_symbols(Symbol_table* symtab, const Layout* layout) else if (parameters->get_size() == 64) { #if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG) - this->sized_finalize_symbols<64>(symtab, layout); + this->sized_finalize<64>(symtab, layout); #else gold_unreachable(); #endif @@ -943,19 +944,116 @@ Script_options::finalize_symbols(Symbol_table* symtab, const Layout* layout) template void -Script_options::sized_finalize_symbols(Symbol_table* symtab, - const Layout* layout) +Symbol_assignment::sized_finalize(Symbol_table* symtab, const Layout* layout) +{ + Sized_symbol* ssym = symtab->get_sized_symbol(this->sym_); + ssym->set_value(this->val_->eval(symtab, layout)); +} + +// Print for debugging. + +void +Symbol_assignment::print(FILE* f) const +{ + if (this->provide_ && this->hidden_) + fprintf(f, "PROVIDE_HIDDEN("); + else if (this->provide_) + fprintf(f, "PROVIDE("); + else if (this->hidden_) + gold_unreachable(); + + fprintf(f, "%s = ", this->name_.c_str()); + this->val_->print(f); + + if (this->provide_ || this->hidden_) + fprintf(f, ")"); + + fprintf(f, "\n"); +} + +// Class Script_assertion. + +// Check the assertion. + +void +Script_assertion::check(const Symbol_table* symtab, const Layout* layout) +{ + if (!this->check_->eval(symtab, layout)) + gold_error("%s", this->message_.c_str()); +} + +// Print for debugging. + +void +Script_assertion::print(FILE* f) const +{ + fprintf(f, "ASSERT("); + this->check_->print(f); + fprintf(f, ", \"%s\")\n", this->message_.c_str()); +} + +// Class Script_options. + +Script_options::Script_options() + : entry_(), symbol_assignments_(), version_script_info_(), + script_sections_() +{ +} + +// Add a symbol to be defined. + +void +Script_options::add_symbol_assignment(const char* name, size_t length, + Expression* value, bool provide, + bool hidden) +{ + if (this->script_sections_.in_sections_clause()) + this->script_sections_.add_symbol_assignment(name, length, value, + provide, hidden); + else + { + Symbol_assignment* p = new Symbol_assignment(name, length, value, + provide, hidden); + this->symbol_assignments_.push_back(p); + } +} + +// Add an assertion. + +void +Script_options::add_assertion(Expression* check, const char* message, + size_t messagelen) +{ + if (this->script_sections_.in_sections_clause()) + this->script_sections_.add_assertion(check, message, messagelen); + else + { + Script_assertion* p = new Script_assertion(check, message, messagelen); + this->assertions_.push_back(p); + } +} + +// Add any symbols we are defining to the symbol table. + +void +Script_options::add_symbols_to_table(Symbol_table* symtab, + const Target* target) { for (Symbol_assignments::iterator p = this->symbol_assignments_.begin(); p != this->symbol_assignments_.end(); ++p) - { - if (p->sym != NULL) - { - Sized_symbol* ssym = symtab->get_sized_symbol(p->sym); - ssym->set_value(p->value->eval(symtab, layout)); - } - } + (*p)->add_to_table(symtab, target); +} + +// Finalize symbol values. + +void +Script_options::finalize_symbols(Symbol_table* symtab, const Layout* layout) +{ + for (Symbol_assignments::iterator p = this->symbol_assignments_.begin(); + p != this->symbol_assignments_.end(); + ++p) + (*p)->finalize(symtab, layout); } // This class holds data passed through the parser to the lexer and to @@ -1283,6 +1381,32 @@ Script_options::define_symbol(const char* definition) return true; } +// Print the script to F for debugging. + +void +Script_options::print(FILE* f) const +{ + fprintf(f, "%s: Dumping linker script\n", program_name); + + if (!this->entry_.empty()) + fprintf(f, "ENTRY(%s)\n", this->entry_.c_str()); + + for (Symbol_assignments::const_iterator p = + this->symbol_assignments_.begin(); + p != this->symbol_assignments_.end(); + ++p) + (*p)->print(f); + + for (Assertions::const_iterator p = this->assertions_.begin(); + p != this->assertions_.end(); + ++p) + (*p)->print(f); + + this->script_sections_.print(f); + + this->version_script_info_.print(f); +} + // Manage mapping from keywords to the codes expected by the bison // parser. We construct one global object for each lex mode with // keywords. @@ -1333,13 +1457,11 @@ script_keyword_parsecodes[] = { "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 }, @@ -1349,7 +1471,6 @@ script_keyword_parsecodes[] = { "GROUP", GROUP }, { "HLL", HLL }, { "INCLUDE", INCLUDE }, - { "INFO", INFO }, { "INHIBIT_COMMON_ALLOCATION", INHIBIT_COMMON_ALLOCATION }, { "INPUT", INPUT }, { "KEEP", KEEP }, @@ -1363,7 +1484,6 @@ script_keyword_parsecodes[] = { "NEXT", NEXT }, { "NOCROSSREFS", NOCROSSREFS }, { "NOFLOAT", NOFLOAT }, - { "NOLOAD", NOLOAD }, { "ONLY_IF_RO", ONLY_IF_RO }, { "ONLY_IF_RW", ONLY_IF_RW }, { "OPTION", OPTION }, @@ -1464,119 +1584,354 @@ Keyword_to_parsecode::keyword_to_parsecode(const char* keyword, return ktt->parsecode; } -} // End namespace gold. +// The following structs are used within the VersionInfo class as well +// as in the bison helper functions. They store the information +// parsed from the version script. -// The remaining functions are extern "C", so it's clearer to not put -// them in namespace gold. +// A single version expression. +// For example, pattern="std::map*" and language="C++". +// pattern and language should be from the stringpool +struct Version_expression { + Version_expression(const std::string& pattern, + const std::string& language, + bool exact_match) + : pattern(pattern), language(language), exact_match(exact_match) {} -using namespace gold; + std::string pattern; + std::string language; + // If false, we use glob() to match pattern. If true, we use strcmp(). + bool exact_match; +}; -// 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); - const Token* token = closure->next_token(); - switch (token->classification()) - { - default: - gold_unreachable(); +// A list of expressions. +struct Version_expression_list { + std::vector expressions; +}; - case Token::TOKEN_INVALID: - yyerror(closurev, "invalid character"); - return 0; - case Token::TOKEN_EOF: - return 0; +// A list of which versions upon which another version depends. +// Strings should be from the Stringpool. +struct Version_dependency_list { + std::vector dependencies; +}; - case Token::TOKEN_STRING: - { - // This is either a keyword or a STRING. - size_t len; - const char* str = token->string_value(&len); - int parsecode = 0; - switch (closure->lex_mode()) - { - case Lex::LINKER_SCRIPT: - parsecode = script_keywords.keyword_to_parsecode(str, len); - break; - case Lex::VERSION_SCRIPT: - parsecode = version_script_keywords.keyword_to_parsecode(str, len); - break; - default: - break; - } - if (parsecode != 0) - return parsecode; - lvalp->string.value = str; - lvalp->string.length = len; - return STRING; - } - case Token::TOKEN_QUOTED_STRING: - lvalp->string.value = token->string_value(&lvalp->string.length); - return QUOTED_STRING; +// The total definition of a version. It includes the tag for the +// version, its global and local expressions, and any dependencies. +struct Version_tree { + Version_tree() + : tag(), global(NULL), local(NULL), dependencies(NULL) {} - case Token::TOKEN_OPERATOR: - return token->operator_value(); + std::string tag; + const struct Version_expression_list* global; + const struct Version_expression_list* local; + const struct Version_dependency_list* dependencies; +}; - case Token::TOKEN_INTEGER: - lvalp->integer = token->integer_value(); - return INTEGER; - } +Version_script_info::~Version_script_info() +{ + for (size_t k = 0; k < dependency_lists_.size(); ++k) + delete dependency_lists_[k]; + for (size_t k = 0; k < version_trees_.size(); ++k) + delete version_trees_[k]; + for (size_t k = 0; k < expression_lists_.size(); ++k) + delete expression_lists_[k]; } -// This function is called by the bison parser to report an error. - -extern "C" void -yyerror(void* closurev, const char* message) +std::vector +Version_script_info::get_versions() const { - Parser_closure* closure = static_cast(closurev); - gold_error(_("%s:%d:%d: %s"), closure->filename(), closure->lineno(), - closure->charpos(), message); + std::vector ret; + for (size_t j = 0; j < version_trees_.size(); ++j) + ret.push_back(version_trees_[j]->tag); + return ret; } -// Called by the bison parser to add a file to the link. - -extern "C" void -script_add_file(void* closurev, const char* name, size_t length) +std::vector +Version_script_info::get_dependencies(const char* version) const { - Parser_closure* closure = static_cast(closurev); - - // If this is an absolute path, and we found the script in the - // sysroot, then we want to prepend the sysroot to the file name. - // For example, this is how we handle a cross link to the x86_64 - // libc.so, which refers to /lib/libc.so.6. - std::string name_string(name, length); - const char* extra_search_path = "."; - std::string script_directory; - if (IS_ABSOLUTE_PATH(name_string.c_str())) - { - if (closure->is_in_sysroot()) - { - const std::string& sysroot(parameters->sysroot()); - gold_assert(!sysroot.empty()); - name_string = sysroot + name_string; - } - } - else - { - // In addition to checking the normal library search path, we - // also want to check in the script-directory. - const char *slash = strrchr(closure->filename(), '/'); - if (slash != NULL) - { - script_directory.assign(closure->filename(), - slash - closure->filename() + 1); - extra_search_path = script_directory.c_str(); - } - } - - Input_file_argument file(name_string.c_str(), false, extra_search_path, - closure->position_dependent_options()); - closure->inputs()->add_file(file); + std::vector ret; + for (size_t j = 0; j < version_trees_.size(); ++j) + if (version_trees_[j]->tag == version) + { + const struct Version_dependency_list* deps = + version_trees_[j]->dependencies; + if (deps != NULL) + for (size_t k = 0; k < deps->dependencies.size(); ++k) + ret.push_back(deps->dependencies[k]); + return ret; + } + return ret; +} + +const std::string& +Version_script_info::get_symbol_version_helper(const char* symbol_name, + bool check_global) const +{ + for (size_t j = 0; j < version_trees_.size(); ++j) + { + // Is it a global symbol for this version? + const Version_expression_list* explist = + check_global ? version_trees_[j]->global : version_trees_[j]->local; + if (explist != NULL) + for (size_t k = 0; k < explist->expressions.size(); ++k) + { + const char* name_to_match = symbol_name; + const struct Version_expression& exp = explist->expressions[k]; + char* demangled_name = NULL; + if (exp.language == "C++") + { + demangled_name = cplus_demangle(symbol_name, + DMGL_ANSI | DMGL_PARAMS); + // This isn't a C++ symbol. + if (demangled_name == NULL) + continue; + name_to_match = demangled_name; + } + else if (exp.language == "Java") + { + demangled_name = cplus_demangle(symbol_name, + (DMGL_ANSI | DMGL_PARAMS + | DMGL_JAVA)); + // This isn't a Java symbol. + if (demangled_name == NULL) + continue; + name_to_match = demangled_name; + } + bool matched; + if (exp.exact_match) + matched = strcmp(exp.pattern.c_str(), name_to_match) == 0; + else + matched = fnmatch(exp.pattern.c_str(), name_to_match, + FNM_NOESCAPE) == 0; + if (demangled_name != NULL) + free(demangled_name); + if (matched) + return version_trees_[j]->tag; + } + } + static const std::string empty = ""; + return empty; +} + +struct Version_dependency_list* +Version_script_info::allocate_dependency_list() +{ + dependency_lists_.push_back(new Version_dependency_list); + return dependency_lists_.back(); +} + +struct Version_expression_list* +Version_script_info::allocate_expression_list() +{ + expression_lists_.push_back(new Version_expression_list); + return expression_lists_.back(); +} + +struct Version_tree* +Version_script_info::allocate_version_tree() +{ + version_trees_.push_back(new Version_tree); + return version_trees_.back(); +} + +// Print for debugging. + +void +Version_script_info::print(FILE* f) const +{ + if (this->empty()) + return; + + fprintf(f, "VERSION {"); + + for (size_t i = 0; i < this->version_trees_.size(); ++i) + { + const Version_tree* vt = this->version_trees_[i]; + + if (vt->tag.empty()) + fprintf(f, " {\n"); + else + fprintf(f, " %s {\n", vt->tag.c_str()); + + if (vt->global != NULL) + { + fprintf(f, " global :\n"); + this->print_expression_list(f, vt->global); + } + + if (vt->local != NULL) + { + fprintf(f, " local :\n"); + this->print_expression_list(f, vt->local); + } + + fprintf(f, " }"); + if (vt->dependencies != NULL) + { + const Version_dependency_list* deps = vt->dependencies; + for (size_t j = 0; j < deps->dependencies.size(); ++j) + { + if (j < deps->dependencies.size() - 1) + fprintf(f, "\n"); + fprintf(f, " %s", deps->dependencies[j].c_str()); + } + } + fprintf(f, ";\n"); + } + + fprintf(f, "}\n"); +} + +void +Version_script_info::print_expression_list( + FILE* f, + const Version_expression_list* vel) const +{ + std::string current_language; + for (size_t i = 0; i < vel->expressions.size(); ++i) + { + const Version_expression& ve(vel->expressions[i]); + + if (ve.language != current_language) + { + if (!current_language.empty()) + fprintf(f, " }\n"); + fprintf(f, " extern \"%s\" {\n", ve.language.c_str()); + current_language = ve.language; + } + + fprintf(f, " "); + if (!current_language.empty()) + fprintf(f, " "); + + if (ve.exact_match) + fprintf(f, "\""); + fprintf(f, "%s", ve.pattern.c_str()); + if (ve.exact_match) + fprintf(f, "\""); + + fprintf(f, "\n"); + } + + if (!current_language.empty()) + fprintf(f, " }\n"); +} + +} // 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); + const Token* token = closure->next_token(); + switch (token->classification()) + { + default: + gold_unreachable(); + + case Token::TOKEN_INVALID: + yyerror(closurev, "invalid character"); + return 0; + + case Token::TOKEN_EOF: + return 0; + + case Token::TOKEN_STRING: + { + // This is either a keyword or a STRING. + size_t len; + const char* str = token->string_value(&len); + int parsecode = 0; + switch (closure->lex_mode()) + { + case Lex::LINKER_SCRIPT: + parsecode = script_keywords.keyword_to_parsecode(str, len); + break; + case Lex::VERSION_SCRIPT: + parsecode = version_script_keywords.keyword_to_parsecode(str, len); + break; + default: + break; + } + if (parsecode != 0) + return parsecode; + lvalp->string.value = str; + lvalp->string.length = len; + return STRING; + } + + case Token::TOKEN_QUOTED_STRING: + lvalp->string.value = token->string_value(&lvalp->string.length); + return QUOTED_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); + gold_error(_("%s:%d:%d: %s"), closure->filename(), closure->lineno(), + closure->charpos(), message); +} + +// Called by the bison parser to add a file to the link. + +extern "C" void +script_add_file(void* closurev, const char* name, size_t length) +{ + Parser_closure* closure = static_cast(closurev); + + // If this is an absolute path, and we found the script in the + // sysroot, then we want to prepend the sysroot to the file name. + // For example, this is how we handle a cross link to the x86_64 + // libc.so, which refers to /lib/libc.so.6. + std::string name_string(name, length); + const char* extra_search_path = "."; + std::string script_directory; + if (IS_ABSOLUTE_PATH(name_string.c_str())) + { + if (closure->is_in_sysroot()) + { + const std::string& sysroot(parameters->sysroot()); + gold_assert(!sysroot.empty()); + name_string = sysroot + name_string; + } + } + else + { + // In addition to checking the normal library search path, we + // also want to check in the script-directory. + const char *slash = strrchr(closure->filename(), '/'); + if (slash != NULL) + { + script_directory.assign(closure->filename(), + slash - closure->filename() + 1); + extra_search_path = script_directory.c_str(); + } + } + + Input_file_argument file(name_string.c_str(), false, extra_search_path, + closure->position_dependent_options()); + closure->inputs()->add_file(file); } // Called by the bison parser to start a group. If we are already in @@ -1648,6 +2003,16 @@ script_set_symbol(void* closurev, const char* name, size_t length, provide, hidden); } +// Called by the bison parser to add an assertion. + +extern "C" void +script_add_assertion(void* closurev, Expression* check, const char* message, + size_t messagelen) +{ + Parser_closure* closure = static_cast(closurev); + closure->script_options()->add_assertion(check, message, messagelen); +} + // Called by the bison parser to parse an OPTION. extern "C" void @@ -1678,7 +2043,7 @@ script_parse_option(void* closurev, const char* option, size_t length) /* Called by the bison parser to push the lexer into expression mode. */ -extern void +extern "C" void script_push_lex_into_expression_mode(void* closurev) { Parser_closure* closure = static_cast(closurev); @@ -1688,7 +2053,7 @@ script_push_lex_into_expression_mode(void* closurev) /* Called by the bison parser to push the lexer into version mode. */ -extern void +extern "C" void script_push_lex_into_version_mode(void* closurev) { Parser_closure* closure = static_cast(closurev); @@ -1697,165 +2062,13 @@ script_push_lex_into_version_mode(void* closurev) /* Called by the bison parser to pop the lexer mode. */ -extern void +extern "C" void script_pop_lex_mode(void* closurev) { Parser_closure* closure = static_cast(closurev); closure->pop_lex_mode(); } -// The following structs are used within the VersionInfo class as well -// as in the bison helper functions. They store the information -// parsed from the version script. - -// A single version expression. -// For example, pattern="std::map*" and language="C++". -// pattern and language should be from the stringpool -struct Version_expression { - Version_expression(const std::string& pattern, - const std::string& language, - bool exact_match) - : pattern(pattern), language(language), exact_match(exact_match) {} - - std::string pattern; - std::string language; - // If false, we use glob() to match pattern. If true, we use strcmp(). - bool exact_match; -}; - - -// A list of expressions. -struct Version_expression_list { - std::vector expressions; -}; - - -// A list of which versions upon which another version depends. -// Strings should be from the Stringpool. -struct Version_dependency_list { - std::vector dependencies; -}; - - -// The total definition of a version. It includes the tag for the -// version, its global and local expressions, and any dependencies. -struct Version_tree { - Version_tree() - : tag(), global(NULL), local(NULL), dependencies(NULL) {} - - std::string tag; - const struct Version_expression_list* global; - const struct Version_expression_list* local; - const struct Version_dependency_list* dependencies; -}; - -Version_script_info::~Version_script_info() -{ - for (size_t k = 0; k < dependency_lists_.size(); ++k) - delete dependency_lists_[k]; - for (size_t k = 0; k < version_trees_.size(); ++k) - delete version_trees_[k]; - for (size_t k = 0; k < expression_lists_.size(); ++k) - delete expression_lists_[k]; -} - -std::vector -Version_script_info::get_versions() const -{ - std::vector ret; - for (size_t j = 0; j < version_trees_.size(); ++j) - ret.push_back(version_trees_[j]->tag); - return ret; -} - -std::vector -Version_script_info::get_dependencies(const char* version) const -{ - std::vector ret; - for (size_t j = 0; j < version_trees_.size(); ++j) - if (version_trees_[j]->tag == version) - { - const struct Version_dependency_list* deps = - version_trees_[j]->dependencies; - if (deps != NULL) - for (size_t k = 0; k < deps->dependencies.size(); ++k) - ret.push_back(deps->dependencies[k]); - return ret; - } - return ret; -} - -const std::string& -Version_script_info::get_symbol_version_helper(const char* symbol_name, - bool check_global) const -{ - for (size_t j = 0; j < version_trees_.size(); ++j) - { - // Is it a global symbol for this version? - const Version_expression_list* explist = - check_global ? version_trees_[j]->global : version_trees_[j]->local; - if (explist != NULL) - for (size_t k = 0; k < explist->expressions.size(); ++k) - { - const char* name_to_match = symbol_name; - const struct Version_expression& exp = explist->expressions[k]; - char* demangled_name = NULL; - if (exp.language == "C++") - { - demangled_name = cplus_demangle(symbol_name, - DMGL_ANSI | DMGL_PARAMS); - // This isn't a C++ symbol. - if (demangled_name == NULL) - continue; - name_to_match = demangled_name; - } - else if (exp.language == "Java") - { - demangled_name = cplus_demangle(symbol_name, - (DMGL_ANSI | DMGL_PARAMS - | DMGL_JAVA)); - // This isn't a Java symbol. - if (demangled_name == NULL) - continue; - name_to_match = demangled_name; - } - bool matched; - if (exp.exact_match) - matched = strcmp(exp.pattern.c_str(), name_to_match) == 0; - else - matched = fnmatch(exp.pattern.c_str(), name_to_match, - FNM_NOESCAPE) == 0; - if (demangled_name != NULL) - free(demangled_name); - if (matched) - return version_trees_[j]->tag; - } - } - static const std::string empty = ""; - return empty; -} - -struct Version_dependency_list* -Version_script_info::allocate_dependency_list() -{ - dependency_lists_.push_back(new Version_dependency_list); - return dependency_lists_.back(); -} - -struct Version_expression_list* -Version_script_info::allocate_expression_list() -{ - expression_lists_.push_back(new Version_expression_list); - return expression_lists_.back(); -} - -struct Version_tree* -Version_script_info::allocate_version_tree() -{ - version_trees_.push_back(new Version_tree); - return version_trees_.back(); -} - // Register an entire version node. For example: // // GLIBC_2.1 { @@ -1957,3 +2170,151 @@ version_script_pop_lang(void* closurev) Parser_closure* closure = static_cast(closurev); closure->pop_language(); } + +// Called by the bison parser to start a SECTIONS clause. + +extern "C" void +script_start_sections(void* closurev) +{ + Parser_closure* closure = static_cast(closurev); + closure->script_options()->script_sections()->start_sections(); +} + +// Called by the bison parser to finish a SECTIONS clause. + +extern "C" void +script_finish_sections(void* closurev) +{ + Parser_closure* closure = static_cast(closurev); + closure->script_options()->script_sections()->finish_sections(); +} + +// Start processing entries for an output section. + +extern "C" void +script_start_output_section(void* closurev, const char* name, size_t namelen, + const struct Parser_output_section_header* header) +{ + Parser_closure* closure = static_cast(closurev); + closure->script_options()->script_sections()->start_output_section(name, + namelen, + header); +} + +// Finish processing entries for an output section. + +extern "C" void +script_finish_output_section(void* closurev, + const struct Parser_output_section_trailer* trail) +{ + Parser_closure* closure = static_cast(closurev); + closure->script_options()->script_sections()->finish_output_section(trail); +} + +// Add a data item (e.g., "WORD (0)") to the current output section. + +extern "C" void +script_add_data(void* closurev, int data_token, Expression* val) +{ + Parser_closure* closure = static_cast(closurev); + int size; + bool is_signed = true; + switch (data_token) + { + case QUAD: + size = 8; + is_signed = false; + break; + case SQUAD: + size = 8; + break; + case LONG: + size = 4; + break; + case SHORT: + size = 2; + break; + case BYTE: + size = 1; + break; + default: + gold_unreachable(); + } + closure->script_options()->script_sections()->add_data(size, is_signed, val); +} + +// Add a clause setting the fill value to the current output section. + +extern "C" void +script_add_fill(void* closurev, Expression* val) +{ + Parser_closure* closure = static_cast(closurev); + closure->script_options()->script_sections()->add_fill(val); +} + +// Add a new input section specification to the current output +// section. + +extern "C" void +script_add_input_section(void* closurev, + const struct Input_section_spec* spec, + int keepi) +{ + Parser_closure* closure = static_cast(closurev); + bool keep = keepi != 0; + closure->script_options()->script_sections()->add_input_section(spec, keep); +} + +// Create a new list of string/sort pairs. + +extern "C" String_sort_list_ptr +script_new_string_sort_list(const struct Wildcard_section* string_sort) +{ + return new String_sort_list(1, *string_sort); +} + +// Add an entry to a list of string/sort pairs. The way the parser +// works permits us to simply modify the first parameter, rather than +// copy the vector. + +extern "C" String_sort_list_ptr +script_string_sort_list_add(String_sort_list_ptr pv, + const struct Wildcard_section* string_sort) +{ + pv->push_back(*string_sort); + return pv; +} + +// Create a new list of strings. + +extern "C" String_list_ptr +script_new_string_list(const char* str, size_t len) +{ + return new String_list(1, std::string(str, len)); +} + +// Add an element to a list of strings. The way the parser works +// permits us to simply modify the first parameter, rather than copy +// the vector. + +extern "C" String_list_ptr +script_string_list_push_back(String_list_ptr pv, const char* str, size_t len) +{ + pv->push_back(std::string(str, len)); + return pv; +} + +// Concatenate two string lists. Either or both may be NULL. The way +// the parser works permits us to modify the parameters, rather than +// copy the vector. + +extern "C" String_list_ptr +script_string_list_append(String_list_ptr pv1, String_list_ptr pv2) +{ + if (pv1 == NULL) + return pv2; + if (pv2 == NULL) + return pv1; + pv1->insert(pv1->end(), pv2->begin(), pv2->end()); + return pv1; +} diff --git a/gold/script.h b/gold/script.h index 69906cb66e0..4d8cfab681f 100644 --- a/gold/script.h +++ b/gold/script.h @@ -30,11 +30,10 @@ #ifndef GOLD_SCRIPT_H #define GOLD_SCRIPT_H +#include #include -struct Version_dependency_list; -struct Version_expression_list; -struct Version_tree; +#include "script-sections.h" namespace gold { @@ -50,6 +49,9 @@ class Input_file; class Target; class Task_token; class Workqueue; +struct Version_dependency_list; +struct Version_expression_list; +struct Version_tree; // This class represents an expression in a linker script. @@ -68,6 +70,10 @@ class Expression uint64_t eval(const Symbol_table*, const Layout*); + // Print the expression to the FILE. This is for debugging. + virtual void + print(FILE*) const = 0; + protected: struct Expression_eval_info; @@ -90,7 +96,8 @@ class Expression // script. A single Version_script_info object per target is owned by // Script_options. -class Version_script_info { +class Version_script_info +{ public: ~Version_script_info(); @@ -138,7 +145,14 @@ class Version_script_info { struct Version_tree* allocate_version_tree(); + // Print contents to the FILE. This is for debugging. + void + print(FILE*) const; + private: + void + print_expression_list(FILE* f, const Version_expression_list*) const; + const std::string& get_symbol_version_helper(const char* symbol, bool check_global) const; @@ -147,6 +161,77 @@ class Version_script_info { std::vector version_trees_; }; +// This class manages assignments to symbols. These can appear in +// three different locations in scripts: outside of a SECTIONS clause, +// within a SECTIONS clause, and within an output section definition +// within a SECTIONS clause. This can also appear on the command line +// via the --defsym command line option. + +class Symbol_assignment +{ + public: + Symbol_assignment(const char* name, size_t namelen, Expression* val, + bool provide, bool hidden) + : name_(name, namelen), val_(val), provide_(provide), hidden_(hidden), + sym_(NULL) + { } + + // Add the symbol to the symbol table. + void + add_to_table(Symbol_table*, const Target*); + + // Finalize the symbol value. + void finalize(Symbol_table*, const Layout*); + + // Print the assignment to the FILE. This is for debugging. + void + print(FILE*) const; + + private: + // Sized version of finalize. + template + void + sized_finalize(Symbol_table*, const Layout*); + + // Symbol name. + std::string name_; + // Expression to assign to symbol. + Expression* val_; + // Whether the assignment should be provided (only set if there is + // an undefined reference to the symbol. + bool provide_; + // Whether the assignment should be hidden. + bool hidden_; + // The entry in the symbol table. + Symbol* sym_; +}; + +// This class manages assertions in linker scripts. These can appear +// in all the places where a Symbol_assignment can appear. + +class Script_assertion +{ + public: + Script_assertion(Expression* check, const char* message, + size_t messagelen) + : check_(check), message_(message, messagelen) + { } + + // Check the assertion. + void + check(const Symbol_table*, const Layout*); + + // Print the assertion to the FILE. This is for debugging. + void + print(FILE*) const; + + private: + // The expression to check. + Expression* check_; + // The message to issue if the expression fails. + std::string message_; +}; + // We can read a linker script in two different contexts: when // initially parsing the command line, and when we find an input file // which is actually a linker script. Also some of the data which can @@ -172,15 +257,14 @@ class Script_options set_entry(const char* entry, size_t length) { this->entry_.assign(entry, length); } - // Add a symbol to be defined. These are for symbol definitions - // which appear outside of a SECTIONS clause. + // Add a symbol to be defined. void add_symbol_assignment(const char* name, size_t length, Expression* value, - bool provided, bool hidden) - { - this->symbol_assignments_.push_back(Symbol_assignment(name, length, value, - provided, hidden)); - } + bool provide, bool hidden); + + // Add an assertion. + void + add_assertion(Expression* check, const char* message, size_t messagelen); // Define a symbol from the command line. bool @@ -198,43 +282,37 @@ class Script_options // else has a pointer to this object. Version_script_info* version_script_info() - { return &version_script_info_; } + { return &this->version_script_info_; } - private: - // We keep a list of symbol assignments. - struct Symbol_assignment - { - // Symbol name. - std::string name; - // Expression to assign to symbol. - Expression* value; - // Whether the assignment should be provided (only set if there is - // an undefined reference to the symbol. - bool provide; - // Whether the assignment should be hidden. - bool hidden; - // The entry in the symbol table. - Symbol* sym; - - Symbol_assignment(const char* namea, size_t lengtha, Expression* valuea, - bool providea, bool hiddena) - : name(namea, lengtha), value(valuea), provide(providea), - hidden(hiddena), sym(NULL) - { } - }; - - typedef std::vector Symbol_assignments; + // A SECTIONS clause parsed from a linker script. Everything else + // has a pointer to this object. + Script_sections* + script_sections() + { return &this->script_sections_; } - template + // Print the script to the FILE. This is for debugging. void - sized_finalize_symbols(Symbol_table*, const Layout*); + print(FILE*) const; + + private: + // We keep a list of symbol assignments which occur outside of a + // SECTIONS clause. + typedef std::vector Symbol_assignments; + + // We keep a list of all assertions whcih occur outside of a + // SECTIONS clause. + typedef std::vector Assertions; // The entry address. This will be empty if not set. std::string entry_; // Symbols to set. Symbol_assignments symbol_assignments_; + // Assertions to check. + Assertions assertions_; // Version information parsed from a version script. Version_script_info version_script_info_; + // Information from any SECTIONS clauses. + Script_sections script_sections_; }; // FILE was found as an argument on the command line, but was not diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index 5820038b5ea..36c3c003fd6 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -9,7 +9,7 @@ AUTOMAKE_OPTIONS = # The two_file_test tests -fmerge-constants, so we simply always turn # it on. This may need to be controlled by a configure option # eventually. -AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CXXFLAGS) -fmerge-constants +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CFLAGS) -fmerge-constants INCLUDES = \ -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../../include \ diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index 8f51d28105d..2879fc027ce 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -619,7 +619,7 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ INSTOBJEXT = @INSTOBJEXT@ LDFLAGS = @LDFLAGS@ -LFS_CXXFLAGS = @LFS_CXXFLAGS@ +LFS_CFLAGS = @LFS_CFLAGS@ LIBINTL = @LIBINTL@ LIBINTL_DEP = @LIBINTL_DEP@ LIBOBJS = @LIBOBJS@ @@ -714,7 +714,7 @@ AUTOMAKE_OPTIONS = # The two_file_test tests -fmerge-constants, so we simply always turn # it on. This may need to be controlled by a configure option # eventually. -AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CXXFLAGS) -fmerge-constants +AM_CXXFLAGS = $(WARN_CXXFLAGS) $(LFS_CFLAGS) -fmerge-constants INCLUDES = \ -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../../include \ -I$(srcdir)/../../elfcpp \ diff --git a/gold/yyscript.y b/gold/yyscript.y index a1d58d395ce..dfba25f966e 100644 --- a/gold/yyscript.y +++ b/gold/yyscript.y @@ -29,6 +29,7 @@ #include #include +#include #include "script-c.h" @@ -55,7 +56,19 @@ uint64_t integer; /* An expression. */ Expression_ptr expr; - // Used for version scripts and within VERSION {} + /* An output section header. */ + struct Parser_output_section_header output_section_header; + /* An output section trailer. */ + struct Parser_output_section_trailer output_section_trailer; + /* A complete input section specification. */ + struct Input_section_spec input_section_spec; + /* A list of wildcard specifications, with exclusions. */ + struct Wildcard_sections wildcard_sections; + /* A single wildcard specification. */ + struct Wildcard_section wildcard_section; + /* A list of strings. */ + String_list_ptr string_list; + /* Used for version scripts and within VERSION {}. */ struct Version_dependency_list* deplist; struct Version_expression_list* versyms; struct Version_tree* versnode; @@ -103,13 +116,11 @@ %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 @@ -120,7 +131,6 @@ %token GROUP %token HLL %token INCLUDE -%token INFO %token INHIBIT_COMMON_ALLOCATION %token INPUT %token KEEP @@ -135,7 +145,6 @@ %token NEXT %token NOCROSSREFS %token NOFLOAT -%token NOLOAD %token ONLY_IF_RO %token ONLY_IF_RW %token ORIGIN /* ORIGIN, o, org */ @@ -182,7 +191,16 @@ /* Non-terminal types, where needed. */ -%type parse_exp exp +%type parse_exp exp opt_address_and_section_type +%type opt_at opt_align opt_subalign opt_fill +%type section_header +%type section_trailer +%type data_length +%type input_section_no_keep +%type wildcard_sections +%type wildcard_file wildcard_section +%type exclude_names +%type wildcard_name %type vers_defns %type vers_tag %type verdep @@ -211,6 +229,10 @@ file_cmd: { script_end_group(closure); } | OPTION '(' string ')' { script_parse_option(closure, $3.value, $3.length); } + | SECTIONS '{' + { script_start_sections(closure); } + sections_block '}' + { script_finish_sections(closure); } | VERSIONK '{' { script_push_lex_into_version_mode(closure); } version_script '}' @@ -245,12 +267,304 @@ input_list_element: { script_end_as_needed(closure); } ; +/* Commands in a SECTIONS block. */ +sections_block: + sections_block section_block_cmd + | /* empty */ + ; + +/* A command which may appear within a SECTIONS block. */ +section_block_cmd: + file_or_sections_cmd + | STRING section_header + { script_start_output_section(closure, $1.value, $1.length, &$2); } + '{' section_cmds '}' section_trailer + { script_finish_output_section(closure, &$7); } + ; + +/* The header of an output section in a SECTIONS block--everything + after the name. */ +section_header: + { script_push_lex_into_expression_mode(closure); } + opt_address_and_section_type opt_at opt_align opt_subalign + { + $$.address = $2; + $$.load_address = $3; + $$.align = $4; + $$.subalign = $5; + script_pop_lex_mode(closure); + } + ; + +/* The optional address followed by the optional section type. This + is a separate nonterminal to avoid a shift/reduce conflict on + '(' in section_header. */ + +opt_address_and_section_type: + ':' + { $$ = NULL; } + | '(' ')' ':' + { $$ = NULL; } + | exp ':' + { $$ = $1; } + | exp '(' ')' ':' + { $$ = $1; } + | exp '(' STRING ')' ':' + { + yyerror(closure, "section types are not supported"); + $$ = $1; + } + ; + +/* The address at which an output section should be loaded. */ +opt_at: + /* empty */ + { $$ = NULL; } + | AT '(' exp ')' + { $$ = $3; } + ; + +/* The alignment of an output section. */ +opt_align: + /* empty */ + { $$ = NULL; } + | ALIGN_K '(' exp ')' + { $$ = $3; } + ; + +/* The input section alignment within an output section. */ +opt_subalign: + /* empty */ + { $$ = NULL; } + | SUBALIGN '(' exp ')' + { $$ = $3; } + ; + +/* The trailer of an output section in a SECTIONS block. */ +section_trailer: + { script_push_lex_into_expression_mode(closure); } + opt_memspec opt_at_memspec opt_phdr opt_fill opt_comma + { + $$.fill = $5; + script_pop_lex_mode(closure); + } + ; + +/* A memory specification for an output section. */ +opt_memspec: + '>' STRING + { yyerror(closure, "memory regions are not supported"); } + | /* empty */ + ; + +/* A memory specification for where to load an output section. */ +opt_at_memspec: + AT '>' STRING + { yyerror(closure, "memory regions are not supported"); } + | /* empty */ + ; + +/* The program segment an output section should go into. */ +opt_phdr: + opt_phdr ':' STRING + { yyerror(closure, "program headers are not supported"); } + | /* empty */ + ; + +/* The value to use to fill an output section. */ +opt_fill: + '=' exp + { $$ = $2; } + | /* empty */ + { $$ = NULL; } + ; + +/* Commands which may appear within the description of an output + section in a SECTIONS block. */ +section_cmds: + /* empty */ + | section_cmds section_cmd + ; + +/* A command which may appear within the description of an output + section in a SECTIONS block. */ +section_cmd: + assignment end + | input_section_spec + | data_length '(' parse_exp ')' + { script_add_data(closure, $1, $3); } + | ASSERT_K '(' parse_exp ',' STRING ')' + { script_add_assertion(closure, $3, $5.value, $5.length); } + | FILL '(' parse_exp ')' + { script_add_fill(closure, $3); } + | CONSTRUCTORS + { + /* The GNU linker uses CONSTRUCTORS for the a.out object + file format. It does nothing when using ELF. Since + some ELF linker scripts use it although it does + nothing, we accept it and ignore it. */ + } + | ';' + ; + +/* The length of data which may appear within the description of an + output section in a SECTIONS block. */ +data_length: + QUAD + { $$ = QUAD; } + | SQUAD + { $$ = SQUAD; } + | LONG + { $$ = LONG; } + | SHORT + { $$ = SHORT; } + | BYTE + { $$ = BYTE; } + ; + +/* An input section specification. This may appear within the + description of an output section in a SECTIONS block. */ +input_section_spec: + input_section_no_keep + { script_add_input_section(closure, &$1, 0); } + | KEEP '(' input_section_no_keep ')' + { script_add_input_section(closure, &$3, 1); } + ; + +/* An input section specification within a KEEP clause. */ +input_section_no_keep: + STRING + { + $$.file.name = $1; + $$.file.sort = SORT_WILDCARD_NONE; + $$.input_sections.sections = NULL; + $$.input_sections.exclude = NULL; + } + | wildcard_file '(' wildcard_sections ')' + { + $$.file = $1; + $$.input_sections = $3; + } + ; + +/* A wildcard file specification. */ +wildcard_file: + wildcard_name + { + $$.name = $1; + $$.sort = SORT_WILDCARD_NONE; + } + | SORT_BY_NAME '(' wildcard_name ')' + { + $$.name = $3; + $$.sort = SORT_WILDCARD_BY_NAME; + } + ; + +/* A list of wild card section specifications. */ +wildcard_sections: + wildcard_sections opt_comma wildcard_section + { + $$.sections = script_string_sort_list_add($1.sections, &$3); + $$.exclude = $1.exclude; + } + | wildcard_section + { + $$.sections = script_new_string_sort_list(&$1); + $$.exclude = NULL; + } + | wildcard_sections opt_comma EXCLUDE_FILE '(' exclude_names ')' + { + $$.sections = $1.sections; + $$.exclude = script_string_list_append($1.exclude, $5); + } + | EXCLUDE_FILE '(' exclude_names ')' + { + $$.sections = NULL; + $$.exclude = $3; + } + ; + +/* A single wild card specification. */ +wildcard_section: + wildcard_name + { + $$.name = $1; + $$.sort = SORT_WILDCARD_NONE; + } + | SORT_BY_NAME '(' wildcard_section ')' + { + $$.name = $3.name; + switch ($3.sort) + { + case SORT_WILDCARD_NONE: + $$.sort = SORT_WILDCARD_BY_NAME; + break; + case SORT_WILDCARD_BY_NAME: + case SORT_WILDCARD_BY_NAME_BY_ALIGNMENT: + break; + case SORT_WILDCARD_BY_ALIGNMENT: + case SORT_WILDCARD_BY_ALIGNMENT_BY_NAME: + $$.sort = SORT_WILDCARD_BY_NAME_BY_ALIGNMENT; + break; + default: + abort(); + } + } + | SORT_BY_ALIGNMENT '(' wildcard_section ')' + { + $$.name = $3.name; + switch ($3.sort) + { + case SORT_WILDCARD_NONE: + $$.sort = SORT_WILDCARD_BY_ALIGNMENT; + break; + case SORT_WILDCARD_BY_ALIGNMENT: + case SORT_WILDCARD_BY_ALIGNMENT_BY_NAME: + break; + case SORT_WILDCARD_BY_NAME: + case SORT_WILDCARD_BY_NAME_BY_ALIGNMENT: + $$.sort = SORT_WILDCARD_BY_ALIGNMENT_BY_NAME; + break; + default: + abort(); + } + } + ; + +/* A list of file names to exclude. */ +exclude_names: + exclude_names opt_comma wildcard_name + { $$ = script_string_list_push_back($1, $3.value, $3.length); } + | wildcard_name + { $$ = script_new_string_list($1.value, $1.length); } + ; + +/* A single wildcard name. We recognize '*' and '?' specially since + they are expression tokens. */ +wildcard_name: + STRING + { $$ = $1; } + | '*' + { + $$.value = "*"; + $$.length = 1; + } + | '?' + { + $$.value = "?"; + $$.length = 1; + } + ; + /* A command which may appear at the top level of a linker script, or within a SECTIONS block. */ file_or_sections_cmd: ENTRY '(' string ')' { script_set_entry(closure, $3.value, $3.length); } | assignment end + | ASSERT_K '(' parse_exp ',' STRING ')' + { script_add_assertion(closure, $3, $5.value, $5.length); } ; /* Set a symbol to a value. */ -- 2.39.2