summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHan Shen <shenhan@google.com>2016-02-04 22:18:54 (GMT)
committerHan Shen <shenhan@google.com>2016-02-05 18:14:21 (GMT)
commit546435180dfe950e1c6b8447c695655d4bd8b553 (patch)
tree346cb76c499ccef863acd2da57f519cf27151455
parentc327c9c1bcffd231fcaa05f3fd8c047d1fe0afcc (diff)
downloadtoolchain_binutils-546435180dfe950e1c6b8447c695655d4bd8b553.zip
toolchain_binutils-546435180dfe950e1c6b8447c695655d4bd8b553.tar.gz
toolchain_binutils-546435180dfe950e1c6b8447c695655d4bd8b553.tar.bz2
Backport upstream patch to fix linker internal error.
The error happens while linking a huge arm32 binary (for example, a debuggable chrome browser on arm) - the plt jump offset overflows. This fixes the problem by introducing another level of indirection. By default this does not affect anything. However in case of plt jump offset overflow, instead of triggering an internal error, the linker generates a proper message and suggests add "--long-plt" to the linker command. Tested via linking debug chrome browser for arm32. Also tested by build.py on android. The upstream patch - commit ce3e49806d505721e0875e704de0b6fcba7660ed Author: Peter Collingbourne <pcc@google.com> Date: Thu Dec 17 16:50:35 2015 -0800 Implement --long-plt flag (ARM only). gold/ PR gold/18780 * arm.cc (Target_arm::do_make_data_plt): Choose PLT generator based on value of --long-plt flag. (Output_data_plt_arm_standard::do_get_plt_entry_size): Moved to Output_data_plt_arm_short. (Output_data_plt_arm_standard::do_fill_plt_entry): Likewise. (Output_data_plt_arm_standard::plt_entry): Likewise. (Output_data_plt_arm_standard::do_fill_first_plt_entry): Fix variable reference. (Output_data_plt_arm_short): New class. (Output_data_plt_arm_short::do_fill_plt_entry): Error out on too large PLT offsets instead of asserting. (Output_data_plt_arm_long): New class. * options.h (General_options): Define --long-plt flag. Change-Id: Ia61126a09f2213d1ca5c3635ec9e5b36a63f6cf3
-rw-r--r--binutils-2.25/gold/ChangeLog17
-rw-r--r--binutils-2.25/gold/arm.cc127
-rw-r--r--binutils-2.25/gold/options.h4
3 files changed, 127 insertions, 21 deletions
diff --git a/binutils-2.25/gold/ChangeLog b/binutils-2.25/gold/ChangeLog
index 2ba3b2b..aa93c36 100644
--- a/binutils-2.25/gold/ChangeLog
+++ b/binutils-2.25/gold/ChangeLog
@@ -1,3 +1,20 @@
+2015-12-17 Peter Collingbourne <pcc@google.com>
+
+ PR gold/18780
+ * arm.cc (Target_arm::do_make_data_plt): Choose PLT generator based
+ on value of --long-plt flag.
+ (Output_data_plt_arm_standard::do_get_plt_entry_size): Moved to
+ Output_data_plt_arm_short.
+ (Output_data_plt_arm_standard::do_fill_plt_entry): Likewise.
+ (Output_data_plt_arm_standard::plt_entry): Likewise.
+ (Output_data_plt_arm_standard::do_fill_first_plt_entry): Fix
+ variable reference.
+ (Output_data_plt_arm_short): New class.
+ (Output_data_plt_arm_short::do_fill_plt_entry): Error out on too large
+ PLT offsets instead of asserting.
+ (Output_data_plt_arm_long): New class.
+ * options.h (General_options): Define --long-plt flag.
+
2016-01-15 Han Shen <shenhan@google.com>
PR gold/19472 - need pc-relative stubs.
diff --git a/binutils-2.25/gold/arm.cc b/binutils-2.25/gold/arm.cc
index 86920c4..94149d6 100644
--- a/binutils-2.25/gold/arm.cc
+++ b/binutils-2.25/gold/arm.cc
@@ -62,7 +62,10 @@ template<bool big_endian>
class Output_data_plt_arm;
template<bool big_endian>
-class Output_data_plt_arm_standard;
+class Output_data_plt_arm_short;
+
+template<bool big_endian>
+class Output_data_plt_arm_long;
template<bool big_endian>
class Stub_table;
@@ -2558,7 +2561,11 @@ class Target_arm : public Sized_target<32, big_endian>
Output_data_space* got_irelative)
{
gold_assert(got_plt != NULL && got_irelative != NULL);
- return new Output_data_plt_arm_standard<big_endian>(
+ if (parameters->options().long_plt())
+ return new Output_data_plt_arm_long<big_endian>(
+ layout, got, got_plt, got_irelative);
+ else
+ return new Output_data_plt_arm_short<big_endian>(
layout, got, got_plt, got_irelative);
}
@@ -7777,29 +7784,14 @@ class Output_data_plt_arm_standard : public Output_data_plt_arm<big_endian>
do_first_plt_entry_offset() const
{ return sizeof(first_plt_entry); }
- // Return the size of a PLT entry.
- virtual unsigned int
- do_get_plt_entry_size() const
- { return sizeof(plt_entry); }
-
virtual void
do_fill_first_plt_entry(unsigned char* pov,
Arm_address got_address,
Arm_address plt_address);
- virtual void
- do_fill_plt_entry(unsigned char* pov,
- Arm_address got_address,
- Arm_address plt_address,
- unsigned int got_offset,
- unsigned int plt_offset);
-
private:
// Template for the first PLT entry.
static const uint32_t first_plt_entry[5];
-
- // Template for subsequent PLT entries.
- static const uint32_t plt_entry[3];
};
// ARM PLTs.
@@ -7827,7 +7819,7 @@ Output_data_plt_arm_standard<big_endian>::do_fill_first_plt_entry(
{
// Write first PLT entry. All but the last word are constants.
const size_t num_first_plt_words = (sizeof(first_plt_entry)
- / sizeof(plt_entry[0]));
+ / sizeof(first_plt_entry[0]));
for (size_t i = 0; i < num_first_plt_words - 1; i++)
elfcpp::Swap<32, big_endian>::writeval(pov + i * 4, first_plt_entry[i]);
// Last word in first PLT entry is &GOT[0] - .
@@ -7836,9 +7828,39 @@ Output_data_plt_arm_standard<big_endian>::do_fill_first_plt_entry(
}
// Subsequent entries in the PLT.
+// This class generates short (12-byte) entries, for displacements up to 2^28.
template<bool big_endian>
-const uint32_t Output_data_plt_arm_standard<big_endian>::plt_entry[3] =
+class Output_data_plt_arm_short : public Output_data_plt_arm_standard<big_endian>
+{
+ public:
+ Output_data_plt_arm_short(Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ : Output_data_plt_arm_standard<big_endian>(layout, got, got_plt, got_irelative)
+ { }
+
+ protected:
+ // Return the size of a PLT entry.
+ virtual unsigned int
+ do_get_plt_entry_size() const
+ { return sizeof(plt_entry); }
+
+ virtual void
+ do_fill_plt_entry(unsigned char* pov,
+ Arm_address got_address,
+ Arm_address plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset);
+
+ private:
+ // Template for subsequent PLT entries.
+ static const uint32_t plt_entry[3];
+};
+
+template<bool big_endian>
+const uint32_t Output_data_plt_arm_short<big_endian>::plt_entry[3] =
{
0xe28fc600, // add ip, pc, #0xNN00000
0xe28cca00, // add ip, ip, #0xNN000
@@ -7847,7 +7869,7 @@ const uint32_t Output_data_plt_arm_standard<big_endian>::plt_entry[3] =
template<bool big_endian>
void
-Output_data_plt_arm_standard<big_endian>::do_fill_plt_entry(
+Output_data_plt_arm_short<big_endian>::do_fill_plt_entry(
unsigned char* pov,
Arm_address got_address,
Arm_address plt_address,
@@ -7856,8 +7878,9 @@ Output_data_plt_arm_standard<big_endian>::do_fill_plt_entry(
{
int32_t offset = ((got_address + got_offset)
- (plt_address + plt_offset + 8));
+ if (offset < 0 || offset > 0x0fffffff)
+ gold_error(_("PLT offset too large, try linking with --long-plt"));
- gold_assert(offset >= 0 && offset < 0x0fffffff);
uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff);
elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
uint32_t plt_insn1 = plt_entry[1] | ((offset >> 12) & 0xff);
@@ -7866,6 +7889,68 @@ Output_data_plt_arm_standard<big_endian>::do_fill_plt_entry(
elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
}
+// This class generates long (16-byte) entries, for arbitrary displacements.
+
+template<bool big_endian>
+class Output_data_plt_arm_long : public Output_data_plt_arm_standard<big_endian>
+{
+ public:
+ Output_data_plt_arm_long(Layout* layout,
+ Arm_output_data_got<big_endian>* got,
+ Output_data_space* got_plt,
+ Output_data_space* got_irelative)
+ : Output_data_plt_arm_standard<big_endian>(layout, got, got_plt, got_irelative)
+ { }
+
+ protected:
+ // Return the size of a PLT entry.
+ virtual unsigned int
+ do_get_plt_entry_size() const
+ { return sizeof(plt_entry); }
+
+ virtual void
+ do_fill_plt_entry(unsigned char* pov,
+ Arm_address got_address,
+ Arm_address plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset);
+
+ private:
+ // Template for subsequent PLT entries.
+ static const uint32_t plt_entry[4];
+};
+
+template<bool big_endian>
+const uint32_t Output_data_plt_arm_long<big_endian>::plt_entry[4] =
+{
+ 0xe28fc200, // add ip, pc, #0xN0000000
+ 0xe28cc600, // add ip, ip, #0xNN00000
+ 0xe28cca00, // add ip, ip, #0xNN000
+ 0xe5bcf000, // ldr pc, [ip, #0xNNN]!
+};
+
+template<bool big_endian>
+void
+Output_data_plt_arm_long<big_endian>::do_fill_plt_entry(
+ unsigned char* pov,
+ Arm_address got_address,
+ Arm_address plt_address,
+ unsigned int got_offset,
+ unsigned int plt_offset)
+{
+ int32_t offset = ((got_address + got_offset)
+ - (plt_address + plt_offset + 8));
+
+ uint32_t plt_insn0 = plt_entry[0] | (offset >> 28);
+ elfcpp::Swap<32, big_endian>::writeval(pov, plt_insn0);
+ uint32_t plt_insn1 = plt_entry[1] | ((offset >> 20) & 0xff);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
+ uint32_t plt_insn2 = plt_entry[2] | ((offset >> 12) & 0xff);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_insn2);
+ uint32_t plt_insn3 = plt_entry[3] | (offset & 0xfff);
+ elfcpp::Swap<32, big_endian>::writeval(pov + 12, plt_insn3);
+}
+
// Write out the PLT. This uses the hand-coded instructions above,
// and adjusts them as needed. This is all specified by the arm ELF
// Processor Supplement.
diff --git a/binutils-2.25/gold/options.h b/binutils-2.25/gold/options.h
index db5254b..0fa26f1 100644
--- a/binutils-2.25/gold/options.h
+++ b/binutils-2.25/gold/options.h
@@ -841,6 +841,10 @@ class General_options
"veneer"),
NULL);
+ DEFINE_bool(long_plt, options::TWO_DASHES, '\0', false,
+ N_("(ARM only) Generate long PLT entries"),
+ N_("(ARM only) Do not generate long PLT entries"));
+
DEFINE_bool(g, options::EXACTLY_ONE_DASH, '\0', false,
N_("Ignored"), NULL);