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.
This commit is contained in:
Peter Collingbourne 2015-12-17 16:50:35 -08:00 committed by Cary Coutant
parent 33e0d1ca50
commit ce3e49806d
3 changed files with 130 additions and 24 deletions

View file

@ -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

View file

@ -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,69 @@ 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,
unsigned int got_offset,
unsigned int plt_offset)
{
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"));
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);
elfcpp::Swap<32, big_endian>::writeval(pov + 4, plt_insn1);
uint32_t plt_insn2 = plt_entry[2] | (offset & 0xfff);
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,
@ -7795,13 +7879,14 @@ Output_data_plt_arm_standard<big_endian>::do_fill_plt_entry(
int32_t offset = ((got_address + got_offset)
- (plt_address + plt_offset + 8));
gold_assert(offset >= 0 && offset < 0x0fffffff);
uint32_t plt_insn0 = plt_entry[0] | ((offset >> 20) & 0xff);
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 >> 12) & 0xff);
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 & 0xfff);
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,

View file

@ -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);