aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gold/ChangeLog75
-rw-r--r--gold/arm.cc9
-rwxr-xr-xgold/configure71
-rw-r--r--gold/configure.ac6
-rw-r--r--gold/dynobj.cc3
-rw-r--r--gold/gold.cc17
-rw-r--r--gold/i386.cc73
-rw-r--r--gold/object.cc29
-rw-r--r--gold/object.h73
-rw-r--r--gold/options.h4
-rw-r--r--gold/powerpc.cc9
-rw-r--r--gold/reloc.cc283
-rw-r--r--gold/sparc.cc9
-rw-r--r--gold/target-reloc.h31
-rw-r--r--gold/target.cc47
-rw-r--r--gold/target.h43
-rw-r--r--gold/testsuite/Makefile.am77
-rw-r--r--gold/testsuite/Makefile.in79
-rwxr-xr-xgold/testsuite/split_i386.sh54
-rw-r--r--gold/testsuite/split_i386_1.s33
-rw-r--r--gold/testsuite/split_i386_2.s33
-rw-r--r--gold/testsuite/split_i386_3.s22
-rw-r--r--gold/testsuite/split_i386_4.s23
-rw-r--r--gold/testsuite/split_i386_n.s12
-rwxr-xr-xgold/testsuite/split_x86_64.sh54
-rw-r--r--gold/testsuite/split_x86_64_1.s33
-rw-r--r--gold/testsuite/split_x86_64_2.s33
-rw-r--r--gold/testsuite/split_x86_64_3.s22
-rw-r--r--gold/testsuite/split_x86_64_4.s23
-rw-r--r--gold/testsuite/split_x86_64_n.s12
-rw-r--r--gold/testsuite/testfile.cc2
-rw-r--r--gold/x86_64.cc90
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