* options.h (class General_options): Define
authorIan Lance Taylor <ian@airs.com>
Tue, 6 Oct 2009 22:58:27 +0000 (22:58 +0000)
committerIan Lance Taylor <ian@airs.com>
Tue, 6 Oct 2009 22:58:27 +0000 (22:58 +0000)
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.

32 files changed:
gold/ChangeLog
gold/arm.cc
gold/configure
gold/configure.ac
gold/dynobj.cc
gold/gold.cc
gold/i386.cc
gold/object.cc
gold/object.h
gold/options.h
gold/powerpc.cc
gold/reloc.cc
gold/sparc.cc
gold/target-reloc.h
gold/target.cc
gold/target.h
gold/testsuite/Makefile.am
gold/testsuite/Makefile.in
gold/testsuite/split_i386.sh [new file with mode: 0755]
gold/testsuite/split_i386_1.s [new file with mode: 0644]
gold/testsuite/split_i386_2.s [new file with mode: 0644]
gold/testsuite/split_i386_3.s [new file with mode: 0644]
gold/testsuite/split_i386_4.s [new file with mode: 0644]
gold/testsuite/split_i386_n.s [new file with mode: 0644]
gold/testsuite/split_x86_64.sh [new file with mode: 0755]
gold/testsuite/split_x86_64_1.s [new file with mode: 0644]
gold/testsuite/split_x86_64_2.s [new file with mode: 0644]
gold/testsuite/split_x86_64_3.s [new file with mode: 0644]
gold/testsuite/split_x86_64_4.s [new file with mode: 0644]
gold/testsuite/split_x86_64_n.s [new file with mode: 0644]
gold/testsuite/testfile.cc
gold/x86_64.cc

index eab140b161000bd16f46cfa8cb9b70bf8c3cc083..796344cd226dc16c4a97bdc8fa16494fd725f95d 100644 (file)
@@ -1,3 +1,78 @@
+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.
index 17028b5ebf758486e3f9b65a973be38b16755c77..96bbdef24b57fa4580cc357695c7804061ab9065 100644 (file)
@@ -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
index 59f52a54a5a366052f2692e2de1cf06bbf8b92d6..4fa30b8a69dffc0771e85496f15863048f690780 100755 (executable)
@@ -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
index 0a535987d914fda02ec71eb001f8db8a037330bf..2492c7a11b2b3003ed959d8c8d1c48cc0d20c1d5 100644 (file)
@@ -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
index 48ff474fb4e5bef62087b915db4124a74687c798..b14d06db12dd8f360067ab47afd1b8fad8f6eb72 100644 (file)
@@ -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;
index 24542a2ce081ab0a4af318b86e7f5f71acc0a721..43fd2fc49f7f9f7d8202392678242107fec38bb5 100644 (file)
@@ -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);
 
index 04a2fa9c8ffcc6042bc953e8b93bd301c822ef50..c01b5f2233244c561b21518b542d4f1dc5800ece 100644 (file)
@@ -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
index 4d892c8c2e93713fdf25603535a32d0e9c84a6a6..5ac7878e5eedc627c7a67eab6964e2ead456eca0 100644 (file)
@@ -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)
             {
index e7d05c72c880c2616f441c20c440ed09f73daf6b..bbc1a106e46fd2efe63d7104870f2c03be586509 100644 (file)
@@ -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
index 24f52fa0c55872984e21a9a2246db508ac8eb0f8..f590630731333e2a72a65990fd6e1b56a0e603cf 100644 (file)
@@ -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',
index 05d627be19910834c96b108a41ce4cf8eccafd84..637cbbdf678802c44ca913d70c3b6d1ab81701c0 100644 (file)
@@ -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
index bfa2f1c38edb381a4f48329dcf0a733920bc7775..7fb5cea7d50556ca167d963c79b791756bd4e438 100644 (file)
@@ -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>
index 16a6ffcfc5247984c6472a4076157a8d1e2cc301..3d31dd6e77519007cf7e5c38323b13c9aea7740d 100644 (file)
@@ -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
index d93e7552888bb71a432d7cc8324964e967354c45..bc001232f5799b02938a0f4d9cbfd2e41d265c4d 100644 (file)
@@ -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())
index d971cff62674a401bc6350eb1ca82d540d1f2a73..b129b59fac6278f0ccdeaa21dc184c208fb5efb5 100644 (file)
@@ -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.
index ac59fb25ec2067a0099cd81c554a5e9ac909f874..68d89dc5ed814e8646b43fd9687d91caf5505919 100644 (file)
@@ -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
index 25740b83289a9d3889994c981b253139b509ac0d..6d40a5dc625503f78e2a4b96518e5bc695e51fa2 100644 (file)
@@ -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
index 36334ef0c1e08aa5e521b6de1afa256a8e7aff10..f0472145b8e79a0232aea6b51bfcdd8352bf4fb4 100644 (file)
@@ -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 (executable)
index 0000000..e94fea2
--- /dev/null
@@ -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 (file)
index 0000000..9ac816e
--- /dev/null
@@ -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 (file)
index 0000000..b4a2f14
--- /dev/null
@@ -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 (file)
index 0000000..fdde7a9
--- /dev/null
@@ -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 (file)
index 0000000..be774ae
--- /dev/null
@@ -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 (file)
index 0000000..4d4e6e8
--- /dev/null
@@ -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 (executable)
index 0000000..61544b2
--- /dev/null
@@ -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 (file)
index 0000000..e23ea7f
--- /dev/null
@@ -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 (file)
index 0000000..0559bbc
--- /dev/null
@@ -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 (file)
index 0000000..68ae6e3
--- /dev/null
@@ -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 (file)
index 0000000..653b917
--- /dev/null
@@ -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 (file)
index 0000000..10436b1
--- /dev/null
@@ -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
index 2532307994f5c9c981ed964164e02da965f22108..d66189a8c79ff162796760c9b330307f9bfd9503 100644 (file)
@@ -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
index 419746dc529257a72b5b65a1679e28d6e30d7901..46e879df5f8d5813c338221995ec9ff00453118f 100644 (file)
@@ -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