diff options
32 files changed, 1321 insertions, 63 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index eab140b..796344c 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,5 +1,80 @@ 2009-10-06 Ian Lance Taylor <iant@google.com> + * options.h (class General_options): Define + split_stack_adjust_size parameter. + * object.h (class Object): Add uses_split_stack_ and + has_no_split_stack_ fields. Add uses_split_stack and + has_no_split_stack accessor functions. Declare + handle_split_stack_section. + (class Reloc_symbol_changes): Define. + (class Sized_relobj): Define Function_offsets. Declare + split_stack_adjust, split_stack_adjust_reltype, and + find_functions. + * object.cc (Object::handle_split_stack_section): New function. + (Sized_relobj::do_layout): Call handle_split_stack_section. + * dynobj.cc (Sized_dynobj::do_layout): Call + handle_split_stack_section. + * reloc.cc (Sized_relobj::relocate_sections): Call + split_stack_adjust for executable sections in split_stack + objects. Pass reloc_map to relocate_section. + (Sized_relobj::split_stack_adjust): New function. + (Sized_relobj::split_stack_adjust_reltype): New function. + (Sized_relobj::find_functions): New function. + * target-reloc.h: Include "object.h". + (relocate_section): Add reloc_symbol_changes parameter. Change + all callers. + * target.h (class Target): Add calls_non_split method. Declare + do_calls_non_split virtual method. Declare match_view and + set_view_to_nop. + * target.cc: Include "elfcpp.h". + (Target::do_calls_non_split): New function. + (Target::match_view): New function. + (Target::set_view_to_nop): New function. + * gold.cc (queue_middle_tasks): Give an error if mixing + split-stack and non-split-stack objects with -r. + * i386.cc (Target_i386::relocate_section): Add + reloc_symbol_changes parameter. + (Target_i386::do_calls_non_split): New function. + * x86_64.cc (Target_x86_64::relocate_section): Add + reloc_symbol_changes parameter. + (Target_x86_64::do_calls_non_split): New function. + * arm.cc (Target_arm::relocate_section): Add reloc_symbol_changes + parameter. + * powerpc.cc (Target_powerpc::relocate_section): Add + reloc_symbol_changes parameter. + * sparc.cc (Target_sparc::relocate_section): Add + reloc_symbol_changes parameter. + * configure.ac: Call AM_CONDITIONAL for the default target. + * configure: Rebuild. + * testsuite/Makefile.am (TEST_AS): New variable. + (check_SCRIPTS): Add split_i386.sh and split_x86_64.sh. + (check_DATA): Add split_i386 and split_x86_64 files. + (SPLIT_DEFSYMS): Define. + (split_i386_[1234n].o): New targets. + (split_i386_[124]): New targets. + (split_i386_[1234r].stdout): New targets. + (split_x86_64_[1234n].o): New targets. + (split_x86_64_[124]): New targets. + (split_x86_64_[1234r].stdout): New targets. + (MOSTLYCLEANFILES): Add new executables. + * testsuite/split_i386.sh: New file. + * testsuite/split_x86_64.sh: New file. + * testsuite/split_i386_1.s: New file. + * testsuite/split_i386_2.s: New file. + * testsuite/split_i386_3.s: New file. + * testsuite/split_i386_4.s: New file. + * testsuite/split_i386_n.s: New file. + * testsuite/split_x86_64_1.s: New file. + * testsuite/split_x86_64_2.s: New file. + * testsuite/split_x86_64_3.s: New file. + * testsuite/split_x86_64_4.s: New file. + * testsuite/split_x86_64_n.s: New file. + * testsuite/testfile.cc (Target_test): Update relocation_section + function. + * testsuite/Makefile.in: Rebuild. + +2009-10-06 Ian Lance Taylor <iant@google.com> + * i386.cc (class Target_i386::Relocate): Add ldo_addrs_ field. (Target_i386::Relocate::relocate_tls): Call fix_up_ldo before changing local_dynamic_type_ from LOCAL_DYNAMIC_NONE. When diff --git a/gold/arm.cc b/gold/arm.cc index 17028b5..96bbdef 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -205,7 +205,8 @@ class Target_arm : public Sized_target<32, big_endian> bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr view_address, - section_size_type view_size); + section_size_type view_size, + const Reloc_symbol_changes*); // Scan the relocs during a relocatable link. void @@ -1792,7 +1793,8 @@ Target_arm<big_endian>::relocate_section( bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr address, - section_size_type view_size) + section_size_type view_size, + const Reloc_symbol_changes* reloc_symbol_changes) { typedef typename Target_arm<big_endian>::Relocate Arm_relocate; gold_assert(sh_type == elfcpp::SHT_REL); @@ -1807,7 +1809,8 @@ Target_arm<big_endian>::relocate_section( needs_special_offset_handling, view, address, - view_size); + view_size, + reloc_symbol_changes); } // Return the size of a relocation while scanning during a relocatable diff --git a/gold/configure b/gold/configure index 59f52a5..4fa30b8 100755 --- a/gold/configure +++ b/gold/configure @@ -668,6 +668,16 @@ LDFLAGS CFLAGS CC TARGETOBJS +DEFAULT_TARGET_X86_64_FALSE +DEFAULT_TARGET_X86_64_TRUE +DEFAULT_TARGET_SPARC_FALSE +DEFAULT_TARGET_SPARC_TRUE +DEFAULT_TARGET_POWERPC_FALSE +DEFAULT_TARGET_POWERPC_TRUE +DEFAULT_TARGET_I386_FALSE +DEFAULT_TARGET_I386_TRUE +DEFAULT_TARGET_ARM_FALSE +DEFAULT_TARGET_ARM_TRUE PLUGINS_FALSE PLUGINS_TRUE THREADS_FALSE @@ -3354,6 +3364,47 @@ for targ in $target $canon_targets; do default_size=$targ_size default_big_endian=$targ_big_endian default_osabi=$targ_osabi + + if test "$targ_obj" = "arm"; then + DEFAULT_TARGET_ARM_TRUE= + DEFAULT_TARGET_ARM_FALSE='#' +else + DEFAULT_TARGET_ARM_TRUE='#' + DEFAULT_TARGET_ARM_FALSE= +fi + + if test "$targ_obj" = "i386"; then + DEFAULT_TARGET_I386_TRUE= + DEFAULT_TARGET_I386_FALSE='#' +else + DEFAULT_TARGET_I386_TRUE='#' + DEFAULT_TARGET_I386_FALSE= +fi + + if test "$targ_obj" = "powerpc"; then + DEFAULT_TARGET_POWERPC_TRUE= + DEFAULT_TARGET_POWERPC_FALSE='#' +else + DEFAULT_TARGET_POWERPC_TRUE='#' + DEFAULT_TARGET_POWERPC_FALSE= +fi + + if test "$targ_obj" = "sparc"; then + DEFAULT_TARGET_SPARC_TRUE= + DEFAULT_TARGET_SPARC_FALSE='#' +else + DEFAULT_TARGET_SPARC_TRUE='#' + DEFAULT_TARGET_SPARC_FALSE= +fi + + if test "$targ_obj" = "x86_64"; then + DEFAULT_TARGET_X86_64_TRUE= + DEFAULT_TARGET_X86_64_FALSE='#' +else + DEFAULT_TARGET_X86_64_TRUE='#' + DEFAULT_TARGET_X86_64_FALSE= +fi + fi fi fi @@ -6970,6 +7021,26 @@ if test -z "${PLUGINS_TRUE}" && test -z "${PLUGINS_FALSE}"; then as_fn_error "conditional \"PLUGINS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${DEFAULT_TARGET_ARM_TRUE}" && test -z "${DEFAULT_TARGET_ARM_FALSE}"; then + as_fn_error "conditional \"DEFAULT_TARGET_ARM\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${DEFAULT_TARGET_I386_TRUE}" && test -z "${DEFAULT_TARGET_I386_FALSE}"; then + as_fn_error "conditional \"DEFAULT_TARGET_I386\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${DEFAULT_TARGET_POWERPC_TRUE}" && test -z "${DEFAULT_TARGET_POWERPC_FALSE}"; then + as_fn_error "conditional \"DEFAULT_TARGET_POWERPC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${DEFAULT_TARGET_SPARC_TRUE}" && test -z "${DEFAULT_TARGET_SPARC_FALSE}"; then + as_fn_error "conditional \"DEFAULT_TARGET_SPARC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${DEFAULT_TARGET_X86_64_TRUE}" && test -z "${DEFAULT_TARGET_X86_64_FALSE}"; then + as_fn_error "conditional \"DEFAULT_TARGET_X86_64\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/gold/configure.ac b/gold/configure.ac index 0a53598..2492c7a 100644 --- a/gold/configure.ac +++ b/gold/configure.ac @@ -151,6 +151,12 @@ for targ in $target $canon_targets; do default_size=$targ_size default_big_endian=$targ_big_endian default_osabi=$targ_osabi + + AM_CONDITIONAL(DEFAULT_TARGET_ARM, test "$targ_obj" = "arm") + AM_CONDITIONAL(DEFAULT_TARGET_I386, test "$targ_obj" = "i386") + AM_CONDITIONAL(DEFAULT_TARGET_POWERPC, test "$targ_obj" = "powerpc") + AM_CONDITIONAL(DEFAULT_TARGET_SPARC, test "$targ_obj" = "sparc") + AM_CONDITIONAL(DEFAULT_TARGET_X86_64, test "$targ_obj" = "x86_64") fi fi fi diff --git a/gold/dynobj.cc b/gold/dynobj.cc index 48ff474..b14d06d 100644 --- a/gold/dynobj.cc +++ b/gold/dynobj.cc @@ -408,7 +408,7 @@ Sized_dynobj<size, big_endian>::do_initialize_xindex() // 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. +// here is check for .gnu.warning and .note.GNU-split-stack sections. template<int size, bool big_endian> void @@ -443,6 +443,7 @@ Sized_dynobj<size, big_endian>::do_layout(Symbol_table* symtab, const char* name = pnames + shdr.get_sh_name(); this->handle_gnu_warning_section(name, i, symtab); + this->handle_split_stack_section(name); } delete sd->section_headers; diff --git a/gold/gold.cc b/gold/gold.cc index 24542a2..43fd2fc 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -408,6 +408,23 @@ queue_middle_tasks(const General_options& options, gold_fatal(_("cannot use non-ELF output format with dynamic object %s"), (*input_objects->dynobj_begin())->name().c_str()); + if (parameters->options().relocatable()) + { + Input_objects::Relobj_iterator p = input_objects->relobj_begin(); + if (p != input_objects->relobj_end()) + { + bool uses_split_stack = (*p)->uses_split_stack(); + for (++p; p != input_objects->relobj_end(); ++p) + { + if ((*p)->uses_split_stack() != uses_split_stack) + gold_fatal(_("cannot mix split-stack '%s' and " + "non-split-stack '%s' when using -r"), + (*input_objects->relobj_begin())->name().c_str(), + (*p)->name().c_str()); + } + } + } + if (is_debugging_enabled(DEBUG_SCRIPT)) layout->script_options()->print(stderr); diff --git a/gold/i386.cc b/gold/i386.cc index 04a2fa9..c01b5f2 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -113,7 +113,8 @@ class Target_i386 : public Target_freebsd<32, false> bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr view_address, - section_size_type view_size); + section_size_type view_size, + const Reloc_symbol_changes*); // Scan the relocs during a relocatable link. void @@ -168,6 +169,13 @@ class Target_i386 : public Target_freebsd<32, false> return Target::do_is_local_label_name(name); } + // Adjust -fstack-split code which calls non-stack-split code. + void + do_calls_non_split(Relobj* object, unsigned int shndx, + section_offset_type fnoffset, section_size_type fnsize, + unsigned char* view, section_size_type view_size, + std::string* from, std::string* to) const; + // Return the size of the GOT section. section_size_type got_size() @@ -2465,7 +2473,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr address, - section_size_type view_size) + section_size_type view_size, + const Reloc_symbol_changes* reloc_symbol_changes) { gold_assert(sh_type == elfcpp::SHT_REL); @@ -2479,7 +2488,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, needs_special_offset_handling, view, address, - view_size); + view_size, + reloc_symbol_changes); } // Return the size of a relocation while scanning during a relocatable @@ -2699,6 +2709,63 @@ Target_i386::do_code_fill(section_size_type length) const return std::string(nops[length], length); } +// FNOFFSET in section SHNDX in OBJECT is the start of a function +// compiled with -fstack-split. The function calls non-stack-split +// code. We have to change the function so that it always ensures +// that it has enough stack space to run some random function. + +void +Target_i386::do_calls_non_split(Relobj* object, unsigned int shndx, + section_offset_type fnoffset, + section_size_type fnsize, + unsigned char* view, + section_size_type view_size, + std::string* from, + std::string* to) const +{ + // The function starts with a comparison of the stack pointer and a + // field in the TCB. This is followed by a jump. + + // cmp %gs:NN,%esp + if (this->match_view(view, view_size, fnoffset, "\x65\x3b\x25", 3) + && fnsize > 7) + { + // We will call __morestack if the carry flag is set after this + // comparison. We turn the comparison into an stc instruction + // and some nops. + view[fnoffset] = '\xf9'; + this->set_view_to_nop(view, view_size, fnoffset + 1, 6); + } + // lea NN(%esp),%ecx + else if (this->match_view(view, view_size, fnoffset, "\x8d\x8c\x24", 3) + && fnsize > 7) + { + // This is loading an offset from the stack pointer for a + // comparison. The offset is negative, so we decrease the + // offset by the amount of space we need for the stack. This + // means we will avoid calling __morestack if there happens to + // be plenty of space on the stack already. + unsigned char* pval = view + fnoffset + 3; + uint32_t val = elfcpp::Swap_unaligned<32, false>::readval(pval); + val -= parameters->options().split_stack_adjust_size(); + elfcpp::Swap_unaligned<32, false>::writeval(pval, val); + } + else + { + if (!object->has_no_split_stack()) + object->error(_("failed to match split-stack sequence at " + "section %u offset %0zx"), + shndx, fnoffset); + return; + } + + // We have to change the function so that it calls + // __morestack_non_split instead of __morestack. The former will + // allocate additional stack space. + *from = "__morestack"; + *to = "__morestack_non_split"; +} + // The selector for i386 object files. class Target_selector_i386 : public Target_selector_freebsd diff --git a/gold/object.cc b/gold/object.cc index 4d892c8..5ac7878 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -231,6 +231,25 @@ Object::handle_gnu_warning_section(const char* name, unsigned int shndx, return false; } +// If NAME is the name of the special section which indicates that +// this object was compiled with -fstack-split, mark it accordingly. + +bool +Object::handle_split_stack_section(const char* name) +{ + if (strcmp(name, ".note.GNU-split-stack") == 0) + { + this->uses_split_stack_ = true; + return true; + } + if (strcmp(name, ".note.GNU-no-split-stack") == 0) + { + this->has_no_split_stack_ = true; + return true; + } + return false; +} + // Class Relobj // To copy the symbols data read from the file to a local data structure. @@ -1109,6 +1128,16 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab, omit[i] = true; } + // The .note.GNU-split-stack section is also special. It + // indicates that the object was compiled with + // -fsplit-stack. + if (this->handle_split_stack_section(name)) + { + if (!parameters->options().relocatable() + && !parameters->options().shared()) + omit[i] = true; + } + bool discard = omit[i]; if (!discard) { diff --git a/gold/object.h b/gold/object.h index e7d05c7..bbc1a10 100644 --- a/gold/object.h +++ b/gold/object.h @@ -195,7 +195,8 @@ class Object Object(const std::string& name, Input_file* input_file, bool is_dynamic, off_t offset = 0) : name_(name), input_file_(input_file), offset_(offset), shnum_(-1U), - is_dynamic_(is_dynamic), xindex_(NULL), no_export_(false) + is_dynamic_(is_dynamic), uses_split_stack_(false), + has_no_split_stack_(false), xindex_(NULL), no_export_(false) { input_file->file().add_object(); } virtual ~Object() @@ -216,6 +217,17 @@ class Object is_dynamic() const { return this->is_dynamic_; } + // Return whether this object was compiled with -fsplit-stack. + bool + uses_split_stack() const + { return this->uses_split_stack_; } + + // Return whether this object contains any functions compiled with + // the no_split_stack attribute. + bool + has_no_split_stack() const + { return this->has_no_split_stack_; } + // Returns NULL for Objects that are not plugin objects. This method // is overridden in the Pluginobj class. Pluginobj* @@ -556,6 +568,12 @@ class Object handle_gnu_warning_section(const char* name, unsigned int shndx, Symbol_table*); + // If NAME is the name of the special section which indicates that + // this object was compiled with -fstack-split, mark it accordingly, + // and return true. Otherwise return false. + bool + handle_split_stack_section(const char* name); + private: // This class may not be copied. Object(const Object&); @@ -572,6 +590,11 @@ class Object unsigned int shnum_; // Whether this is a dynamic object. bool is_dynamic_; + // Whether this object was compiled with -fsplit-stack. + bool uses_split_stack_; + // Whether this object contains any functions compiled with the + // no_split_stack attribute. + bool has_no_split_stack_; // Many sections for objects with more than SHN_LORESERVE sections. Xindex* xindex_; // True if exclude this object from automatic symbol export. @@ -1252,6 +1275,30 @@ class Got_offset_list Got_offset_list* got_next_; }; +// This type is used to modify relocations for -fsplit-stack. It is +// indexed by relocation index, and means that the relocation at that +// index should use the symbol from the vector, rather than the one +// indicated by the relocation. + +class Reloc_symbol_changes +{ + public: + Reloc_symbol_changes(size_t count) + : vec_(count, NULL) + { } + + void + set(size_t i, Symbol* sym) + { this->vec_[i] = sym; } + + const Symbol* + operator[](size_t i) const + { return this->vec_[i]; } + + private: + std::vector<Symbol*> vec_; +}; + // A regular object file. This is size and endian specific. template<int size, bool big_endian> @@ -1671,6 +1718,30 @@ class Sized_relobj : public Relobj unsigned char* reloc_view, section_size_type reloc_view_size); + // A type shared by split_stack_adjust_reltype and find_functions. + typedef std::map<section_offset_type, section_size_type> Function_offsets; + + // Check for -fsplit-stack routines calling non-split-stack routines. + void + split_stack_adjust(const Symbol_table*, const unsigned char* pshdrs, + unsigned int sh_type, unsigned int shndx, + const unsigned char* prelocs, size_t reloc_count, + unsigned char* view, section_size_type view_size, + Reloc_symbol_changes** reloc_map); + + template<int sh_type> + void + split_stack_adjust_reltype(const Symbol_table*, const unsigned char* pshdrs, + unsigned int shndx, const unsigned char* prelocs, + size_t reloc_count, unsigned char* view, + section_size_type view_size, + Reloc_symbol_changes** reloc_map); + + // Find all functions in a section. + void + find_functions(const unsigned char* pshdrs, unsigned int shndx, + Function_offsets*); + // Initialize input to output maps for section symbols in merged // sections. void diff --git a/gold/options.h b/gold/options.h index 24f52fa..f590630 100644 --- a/gold/options.h +++ b/gold/options.h @@ -812,6 +812,10 @@ class General_options DEFINE_bool(Bshareable, options::ONE_DASH, '\0', false, N_("Generate shared library"), NULL); + DEFINE_uint(split_stack_adjust_size, options::TWO_DASHES, '\0', 0x4000, + N_("Stack size when -fsplit-stack function calls non-split"), + N_("SIZE")); + // This is not actually special in any way, but I need to give it // a non-standard accessor-function name because 'static' is a keyword. DEFINE_special(static, options::ONE_DASH, '\0', diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 05d627b..637cbbd 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -110,7 +110,8 @@ class Target_powerpc : public Sized_target<size, big_endian> bool needs_special_offset_handling, unsigned char* view, typename elfcpp::Elf_types<size>::Elf_Addr view_address, - section_size_type view_size); + section_size_type view_size, + const Reloc_symbol_changes*); // Scan the relocs during a relocatable link. void @@ -1854,7 +1855,8 @@ Target_powerpc<size, big_endian>::relocate_section( bool needs_special_offset_handling, unsigned char* view, typename elfcpp::Elf_types<size>::Elf_Addr address, - section_size_type view_size) + section_size_type view_size, + const Reloc_symbol_changes* reloc_symbol_changes) { typedef Target_powerpc<size, big_endian> Powerpc; typedef typename Target_powerpc<size, big_endian>::Relocate Powerpc_relocate; @@ -1871,7 +1873,8 @@ Target_powerpc<size, big_endian>::relocate_section( needs_special_offset_handling, view, address, - view_size); + view_size, + reloc_symbol_changes); } // Return the size of a relocation while scanning during a relocatable diff --git a/gold/reloc.cc b/gold/reloc.cc index bfa2f1c..7fb5cea 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -888,39 +888,36 @@ Sized_relobj<size, big_endian>::relocate_sections( relinfo.reloc_shndx = i; relinfo.data_shndx = index; + unsigned char* view = (*pviews)[index].view; + Address address = (*pviews)[index].address; + section_size_type view_size = (*pviews)[index].view_size; + + Reloc_symbol_changes* reloc_map = NULL; + if (this->uses_split_stack() && output_offset != invalid_address) + { + typename This::Shdr data_shdr(pshdrs + index * This::shdr_size); + if ((data_shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0) + this->split_stack_adjust(symtab, pshdrs, sh_type, index, + prelocs, reloc_count, view, view_size, + &reloc_map); + } + if (!parameters->options().relocatable()) { - target->relocate_section(&relinfo, - sh_type, - prelocs, - reloc_count, - os, + target->relocate_section(&relinfo, sh_type, prelocs, reloc_count, os, output_offset == invalid_address, - (*pviews)[index].view, - (*pviews)[index].address, - (*pviews)[index].view_size); + view, address, view_size, reloc_map); if (parameters->options().emit_relocs()) this->emit_relocs(&relinfo, i, sh_type, prelocs, reloc_count, - os, output_offset, - (*pviews)[index].view, - (*pviews)[index].address, - (*pviews)[index].view_size, - (*pviews)[i].view, - (*pviews)[i].view_size); + os, output_offset, view, address, view_size, + (*pviews)[i].view, (*pviews)[i].view_size); } else { Relocatable_relocs* rr = this->relocatable_relocs(i); - target->relocate_for_relocatable(&relinfo, - sh_type, - prelocs, - reloc_count, - os, - output_offset, - rr, - (*pviews)[index].view, - (*pviews)[index].address, - (*pviews)[index].view_size, + target->relocate_for_relocatable(&relinfo, sh_type, prelocs, + reloc_count, os, output_offset, rr, + view, address, view_size, (*pviews)[i].view, (*pviews)[i].view_size); } @@ -1025,6 +1022,244 @@ Sized_relobj<size, big_endian>::free_input_to_output_maps() } } +// If an object was compiled with -fsplit-stack, this is called to +// check whether any relocations refer to functions defined in objects +// which were not compiled with -fsplit-stack. If they were, then we +// need to apply some target-specific adjustments to request +// additional stack space. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::split_stack_adjust( + const Symbol_table* symtab, + const unsigned char* pshdrs, + unsigned int sh_type, + unsigned int shndx, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + section_size_type view_size, + Reloc_symbol_changes** reloc_map) +{ + if (sh_type == elfcpp::SHT_REL) + this->split_stack_adjust_reltype<elfcpp::SHT_REL>(symtab, pshdrs, shndx, + prelocs, reloc_count, + view, view_size, + reloc_map); + else + { + gold_assert(sh_type == elfcpp::SHT_RELA); + this->split_stack_adjust_reltype<elfcpp::SHT_RELA>(symtab, pshdrs, shndx, + prelocs, reloc_count, + view, view_size, + reloc_map); + } +} + +// Adjust for -fsplit-stack, templatized on the type of the relocation +// section. + +template<int size, bool big_endian> +template<int sh_type> +void +Sized_relobj<size, big_endian>::split_stack_adjust_reltype( + const Symbol_table* symtab, + const unsigned char* pshdrs, + unsigned int shndx, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + section_size_type view_size, + Reloc_symbol_changes** reloc_map) +{ + typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype; + const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size; + + size_t local_count = this->local_symbol_count(); + + std::vector<section_offset_type> non_split_refs; + + const unsigned char* pr = prelocs; + for (size_t i = 0; i < reloc_count; ++i, pr += reloc_size) + { + Reltype reloc(pr); + + typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info(); + unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); + if (r_sym < local_count) + continue; + + const Symbol* gsym = this->global_symbol(r_sym); + gold_assert(gsym != NULL); + if (gsym->is_forwarder()) + gsym = symtab->resolve_forwards(gsym); + + // See if this relocation refers to a function defined in an + // object compiled without -fsplit-stack. Note that we don't + // care about the type of relocation--this means that in some + // cases we will ask for a large stack unnecessarily, but this + // is not fatal. FIXME: Some targets have symbols which are + // functions but are not type STT_FUNC, e.g., STT_ARM_TFUNC. + if (gsym->type() == elfcpp::STT_FUNC + && !gsym->is_undefined() + && gsym->source() == Symbol::FROM_OBJECT + && !gsym->object()->uses_split_stack()) + { + section_offset_type offset = + convert_to_section_size_type(reloc.get_r_offset()); + non_split_refs.push_back(offset); + } + } + + if (non_split_refs.empty()) + return; + + // At this point, every entry in NON_SPLIT_REFS indicates a + // relocation which refers to a function in an object compiled + // without -fsplit-stack. We now have to convert that list into a + // set of offsets to functions. First, we find all the functions. + + Function_offsets function_offsets; + this->find_functions(pshdrs, shndx, &function_offsets); + if (function_offsets.empty()) + return; + + // Now get a list of the function with references to non split-stack + // code. + + Function_offsets calls_non_split; + for (std::vector<section_offset_type>::const_iterator p + = non_split_refs.begin(); + p != non_split_refs.end(); + ++p) + { + Function_offsets::const_iterator low = function_offsets.lower_bound(*p); + if (low == function_offsets.end()) + --low; + else if (low->first == *p) + ; + else if (low == function_offsets.begin()) + continue; + else + --low; + + calls_non_split.insert(*low); + } + if (calls_non_split.empty()) + return; + + // Now we have a set of functions to adjust. The adjustments are + // target specific. Besides changing the output section view + // however, it likes, the target may request a relocation change + // from one global symbol name to another. + + for (Function_offsets::const_iterator p = calls_non_split.begin(); + p != calls_non_split.end(); + ++p) + { + std::string from; + std::string to; + parameters->target().calls_non_split(this, shndx, p->first, p->second, + view, view_size, &from, &to); + if (!from.empty()) + { + gold_assert(!to.empty()); + Symbol* tosym = NULL; + + // Find relocations in the relevant function which are for + // FROM. + pr = prelocs; + for (size_t i = 0; i < reloc_count; ++i, pr += reloc_size) + { + Reltype reloc(pr); + + typename elfcpp::Elf_types<size>::Elf_WXword r_info = + reloc.get_r_info(); + unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); + if (r_sym < local_count) + continue; + + section_offset_type offset = + convert_to_section_size_type(reloc.get_r_offset()); + if (offset < p->first + || (offset + >= (p->first + + static_cast<section_offset_type>(p->second)))) + continue; + + const Symbol* gsym = this->global_symbol(r_sym); + if (from == gsym->name()) + { + if (tosym == NULL) + { + tosym = symtab->lookup(to.c_str()); + if (tosym == NULL) + { + this->error(_("could not convert call " + "to '%s' to '%s'"), + from.c_str(), to.c_str()); + break; + } + } + + if (*reloc_map == NULL) + *reloc_map = new Reloc_symbol_changes(reloc_count); + (*reloc_map)->set(i, tosym); + } + } + } + } +} + +// Find all the function in this object defined in section SHNDX. +// Store their offsets in the section in FUNCTION_OFFSETS. + +template<int size, bool big_endian> +void +Sized_relobj<size, big_endian>::find_functions( + const unsigned char* pshdrs, + unsigned int shndx, + Sized_relobj<size, big_endian>::Function_offsets* function_offsets) +{ + // We need to read the symbols to find the functions. If we wanted + // to, we could cache reading the symbols across all sections in the + // object. + const unsigned int symtab_shndx = this->symtab_shndx_; + typename This::Shdr symtabshdr(pshdrs + symtab_shndx * This::shdr_size); + gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); + + typename elfcpp::Elf_types<size>::Elf_WXword sh_size = + symtabshdr.get_sh_size(); + const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(), + sh_size, true, true); + + const int sym_size = This::sym_size; + const unsigned int symcount = sh_size / sym_size; + for (unsigned int i = 0; i < symcount; ++i, psyms += sym_size) + { + typename elfcpp::Sym<size, big_endian> isym(psyms); + + // FIXME: Some targets can have functions which do not have type + // STT_FUNC, e.g., STT_ARM_TFUNC. + if (isym.get_st_type() != elfcpp::STT_FUNC + || isym.get_st_size() == 0) + continue; + + bool is_ordinary; + unsigned int sym_shndx = this->adjust_sym_shndx(i, isym.get_st_shndx(), + &is_ordinary); + if (!is_ordinary || sym_shndx != shndx) + continue; + + section_offset_type value = + convert_to_section_size_type(isym.get_st_value()); + section_size_type fnsize = + convert_to_section_size_type(isym.get_st_size()); + + (*function_offsets)[value] = fnsize; + } +} + // Class Merged_symbol_value. template<int size> diff --git a/gold/sparc.cc b/gold/sparc.cc index 16a6ffc..3d31dd6 100644 --- a/gold/sparc.cc +++ b/gold/sparc.cc @@ -112,7 +112,8 @@ class Target_sparc : public Sized_target<size, big_endian> bool needs_special_offset_handling, unsigned char* view, typename elfcpp::Elf_types<size>::Elf_Addr view_address, - section_size_type view_size); + section_size_type view_size, + const Reloc_symbol_changes*); // Scan the relocs during a relocatable link. void @@ -3106,7 +3107,8 @@ Target_sparc<size, big_endian>::relocate_section( bool needs_special_offset_handling, unsigned char* view, typename elfcpp::Elf_types<size>::Elf_Addr address, - section_size_type view_size) + section_size_type view_size, + const Reloc_symbol_changes* reloc_symbol_changes) { typedef Target_sparc<size, big_endian> Sparc; typedef typename Target_sparc<size, big_endian>::Relocate Sparc_relocate; @@ -3123,7 +3125,8 @@ Target_sparc<size, big_endian>::relocate_section( needs_special_offset_handling, view, address, - view_size); + view_size, + reloc_symbol_changes); } // Return the size of a relocation while scanning during a relocatable diff --git a/gold/target-reloc.h b/gold/target-reloc.h index d93e755..bc00123 100644 --- a/gold/target-reloc.h +++ b/gold/target-reloc.h @@ -25,6 +25,7 @@ #include "elfcpp.h" #include "symtab.h" +#include "object.h" #include "reloc.h" #include "reloc-types.h" @@ -163,6 +164,12 @@ get_comdat_behavior(const char* name) // NEEDS_SPECIAL_OFFSET_HANDLING is true, in which case they refer to // the output section. +// RELOC_SYMBOL_CHANGES is used for -fsplit-stack support. If it is +// not NULL, it is a vector indexed by relocation index. If that +// entry is not NULL, it points to a global symbol which used as the +// symbol for the relocation, ignoring the symbol index in the +// relocation. + template<int size, bool big_endian, typename Target_type, int sh_type, typename Relocate> inline void @@ -175,7 +182,8 @@ relocate_section( bool needs_special_offset_handling, unsigned char* view, typename elfcpp::Elf_types<size>::Elf_Addr view_address, - section_size_type view_size) + section_size_type view_size, + const Reloc_symbol_changes* reloc_symbol_changes) { typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype; const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size; @@ -210,7 +218,9 @@ relocate_section( Symbol_value<size> symval; const Symbol_value<size> *psymval; - if (r_sym < local_count) + if (r_sym < local_count + && (reloc_symbol_changes == NULL + || (*reloc_symbol_changes)[i] == NULL)) { sym = NULL; psymval = object->local_symbol(r_sym); @@ -246,7 +256,7 @@ relocate_section( { if (comdat_behavior == CB_WARNING) gold_warning_at_location(relinfo, i, offset, - _("Relocation refers to discarded " + _("relocation refers to discarded " "comdat section")); symval.set_output_value(0); } @@ -256,10 +266,17 @@ relocate_section( } else { - const Symbol* gsym = object->global_symbol(r_sym); - gold_assert(gsym != NULL); - if (gsym->is_forwarder()) - gsym = relinfo->symtab->resolve_forwards(gsym); + const Symbol* gsym; + if (reloc_symbol_changes != NULL + && (*reloc_symbol_changes)[i] != NULL) + gsym = (*reloc_symbol_changes)[i]; + else + { + gsym = object->global_symbol(r_sym); + gold_assert(gsym != NULL); + if (gsym->is_forwarder()) + gsym = relinfo->symtab->resolve_forwards(gsym); + } sym = static_cast<const Sized_symbol<size>*>(gsym); if (sym->has_symtab_index()) diff --git a/gold/target.cc b/gold/target.cc index d971cff..b129b59 100644 --- a/gold/target.cc +++ b/gold/target.cc @@ -23,6 +23,7 @@ #include "gold.h" #include "target.h" #include "dynobj.h" +#include "elfcpp.h" namespace gold { @@ -135,4 +136,50 @@ Target::do_make_elf_object(const std::string& name, Input_file* input_file, } #endif +// Default conversion for -fsplit-stack is to give an error. + +void +Target::do_calls_non_split(Relobj* object, unsigned int, section_offset_type, + section_size_type, unsigned char*, section_size_type, + std::string*, std::string*) const +{ + static bool warned; + if (!warned) + { + gold_error(_("linker does not include stack split support " + "required by %s"), + object->name().c_str()); + warned = true; + } +} + +// Return whether BYTES/LEN matches VIEW/VIEW_SIZE at OFFSET. + +bool +Target::match_view(const unsigned char* view, section_size_type view_size, + section_offset_type offset, const char* bytes, + size_t len) const +{ + if (offset + len > view_size) + return false; + return memcmp(view + offset, bytes, len) == 0; +} + +// Set the contents of a VIEW/VIEW_SIZE to nops starting at OFFSET +// for LEN bytes. + +void +Target::set_view_to_nop(unsigned char* view, section_size_type view_size, + section_offset_type offset, size_t len) const +{ + gold_assert(offset >= 0 && offset + len <= view_size); + if (!this->has_code_fill()) + memset(view + offset, 0, len); + else + { + std::string fill = this->code_fill(len); + memcpy(view + offset, fill.data(), len); + } +} + } // End namespace gold. diff --git a/gold/target.h b/gold/target.h index ac59fb2..68d89dc 100644 --- a/gold/target.h +++ b/gold/target.h @@ -43,11 +43,13 @@ namespace gold class General_options; class Object; +class Relobj; template<int size, bool big_endian> class Sized_relobj; class Relocatable_relocs; template<int size, bool big_endian> class Relocate_info; +class Reloc_symbol_changes; class Symbol; template<int size> class Sized_symbol; @@ -217,6 +219,22 @@ class Target is_local_label_name(const char* name) const { return this->do_is_local_label_name(name); } + // A function starts at OFFSET in section SHNDX in OBJECT. That + // function was compiled with -fsplit-stack, but it refers to a + // function which was compiled without -fsplit-stack. VIEW is a + // modifiable view of the section; VIEW_SIZE is the size of the + // view. The target has to adjust the function so that it allocates + // enough stack. + void + calls_non_split(Relobj* object, unsigned int shndx, + section_offset_type fnoffset, section_size_type fnsize, + unsigned char* view, section_size_type view_size, + std::string* from, std::string* to) const + { + this->do_calls_non_split(object, shndx, fnoffset, fnsize, view, view_size, + from, to); + } + // Make an ELF object. template<int size, bool big_endian> Object* @@ -244,7 +262,7 @@ class Target return pass < 2; return this->do_relax(pass); - } + } protected: // This struct holds the constant information for a child class. We @@ -331,9 +349,15 @@ class Target virtual bool do_is_local_label_name(const char*) const; + // Virtual function which may be overridden by the child class. + virtual void + do_calls_non_split(Relobj* object, unsigned int, section_offset_type, + section_size_type, unsigned char*, section_size_type, + std::string*, std::string*) const; + // make_elf_object hooks. There are four versions of these for // different address sizes and endianities. - + #ifdef HAVE_TARGET_32_LITTLE // Virtual functions which may be overriden by the child class. virtual Object* @@ -372,6 +396,18 @@ class Target do_relax(int) { return false; } + // A function for targets to call. Return whether BYTES/LEN matches + // VIEW/VIEW_SIZE at OFFSET. + bool + match_view(const unsigned char* view, section_size_type view_size, + section_offset_type offset, const char* bytes, size_t len) const; + + // Set the contents of a VIEW/VIEW_SIZE to nops starting at OFFSET + // for LEN bytes. + void + set_view_to_nop(unsigned char* view, section_size_type view_size, + section_offset_type offset, size_t len) const; + private: // The implementations of the four do_make_elf_object virtual functions are // almost identical except for their sizes and endianity. We use a template. @@ -478,7 +514,8 @@ class Sized_target : public Target bool needs_special_offset_handling, unsigned char* view, typename elfcpp::Elf_types<size>::Elf_Addr view_address, - section_size_type view_size) = 0; + section_size_type view_size, + const Reloc_symbol_changes*) = 0; // Scan the relocs during a relocatable link. The parameters are // like scan_relocs, with an additional Relocatable_relocs diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index 25740b8..6d40a5d 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -25,6 +25,7 @@ TEST_CXXFILT = $(top_builddir)/../binutils/cxxfilt TEST_STRIP = $(top_builddir)/../binutils/strip-new TEST_AR = $(top_builddir)/../binutils/ar TEST_NM = $(top_builddir)/../binutils/nm-new +TEST_AS = $(top_builddir)/../gas/as-new if PLUGINS LIBDL = -ldl @@ -1182,3 +1183,79 @@ permission_test: basic_test.o gcctestdir/ld endif GCC endif NATIVE_LINKER + +# These tests work with cross linkers. + +if DEFAULT_TARGET_I386 + +check_SCRIPTS += split_i386.sh +check_DATA += split_i386_1.stdout split_i386_2.stdout \ + split_i386_3.stdout split_i386_4.stdout split_i386_r.stdout +SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200 +split_i386_1.o: split_i386_1.s + $(TEST_AS) -o $@ $< +split_i386_2.o: split_i386_2.s + $(TEST_AS) -o $@ $< +split_i386_3.o: split_i386_3.s + $(TEST_AS) -o $@ $< +split_i386_4.o: split_i386_4.s + $(TEST_AS) -o $@ $< +split_i386_n.o: split_i386_n.s + $(TEST_AS) -o $@ $< +split_i386_1: split_i386_1.o split_i386_n.o ../ld-new + ../ld-new $(SPLIT_DEFSYMS) -o $@ split_i386_1.o split_i386_n.o +split_i386_1.stdout: split_i386_1 + $(TEST_OBJDUMP) -d $< > $@ +split_i386_2: split_i386_2.o split_i386_n.o ../ld-new + ../ld-new $(SPLIT_DEFSYMS) -o $@ split_i386_2.o split_i386_n.o +split_i386_2.stdout: split_i386_2 + $(TEST_OBJDUMP) -d $< > $@ +split_i386_3.stdout: split_i386_3.o split_i386_n.o ../ld-new + ../ld-new $(SPLIT_DEFSYMS) -o split_i386_3 split_i386_3.o split_i386_n.o > $@ 2>&1 || exit 0 +split_i386_4: split_i386_4.o split_i386_n.o ../ld-new + ../ld-new $(SPLIT_DEFSYMS) -o $@ split_i386_4.o split_i386_n.o +split_i386_4.stdout: split_i386_4 + $(TEST_OBJDUMP) -d $< > $@ +split_i386_r.stdout: split_i386_1.o split_i386_n.o ../ld-new + ../ld-new -r split_i386_1.o split_i386_n.o -o split_i386_r > $@ 2>&1 || exit 0 +MOSTLYCLEANFILES += split_i386_1 split_i386_2 split_i386_3 \ + split_i386_4 split_i386_r + +endif DEFAULT_TARGET_I386 + +if DEFAULT_TARGET_X86_64 + +check_SCRIPTS += split_x86_64.sh +check_DATA += split_x86_64_1.stdout split_x86_64_2.stdout \ + split_x86_64_3.stdout split_x86_64_4.stdout split_x86_64_r.stdout +SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200 +split_x86_64_1.o: split_x86_64_1.s + $(TEST_AS) -o $@ $< +split_x86_64_2.o: split_x86_64_2.s + $(TEST_AS) -o $@ $< +split_x86_64_3.o: split_x86_64_3.s + $(TEST_AS) -o $@ $< +split_x86_64_4.o: split_x86_64_4.s + $(TEST_AS) -o $@ $< +split_x86_64_n.o: split_x86_64_n.s + $(TEST_AS) -o $@ $< +split_x86_64_1: split_x86_64_1.o split_x86_64_n.o ../ld-new + ../ld-new $(SPLIT_DEFSYMS) -o $@ split_x86_64_1.o split_x86_64_n.o +split_x86_64_1.stdout: split_x86_64_1 + $(TEST_OBJDUMP) -d $< > $@ +split_x86_64_2: split_x86_64_2.o split_x86_64_n.o ../ld-new + ../ld-new $(SPLIT_DEFSYMS) -o $@ split_x86_64_2.o split_x86_64_n.o +split_x86_64_2.stdout: split_x86_64_2 + $(TEST_OBJDUMP) -d $< > $@ +split_x86_64_3.stdout: split_x86_64_3.o split_x86_64_n.o ../ld-new + ../ld-new $(SPLIT_DEFSYMS) -o split_x86_64_3 split_x86_64_3.o split_x86_64_n.o > $@ 2>&1 || exit 0 +split_x86_64_4: split_x86_64_4.o split_x86_64_n.o ../ld-new + ../ld-new $(SPLIT_DEFSYMS) -o $@ split_x86_64_4.o split_x86_64_n.o +split_x86_64_4.stdout: split_x86_64_4 + $(TEST_OBJDUMP) -d $< > $@ +split_x86_64_r.stdout: split_x86_64_1.o split_x86_64_n.o ../ld-new + ../ld-new -r split_x86_64_1.o split_x86_64_n.o -o split_x86_64_r > $@ 2>&1 || exit 0 +MOSTLYCLEANFILES += split_x86_64_1 split_x86_64_2 split_x86_64_3 \ + split_x86_64_4 split_x86_64_r + +endif DEFAULT_TARGET_X86_64 diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index 36334ef..f047214 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -378,6 +378,22 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ # Test that if the output file already exists and is empty, # it will get execute permission. @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_31 = permission_test + +# These tests work with cross linkers. +@DEFAULT_TARGET_I386_TRUE@am__append_32 = split_i386.sh +@DEFAULT_TARGET_I386_TRUE@am__append_33 = split_i386_1.stdout split_i386_2.stdout \ +@DEFAULT_TARGET_I386_TRUE@ split_i386_3.stdout split_i386_4.stdout split_i386_r.stdout + +@DEFAULT_TARGET_I386_TRUE@am__append_34 = split_i386_1 split_i386_2 split_i386_3 \ +@DEFAULT_TARGET_I386_TRUE@ split_i386_4 split_i386_r + +@DEFAULT_TARGET_X86_64_TRUE@am__append_35 = split_x86_64.sh +@DEFAULT_TARGET_X86_64_TRUE@am__append_36 = split_x86_64_1.stdout split_x86_64_2.stdout \ +@DEFAULT_TARGET_X86_64_TRUE@ split_x86_64_3.stdout split_x86_64_4.stdout split_x86_64_r.stdout + +@DEFAULT_TARGET_X86_64_TRUE@am__append_37 = split_x86_64_1 split_x86_64_2 split_x86_64_3 \ +@DEFAULT_TARGET_X86_64_TRUE@ split_x86_64_4 split_x86_64_r + subdir = testsuite DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -1389,6 +1405,7 @@ TEST_CXXFILT = $(top_builddir)/../binutils/cxxfilt TEST_STRIP = $(top_builddir)/../binutils/strip-new TEST_AR = $(top_builddir)/../binutils/ar TEST_NM = $(top_builddir)/../binutils/nm-new +TEST_AS = $(top_builddir)/../gas/as-new @PLUGINS_TRUE@LIBDL = -ldl @THREADS_TRUE@THREADSLIB = -lpthread @OMP_SUPPORT_TRUE@TLS_TEST_C_CFLAGS = -fopenmp @@ -1399,13 +1416,15 @@ TEST_NM = $(top_builddir)/../binutils/nm-new # the right choice for files 'make' builds that people rebuild. MOSTLYCLEANFILES = *.so *.syms *.stdout $(am__append_3) \ $(am__append_8) $(am__append_17) $(am__append_25) \ - $(am__append_29) + $(am__append_29) $(am__append_34) $(am__append_37) # We will add to these later, for each individual test. Note # that we add each test under check_SCRIPTS or check_PROGRAMS; # the TESTS variable is automatically populated from these. -check_SCRIPTS = $(am__append_1) $(am__append_23) $(am__append_27) -check_DATA = $(am__append_2) $(am__append_24) $(am__append_28) +check_SCRIPTS = $(am__append_1) $(am__append_23) $(am__append_27) \ + $(am__append_32) $(am__append_35) +check_DATA = $(am__append_2) $(am__append_24) $(am__append_28) \ + $(am__append_33) $(am__append_36) BUILT_SOURCES = $(am__append_16) TESTS = $(check_SCRIPTS) $(check_PROGRAMS) @@ -1749,6 +1768,8 @@ binary_unittest_SOURCES = binary_unittest.cc @GCC_TRUE@@MCMODEL_MEDIUM_TRUE@@NATIVE_LINKER_TRUE@large_CFLAGS = -mcmodel=medium @GCC_TRUE@@MCMODEL_MEDIUM_TRUE@@NATIVE_LINKER_TRUE@large_DEPENDENCIES = gcctestdir/ld @GCC_TRUE@@MCMODEL_MEDIUM_TRUE@@NATIVE_LINKER_TRUE@large_LDFLAGS = -Bgcctestdir/ +@DEFAULT_TARGET_I386_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200 +@DEFAULT_TARGET_X86_64_TRUE@SPLIT_DEFSYMS = --defsym __morestack=0x100 --defsym __morestack_non_split=0x200 all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am @@ -2979,6 +3000,58 @@ uninstall-am: @GCC_TRUE@@NATIVE_LINKER_TRUE@ touch $@; \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ chmod 600 $@; \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ basic_test.o +@DEFAULT_TARGET_I386_TRUE@split_i386_1.o: split_i386_1.s +@DEFAULT_TARGET_I386_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_I386_TRUE@split_i386_2.o: split_i386_2.s +@DEFAULT_TARGET_I386_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_I386_TRUE@split_i386_3.o: split_i386_3.s +@DEFAULT_TARGET_I386_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_I386_TRUE@split_i386_4.o: split_i386_4.s +@DEFAULT_TARGET_I386_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_I386_TRUE@split_i386_n.o: split_i386_n.s +@DEFAULT_TARGET_I386_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_I386_TRUE@split_i386_1: split_i386_1.o split_i386_n.o ../ld-new +@DEFAULT_TARGET_I386_TRUE@ ../ld-new $(SPLIT_DEFSYMS) -o $@ split_i386_1.o split_i386_n.o +@DEFAULT_TARGET_I386_TRUE@split_i386_1.stdout: split_i386_1 +@DEFAULT_TARGET_I386_TRUE@ $(TEST_OBJDUMP) -d $< > $@ +@DEFAULT_TARGET_I386_TRUE@split_i386_2: split_i386_2.o split_i386_n.o ../ld-new +@DEFAULT_TARGET_I386_TRUE@ ../ld-new $(SPLIT_DEFSYMS) -o $@ split_i386_2.o split_i386_n.o +@DEFAULT_TARGET_I386_TRUE@split_i386_2.stdout: split_i386_2 +@DEFAULT_TARGET_I386_TRUE@ $(TEST_OBJDUMP) -d $< > $@ +@DEFAULT_TARGET_I386_TRUE@split_i386_3.stdout: split_i386_3.o split_i386_n.o ../ld-new +@DEFAULT_TARGET_I386_TRUE@ ../ld-new $(SPLIT_DEFSYMS) -o split_i386_3 split_i386_3.o split_i386_n.o > $@ 2>&1 || exit 0 +@DEFAULT_TARGET_I386_TRUE@split_i386_4: split_i386_4.o split_i386_n.o ../ld-new +@DEFAULT_TARGET_I386_TRUE@ ../ld-new $(SPLIT_DEFSYMS) -o $@ split_i386_4.o split_i386_n.o +@DEFAULT_TARGET_I386_TRUE@split_i386_4.stdout: split_i386_4 +@DEFAULT_TARGET_I386_TRUE@ $(TEST_OBJDUMP) -d $< > $@ +@DEFAULT_TARGET_I386_TRUE@split_i386_r.stdout: split_i386_1.o split_i386_n.o ../ld-new +@DEFAULT_TARGET_I386_TRUE@ ../ld-new -r split_i386_1.o split_i386_n.o -o split_i386_r > $@ 2>&1 || exit 0 +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_1.o: split_x86_64_1.s +@DEFAULT_TARGET_X86_64_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_2.o: split_x86_64_2.s +@DEFAULT_TARGET_X86_64_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_3.o: split_x86_64_3.s +@DEFAULT_TARGET_X86_64_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_4.o: split_x86_64_4.s +@DEFAULT_TARGET_X86_64_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_n.o: split_x86_64_n.s +@DEFAULT_TARGET_X86_64_TRUE@ $(TEST_AS) -o $@ $< +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_1: split_x86_64_1.o split_x86_64_n.o ../ld-new +@DEFAULT_TARGET_X86_64_TRUE@ ../ld-new $(SPLIT_DEFSYMS) -o $@ split_x86_64_1.o split_x86_64_n.o +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_1.stdout: split_x86_64_1 +@DEFAULT_TARGET_X86_64_TRUE@ $(TEST_OBJDUMP) -d $< > $@ +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_2: split_x86_64_2.o split_x86_64_n.o ../ld-new +@DEFAULT_TARGET_X86_64_TRUE@ ../ld-new $(SPLIT_DEFSYMS) -o $@ split_x86_64_2.o split_x86_64_n.o +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_2.stdout: split_x86_64_2 +@DEFAULT_TARGET_X86_64_TRUE@ $(TEST_OBJDUMP) -d $< > $@ +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_3.stdout: split_x86_64_3.o split_x86_64_n.o ../ld-new +@DEFAULT_TARGET_X86_64_TRUE@ ../ld-new $(SPLIT_DEFSYMS) -o split_x86_64_3 split_x86_64_3.o split_x86_64_n.o > $@ 2>&1 || exit 0 +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_4: split_x86_64_4.o split_x86_64_n.o ../ld-new +@DEFAULT_TARGET_X86_64_TRUE@ ../ld-new $(SPLIT_DEFSYMS) -o $@ split_x86_64_4.o split_x86_64_n.o +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_4.stdout: split_x86_64_4 +@DEFAULT_TARGET_X86_64_TRUE@ $(TEST_OBJDUMP) -d $< > $@ +@DEFAULT_TARGET_X86_64_TRUE@split_x86_64_r.stdout: split_x86_64_1.o split_x86_64_n.o ../ld-new +@DEFAULT_TARGET_X86_64_TRUE@ ../ld-new -r split_x86_64_1.o split_x86_64_n.o -o split_x86_64_r > $@ 2>&1 || exit 0 # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/gold/testsuite/split_i386.sh b/gold/testsuite/split_i386.sh new file mode 100755 index 0000000..e94fea2 --- /dev/null +++ b/gold/testsuite/split_i386.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +# split_i386.sh -- test -fstack-split for i386 + +# Copyright 2009 Free Software Foundation, Inc. +# Written by Ian Lance Taylor <iant@google.com>. + +# 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. + +match() +{ + if ! egrep "$1" "$2" >/dev/null 2>&1; then + echo 1>&2 "could not find '$1' in $2" + exit 1 + fi +} + +nomatch() +{ + if egrep "$1" "$2" >/dev/null 2>&1; then + echo 1>&2 "found unexpected '$1' in $2" + exit 1 + fi +} + +match 'cmp.*+%gs:[^,]*,%esp' split_i386_1.stdout +match 'call.*__morestack>?$' split_i386_1.stdout +match 'lea.*-0x200\(%esp\),' split_i386_1.stdout + +match 'stc' split_i386_2.stdout +match 'call.*__morestack_non_split>?$' split_i386_2.stdout +nomatch 'call.*__morestack>?$' split_i386_2.stdout +match 'lea.*-0x4200\(%esp\),' split_i386_2.stdout + +match 'failed to match' split_i386_3.stdout + +match 'call.*__morestack>?$' split_i386_4.stdout + +match 'cannot mix' split_i386_r.stdout diff --git a/gold/testsuite/split_i386_1.s b/gold/testsuite/split_i386_1.s new file mode 100644 index 0000000..9ac816e --- /dev/null +++ b/gold/testsuite/split_i386_1.s @@ -0,0 +1,33 @@ +# split_i386_1.s: i386 specific test case for -fsplit-stack. + + .text + + .global fn1 + .type fn1,@function +fn1: + cmp %gs:0x30,%esp + jae 1f + call __morestack + ret +1: + call fn2 + ret + + .size fn1,. - fn1 + + .global fn2 + .type fn2,@function +fn2: + lea -0x200(%esp),%ecx + cmp %gs:0x30,%ecx + jae 1f + call __morestack + ret +1: + call fn1 + ret + + .size fn2,. - fn2 + + .section .note.GNU-stack,"",@progbits + .section .note.GNU-split-stack,"",@progbits diff --git a/gold/testsuite/split_i386_2.s b/gold/testsuite/split_i386_2.s new file mode 100644 index 0000000..b4a2f14 --- /dev/null +++ b/gold/testsuite/split_i386_2.s @@ -0,0 +1,33 @@ +# split_i386_2.s: i386 specific, -fsplit-stack calling non-split + + .text + + .global fn1 + .type fn1,@function +fn1: + cmp %gs:0x30,%esp + jae 1f + call __morestack + ret +1: + call fn3 + ret + + .size fn1,. - fn1 + + .global fn2 + .type fn2,@function +fn2: + lea -0x200(%esp),%ecx + cmp %gs:0x30,%ecx + jae 1f + call __morestack + ret +1: + call fn3 + ret + + .size fn2,. - fn2 + + .section .note.GNU-stack,"",@progbits + .section .note.GNU-split-stack,"",@progbits diff --git a/gold/testsuite/split_i386_3.s b/gold/testsuite/split_i386_3.s new file mode 100644 index 0000000..fdde7a9 --- /dev/null +++ b/gold/testsuite/split_i386_3.s @@ -0,0 +1,22 @@ +# split_i386_3.s: i386 specific, adjustment failure + + .text + + .global fn1 + .type fn1,@function +fn1: + push %ebp + mov %esp,%ebp + cmp %gs:0x30,%esp + jae 1f + call __morestack + ret +1: + call fn3 + leave + ret + + .size fn1,. - fn1 + + .section .note.GNU-stack,"",@progbits + .section .note.GNU-split-stack,"",@progbits diff --git a/gold/testsuite/split_i386_4.s b/gold/testsuite/split_i386_4.s new file mode 100644 index 0000000..be774ae --- /dev/null +++ b/gold/testsuite/split_i386_4.s @@ -0,0 +1,23 @@ +# split_i386_4.s: i386 specific, permitted adjustment failure + + .text + + .global fn1 + .type fn1,@function +fn1: + push %ebp + mov %esp,%ebp + cmp %gs:0x30,%esp + jae 1f + call __morestack + ret +1: + call fn3 + leave + ret + + .size fn1,. - fn1 + + .section .note.GNU-stack,"",@progbits + .section .note.GNU-split-stack,"",@progbits + .section .note.GNU-no-split-stack,"",@progbits diff --git a/gold/testsuite/split_i386_n.s b/gold/testsuite/split_i386_n.s new file mode 100644 index 0000000..4d4e6e8 --- /dev/null +++ b/gold/testsuite/split_i386_n.s @@ -0,0 +1,12 @@ +# split_i386_n.s: i386 specific, -fsplit-stack calling non-split + + .text + + .global fn3 + .type fn3,@function +fn3: + ret + + .size fn3,. - fn3 + + .section .note.GNU-stack,"",@progbits diff --git a/gold/testsuite/split_x86_64.sh b/gold/testsuite/split_x86_64.sh new file mode 100755 index 0000000..61544b2 --- /dev/null +++ b/gold/testsuite/split_x86_64.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +# split_x86_64.sh -- test -fstack-split for x86_64 + +# Copyright 2009 Free Software Foundation, Inc. +# Written by Ian Lance Taylor <iant@google.com>. + +# 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. + +match() +{ + if ! egrep "$1" "$2" >/dev/null 2>&1; then + echo 1>&2 "could not find '$1' in $2" + exit 1 + fi +} + +nomatch() +{ + if egrep "$1" "$2" >/dev/null 2>&1; then + echo 1>&2 "found unexpected '$1' in $2" + exit 1 + fi +} + +match 'cmp.*+%fs:[^,]*,%rsp' split_x86_64_1.stdout +match 'callq.*__morestack>?$' split_x86_64_1.stdout +match 'lea.*-0x200\(%rsp\),' split_x86_64_1.stdout + +match 'stc' split_x86_64_2.stdout +match 'callq.*__morestack_non_split>?$' split_x86_64_2.stdout +nomatch 'callq.*__morestack>?$' split_x86_64_2.stdout +match 'lea.*-0x4200\(%rsp\),' split_x86_64_2.stdout + +match 'failed to match' split_x86_64_3.stdout + +match 'callq.*__morestack>?$' split_x86_64_4.stdout + +match 'cannot mix' split_x86_64_r.stdout diff --git a/gold/testsuite/split_x86_64_1.s b/gold/testsuite/split_x86_64_1.s new file mode 100644 index 0000000..e23ea7f --- /dev/null +++ b/gold/testsuite/split_x86_64_1.s @@ -0,0 +1,33 @@ +# split_x86_64_1.s: x86_64 specific test case for -fsplit-stack. + + .text + + .global fn1 + .type fn1,@function +fn1: + cmp %fs:0x70,%rsp + jae 1f + callq __morestack + retq +1: + callq fn2 + retq + + .size fn1,. - fn1 + + .global fn2 + .type fn2,@function +fn2: + lea -0x200(%rsp),%r10 + cmp %fs:0x70,%r10 + jae 1f + callq __morestack + retq +1: + callq fn1 + retq + + .size fn2,. - fn2 + + .section .note.GNU-stack,"",@progbits + .section .note.GNU-split-stack,"",@progbits diff --git a/gold/testsuite/split_x86_64_2.s b/gold/testsuite/split_x86_64_2.s new file mode 100644 index 0000000..0559bbc --- /dev/null +++ b/gold/testsuite/split_x86_64_2.s @@ -0,0 +1,33 @@ +# split_x86_64_2.s: x86_64 specific, -fsplit-stack calling non-split + + .text + + .global fn1 + .type fn1,@function +fn1: + cmp %fs:0x70,%rsp + jae 1f + callq __morestack + retq +1: + callq fn3 + retq + + .size fn1,. - fn1 + + .global fn2 + .type fn2,@function +fn2: + lea -0x200(%rsp),%r10 + cmp %fs:0x70,%r10 + jae 1f + callq __morestack + retq +1: + callq fn3 + retq + + .size fn2,. - fn2 + + .section .note.GNU-stack,"",@progbits + .section .note.GNU-split-stack,"",@progbits diff --git a/gold/testsuite/split_x86_64_3.s b/gold/testsuite/split_x86_64_3.s new file mode 100644 index 0000000..68ae6e3 --- /dev/null +++ b/gold/testsuite/split_x86_64_3.s @@ -0,0 +1,22 @@ +# split_x86_64_3.s: x86_64 specific, adjustment failure + + .text + + .global fn1 + .type fn1,@function +fn1: + push %rbp + mov %rsp,%rbp + cmp %fs:0x70,%rsp + jae 1f + callq __morestack + retq +1: + callq fn3 + leaveq + retq + + .size fn1,. - fn1 + + .section .note.GNU-stack,"",@progbits + .section .note.GNU-split-stack,"",@progbits diff --git a/gold/testsuite/split_x86_64_4.s b/gold/testsuite/split_x86_64_4.s new file mode 100644 index 0000000..653b917 --- /dev/null +++ b/gold/testsuite/split_x86_64_4.s @@ -0,0 +1,23 @@ +# split_x86_64_4.s: x86_64 specific, permitted adjustment failure + + .text + + .global fn1 + .type fn1,@function +fn1: + push %rbp + mov %rsp,%rbp + cmp %fs:0x70,%rsp + jae 1f + callq __morestack + retq +1: + callq fn3 + leaveq + retq + + .size fn1,. - fn1 + + .section .note.GNU-stack,"",@progbits + .section .note.GNU-split-stack,"",@progbits + .section .note.GNU-no-split-stack,"",@progbits diff --git a/gold/testsuite/split_x86_64_n.s b/gold/testsuite/split_x86_64_n.s new file mode 100644 index 0000000..10436b1 --- /dev/null +++ b/gold/testsuite/split_x86_64_n.s @@ -0,0 +1,12 @@ +# split_x86_64_n.s: x86_64 specific, -fsplit-stack calling non-split + + .text + + .global fn3 + .type fn3,@function +fn3: + retq + + .size fn3,. - fn3 + + .section .note.GNU-stack,"",@progbits diff --git a/gold/testsuite/testfile.cc b/gold/testsuite/testfile.cc index 2532307..d66189a 100644 --- a/gold/testsuite/testfile.cc +++ b/gold/testsuite/testfile.cc @@ -61,7 +61,7 @@ class Target_test : public Sized_target<size, big_endian> relocate_section(const Relocate_info<size, big_endian>*, unsigned int, const unsigned char*, size_t, Output_section*, bool, unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr, - section_size_type) + section_size_type, const Reloc_symbol_changes*) { ERROR("call to Target_test::relocate_section"); } void diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 419746d..46e879d 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -120,7 +120,8 @@ class Target_x86_64 : public Target_freebsd<64, false> bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<64>::Elf_Addr view_address, - section_size_type view_size); + section_size_type view_size, + const Reloc_symbol_changes*); // Scan the relocs during a relocatable link. void @@ -162,6 +163,13 @@ class Target_x86_64 : public Target_freebsd<64, false> do_is_defined_by_abi(const Symbol* sym) const { return strcmp(sym->name(), "__tls_get_addr") == 0; } + // Adjust -fstack-split code which calls non-stack-split code. + void + do_calls_non_split(Relobj* object, unsigned int shndx, + section_offset_type fnoffset, section_size_type fnsize, + unsigned char* view, section_size_type view_size, + std::string* from, std::string* to) const; + // Return the size of the GOT section. section_size_type got_size() @@ -2417,15 +2425,17 @@ Target_x86_64::Relocate::tls_ie_to_le(const Relocate_info<64, false>* relinfo, // Relocate section data. void -Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo, - unsigned int sh_type, - const unsigned char* prelocs, - size_t reloc_count, - Output_section* output_section, - bool needs_special_offset_handling, - unsigned char* view, - elfcpp::Elf_types<64>::Elf_Addr address, - section_size_type view_size) +Target_x86_64::relocate_section( + const Relocate_info<64, false>* relinfo, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + unsigned char* view, + elfcpp::Elf_types<64>::Elf_Addr address, + section_size_type view_size, + const Reloc_symbol_changes* reloc_symbol_changes) { gold_assert(sh_type == elfcpp::SHT_RELA); @@ -2439,7 +2449,8 @@ Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo, needs_special_offset_handling, view, address, - view_size); + view_size, + reloc_symbol_changes); } // Return the size of a relocation while scanning during a relocatable @@ -2657,6 +2668,63 @@ Target_x86_64::do_code_fill(section_size_type length) const return std::string(nops[length], length); } +// FNOFFSET in section SHNDX in OBJECT is the start of a function +// compiled with -fstack-split. The function calls non-stack-split +// code. We have to change the function so that it always ensures +// that it has enough stack space to run some random function. + +void +Target_x86_64::do_calls_non_split(Relobj* object, unsigned int shndx, + section_offset_type fnoffset, + section_size_type fnsize, + unsigned char* view, + section_size_type view_size, + std::string* from, + std::string* to) const +{ + // The function starts with a comparison of the stack pointer and a + // field in the TCB. This is followed by a jump. + + // cmp %fs:NN,%rsp + if (this->match_view(view, view_size, fnoffset, "\x64\x48\x3b\x24\x25", 5) + && fnsize > 9) + { + // We will call __morestack if the carry flag is set after this + // comparison. We turn the comparison into an stc instruction + // and some nops. + view[fnoffset] = '\xf9'; + this->set_view_to_nop(view, view_size, fnoffset + 1, 8); + } + // lea NN(%rsp),%r10 + else if (this->match_view(view, view_size, fnoffset, "\x4c\x8d\x94\x24", 4) + && fnsize > 8) + { + // This is loading an offset from the stack pointer for a + // comparison. The offset is negative, so we decrease the + // offset by the amount of space we need for the stack. This + // means we will avoid calling __morestack if there happens to + // be plenty of space on the stack already. + unsigned char* pval = view + fnoffset + 4; + uint32_t val = elfcpp::Swap_unaligned<32, false>::readval(pval); + val -= parameters->options().split_stack_adjust_size(); + elfcpp::Swap_unaligned<32, false>::writeval(pval, val); + } + else + { + if (!object->has_no_split_stack()) + object->error(_("failed to match split-stack sequence at " + "section %u offset %0zx"), + shndx, fnoffset); + return; + } + + // We have to change the function so that it calls + // __morestack_non_split instead of __morestack. The former will + // allocate additional stack space. + *from = "__morestack"; + *to = "__morestack_non_split"; +} + // The selector for x86_64 object files. class Target_selector_x86_64 : public Target_selector_freebsd |