| 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. |
| |
| diff --git a/gold/ChangeLog b/gold/ChangeLog |
| index d929cb7..07589c9 100644 |
| --- a/gold/ChangeLog |
| +++ b/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. |
| + |
| 2015-12-16 Roland McGrath <mcgrathr@google.com> |
| |
| PR ld/17473 |
| diff --git a/gold/arm.cc b/gold/arm.cc |
| index 33e8734..fc387bb 100644 |
| --- a/gold/arm.cc |
| +++ b/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; |
| @@ -2554,7 +2557,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); |
| } |
| |
| @@ -7715,29 +7722,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. |
| @@ -7765,7 +7757,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] - . |
| @@ -7774,9 +7766,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 |
| @@ -7785,7 +7807,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, |
| @@ -7794,8 +7816,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); |
| @@ -7804,6 +7827,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/gold/options.h b/gold/options.h |
| index ffc44e6..8b5159f 100644 |
| --- a/gold/options.h |
| +++ b/gold/options.h |
| @@ -834,6 +834,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); |
| |