aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gold/ChangeLog18
-rw-r--r--gold/arm.cc9
-rw-r--r--gold/i386.cc23
-rw-r--r--gold/powerpc.cc176
-rw-r--r--gold/sparc.cc9
-rw-r--r--gold/target-reloc.h41
-rw-r--r--gold/x86_64.cc9
7 files changed, 208 insertions, 77 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 9ad5a88..153a87d 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,21 @@
+2012-09-13 Alan Modra <amodra@gmail.com>
+
+ * target-reloc.h (scan_relocs): Call scan.local for relocs
+ against symbols in discarded sections. Pass is_discarded
+ param.
+ * arm.cc, * i386.cc, * sparc.cc, * x86_64.cc (Target_*::Scan::local):
+ Add is_discarded param.
+ * powerpc (Target_powerpc::Scan::local): Likewise. Use
+ is_discarded to flag opd entry as discarded. Don't emit dyn
+ relocs on such entries.
+ (Target_powerpc::Scan::global): Similarly detect and handle
+ such opd entries.
+ (Powerpc_relobj): Replace opd_ent_shndx_ and opd_ent_off_ with
+ opd_ent_. Update all uses.
+ (Powerpc_relobj::get_opd_discard, set_opd_discard): New functions.
+ (Target_powerpc::relocate_section): Zero out discarded opd
+ entry relocs.
+
2012-09-12 Ian Lance Taylor <iant@google.com>
PR gold/14570
diff --git a/gold/arm.cc b/gold/arm.cc
index 351c6fe..d874ce0 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -2551,7 +2551,8 @@ class Target_arm : public Sized_target<32, big_endian>
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type,
- const elfcpp::Sym<32, big_endian>& lsym);
+ const elfcpp::Sym<32, big_endian>& lsym,
+ bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_arm* target,
@@ -7857,8 +7858,12 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
Output_section* output_section,
const elfcpp::Rel<32, big_endian>& reloc,
unsigned int r_type,
- const elfcpp::Sym<32, big_endian>& lsym)
+ const elfcpp::Sym<32, big_endian>& lsym,
+ bool is_discarded)
{
+ if (is_discarded)
+ return;
+
r_type = get_real_reloc_type(r_type);
switch (r_type)
{
diff --git a/gold/i386.cc b/gold/i386.cc
index 91611a1..47779d0 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -538,7 +538,8 @@ class Target_i386 : public Sized_target<32, false>
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rel<32, false>& reloc, unsigned int r_type,
- const elfcpp::Sym<32, false>& lsym);
+ const elfcpp::Sym<32, false>& lsym,
+ bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_i386* target,
@@ -1702,15 +1703,19 @@ Target_i386::Scan::reloc_needs_plt_for_ifunc(
inline void
Target_i386::Scan::local(Symbol_table* symtab,
- Layout* layout,
- Target_i386* target,
- Sized_relobj_file<32, false>* object,
- unsigned int data_shndx,
- Output_section* output_section,
- const elfcpp::Rel<32, false>& reloc,
- unsigned int r_type,
- const elfcpp::Sym<32, false>& lsym)
+ Layout* layout,
+ Target_i386* target,
+ Sized_relobj_file<32, false>* object,
+ unsigned int data_shndx,
+ Output_section* output_section,
+ const elfcpp::Rel<32, false>& reloc,
+ unsigned int r_type,
+ const elfcpp::Sym<32, false>& lsym,
+ bool is_discarded)
{
+ if (is_discarded)
+ return;
+
// A local STT_GNU_IFUNC symbol may require a PLT entry.
if (lsym.get_st_type() == elfcpp::STT_GNU_IFUNC
&& this->reloc_needs_plt_for_ifunc(object, r_type))
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 31f5ddf..62e850f 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -65,8 +65,7 @@ public:
Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
const typename elfcpp::Ehdr<size, big_endian>& ehdr)
: Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
- special_(0), opd_ent_shndx_(), opd_ent_off_(), access_from_map_(),
- opd_valid_(false)
+ special_(0), opd_valid_(false), opd_ent_(), access_from_map_()
{ }
~Powerpc_relobj()
@@ -97,8 +96,7 @@ public:
init_opd(size_t opd_size)
{
size_t count = this->opd_ent_ndx(opd_size);
- this->opd_ent_shndx_.resize(count);
- this->opd_ent_off_.reserve(count);
+ this->opd_ent_.resize(count);
}
// Return section and offset of function entry for .opd + R_OFF.
@@ -106,11 +104,11 @@ public:
get_opd_ent(Address r_off, Address* value = NULL) const
{
size_t ndx = this->opd_ent_ndx(r_off);
- gold_assert(ndx < this->opd_ent_shndx_.size());
- gold_assert(this->opd_ent_shndx_[ndx] != 0);
+ gold_assert(ndx < this->opd_ent_.size());
+ gold_assert(this->opd_ent_[ndx].shndx != 0);
if (value != NULL)
- *value = this->opd_ent_off_[ndx];
- return this->opd_ent_shndx_[ndx];
+ *value = this->opd_ent_[ndx].off;
+ return this->opd_ent_[ndx].shndx;
}
// Set section and offset of function entry for .opd + R_OFF.
@@ -118,9 +116,27 @@ public:
set_opd_ent(Address r_off, unsigned int shndx, Address value)
{
size_t ndx = this->opd_ent_ndx(r_off);
- gold_assert(ndx < this->opd_ent_shndx_.size());
- this->opd_ent_shndx_[ndx] = shndx;
- this->opd_ent_off_[ndx] = value;
+ gold_assert(ndx < this->opd_ent_.size());
+ this->opd_ent_[ndx].shndx = shndx;
+ this->opd_ent_[ndx].off = value;
+ }
+
+ // Return discard flag for .opd + R_OFF.
+ bool
+ get_opd_discard(Address r_off) const
+ {
+ size_t ndx = this->opd_ent_ndx(r_off);
+ gold_assert(ndx < this->opd_ent_.size());
+ return this->opd_ent_[ndx].discard;
+ }
+
+ // Set discard flag for .opd + R_OFF.
+ void
+ set_opd_discard(Address r_off)
+ {
+ size_t ndx = this->opd_ent_ndx(r_off);
+ gold_assert(ndx < this->opd_ent_.size());
+ this->opd_ent_[ndx].discard = true;
}
Access_from*
@@ -165,38 +181,47 @@ public:
{ return 0x8000; }
private:
- // Return index into opd_ent_shndx or opd_ent_off array for .opd entry
- // at OFF. .opd entries are 24 bytes long, but they can be spaced
- // 16 bytes apart when the language doesn't use the last 8-byte
- // word, the environment pointer. Thus dividing the entry section
- // offset by 16 will give an index into opd_ent_shndx_ and
- // opd_ent_off_ that works for either layout of .opd. (It leaves
- // some elements of the vectors unused when .opd entries are spaced
- // 24 bytes apart, but we don't know the spacing until relocations
- // are processed, and in any case it is possible for an object to
- // have some entries spaced 16 bytes apart and others 24 bytes apart.)
+ struct Opd_ent
+ {
+ unsigned int shndx;
+ bool discard;
+ Offset off;
+ };
+
+ // Return index into opd_ent_ array for .opd entry at OFF.
+ // .opd entries are 24 bytes long, but they can be spaced 16 bytes
+ // apart when the language doesn't use the last 8-byte word, the
+ // environment pointer. Thus dividing the entry section offset by
+ // 16 will give an index into opd_ent_ that works for either layout
+ // of .opd. (It leaves some elements of the vector unused when .opd
+ // entries are spaced 24 bytes apart, but we don't know the spacing
+ // until relocations are processed, and in any case it is possible
+ // for an object to have some entries spaced 16 bytes apart and
+ // others 24 bytes apart.)
size_t
opd_ent_ndx(size_t off) const
{ return off >> 4;}
// For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
unsigned int special_;
+
+ // Set at the start of gc_process_relocs, when we know opd_ent_
+ // vector is valid. The flag could be made atomic and set in
+ // do_read_relocs with memory_order_release and then tested with
+ // memory_order_acquire, potentially resulting in fewer entries in
+ // access_from_map_.
+ bool opd_valid_;
+
// The first 8-byte word of an OPD entry gives the address of the
// entry point of the function. Relocatable object files have a
- // relocation on this word. The following two vectors record the
+ // relocation on this word. The following vector records the
// section and offset specified by these relocations.
- std::vector<unsigned int> opd_ent_shndx_;
- std::vector<Offset> opd_ent_off_;
+ std::vector<Opd_ent> opd_ent_;
+
// References made to this object's .opd section when running
- // gc_process_relocs for another object, before the opd_ent vectors
- // are valid for this object.
+ // gc_process_relocs for another object, before the opd_ent_ vector
+ // is valid for this object.
Access_from access_from_map_;
- // Set at the start of gc_process_relocs, when we know opd_ent
- // vectors are valid. The flag could be made atomic and set in
- // do_read_relocs with memory_order_release and then tested with
- // memory_order_acquire, potentially resulting in fewer entries in
- // access_from_map_.
- bool opd_valid_;
};
template<int size, bool big_endian>
@@ -413,6 +438,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
class Scan
{
public:
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+
Scan()
: issued_non_pic_error_(false)
{ }
@@ -426,7 +453,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
- const elfcpp::Sym<size, big_endian>& lsym);
+ const elfcpp::Sym<size, big_endian>& lsym,
+ bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_powerpc* target,
@@ -2361,11 +2389,21 @@ Target_powerpc<size, big_endian>::Scan::local(
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
- const elfcpp::Sym<size, big_endian>& /* lsym */)
+ const elfcpp::Sym<size, big_endian>& /* lsym */,
+ bool is_discarded)
{
Powerpc_relobj<size, big_endian>* ppc_object
= static_cast<Powerpc_relobj<size, big_endian>*>(object);
+ if (is_discarded)
+ {
+ if (size == 64
+ && data_shndx == ppc_object->opd_shndx()
+ && r_type == elfcpp::R_PPC64_ADDR64)
+ ppc_object->set_opd_discard(reloc.get_r_offset());
+ return;
+ }
+
switch (r_type)
{
case elfcpp::R_POWERPC_NONE:
@@ -2382,13 +2420,19 @@ Target_powerpc<size, big_endian>::Scan::local(
= target->got_section(symtab, layout);
if (parameters->options().output_is_position_independent())
{
+ Address off = reloc.get_r_offset();
+ if (size == 64
+ && data_shndx == ppc_object->opd_shndx()
+ && ppc_object->get_opd_discard(off - 8))
+ break;
+
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+ Powerpc_relobj<size, big_endian>* symobj = ppc_object;
rela_dyn->add_output_section_relative(got->output_section(),
elfcpp::R_POWERPC_RELATIVE,
output_section,
- object, data_shndx,
- reloc.get_r_offset(),
- ppc_object->toc_base_offset());
+ object, data_shndx, off,
+ symobj->toc_base_offset());
}
}
break;
@@ -2658,6 +2702,12 @@ Target_powerpc<size, big_endian>::Scan::global(
= target->got_section(symtab, layout);
if (parameters->options().output_is_position_independent())
{
+ Address off = reloc.get_r_offset();
+ if (size == 64
+ && data_shndx == ppc_object->opd_shndx()
+ && ppc_object->get_opd_discard(off - 8))
+ break;
+
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
Powerpc_relobj<size, big_endian>* symobj = ppc_object;
if (data_shndx != ppc_object->opd_shndx())
@@ -2666,14 +2716,22 @@ Target_powerpc<size, big_endian>::Scan::global(
rela_dyn->add_output_section_relative(got->output_section(),
elfcpp::R_POWERPC_RELATIVE,
output_section,
- object, data_shndx,
- reloc.get_r_offset(),
+ object, data_shndx, off,
symobj->toc_base_offset());
}
}
break;
case elfcpp::R_PPC64_ADDR64:
+ if (size == 64
+ && data_shndx == ppc_object->opd_shndx()
+ && (gsym->is_defined_in_discarded_section()
+ || gsym->object() != object))
+ {
+ ppc_object->set_opd_discard(reloc.get_r_offset());
+ break;
+ }
+ // Fall thru
case elfcpp::R_PPC64_UADDR64:
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
@@ -4038,6 +4096,43 @@ Target_powerpc<size, big_endian>::relocate_section(
gold_assert(sh_type == elfcpp::SHT_RELA);
+ unsigned char *opd_rel = NULL;
+ Powerpc_relobj<size, big_endian>* const object
+ = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
+ if (size == 64
+ && relinfo->data_shndx == object->opd_shndx())
+ {
+ // Rewrite opd relocs, omitting those for discarded sections
+ // to silence gold::relocate_section errors.
+ const int reloc_size
+ = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+ opd_rel = new unsigned char[reloc_count * reloc_size];
+ const unsigned char* rrel = prelocs;
+ unsigned char* wrel = opd_rel;
+
+ for (size_t i = 0;
+ i < reloc_count;
+ ++i, rrel += reloc_size, wrel += reloc_size)
+ {
+ typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
+ reloc(rrel);
+ typename elfcpp::Elf_types<size>::Elf_WXword r_info
+ = reloc.get_r_info();
+ unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+ Address r_off = reloc.get_r_offset();
+ if (r_type == elfcpp::R_PPC64_TOC)
+ r_off -= 8;
+ bool is_discarded = object->get_opd_discard(r_off);
+
+ // Reloc number is reported in some errors, so keep all relocs.
+ if (is_discarded)
+ memset(wrel, 0, reloc_size);
+ else
+ memcpy(wrel, rrel, reloc_size);
+ }
+ prelocs = opd_rel;
+ }
+
gold::relocate_section<size, big_endian, Powerpc, elfcpp::SHT_RELA,
Powerpc_relocate>(
relinfo,
@@ -4050,6 +4145,9 @@ Target_powerpc<size, big_endian>::relocate_section(
address,
view_size,
reloc_symbol_changes);
+
+ if (opd_rel != NULL)
+ delete[] opd_rel;
}
class Powerpc_scan_relocatable_reloc
diff --git a/gold/sparc.cc b/gold/sparc.cc
index 04a88bf..71dd3d5 100644
--- a/gold/sparc.cc
+++ b/gold/sparc.cc
@@ -237,7 +237,8 @@ class Target_sparc : public Sized_target<size, big_endian>
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
- const elfcpp::Sym<size, big_endian>& lsym);
+ const elfcpp::Sym<size, big_endian>& lsym,
+ bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_sparc* target,
@@ -2240,8 +2241,12 @@ Target_sparc<size, big_endian>::Scan::local(
Output_section* output_section,
const elfcpp::Rela<size, big_endian>& reloc,
unsigned int r_type,
- const elfcpp::Sym<size, big_endian>& lsym)
+ const elfcpp::Sym<size, big_endian>& lsym,
+ bool is_discarded)
{
+ if (is_discarded)
+ return;
+
bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
unsigned int orig_r_type = r_type;
r_type &= 0xff;
diff --git a/gold/target-reloc.h b/gold/target-reloc.h
index 96f2614..5e6dba7 100644
--- a/gold/target-reloc.h
+++ b/gold/target-reloc.h
@@ -81,30 +81,25 @@ scan_relocs(
unsigned int shndx = lsym.get_st_shndx();
bool is_ordinary;
shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
- if (is_ordinary
- && shndx != elfcpp::SHN_UNDEF
- && !object->is_section_included(shndx)
- && !symtab->is_section_folded(object, shndx))
- {
- // RELOC is a relocation against a local symbol in a
- // section we are discarding. We can ignore this
- // relocation. It will eventually become a reloc
- // against the value zero.
- //
- // FIXME: We should issue a warning if this is an
- // allocated section; is this the best place to do it?
- //
- // FIXME: The old GNU linker would in some cases look
- // for the linkonce section which caused this section to
- // be discarded, and, if the other section was the same
- // size, change the reloc to refer to the other section.
- // That seems risky and weird to me, and I don't know of
- // any case where it is actually required.
-
- continue;
- }
+ // If RELOC is a relocation against a local symbol in a
+ // section we are discarding then we can ignore it. It will
+ // eventually become a reloc against the value zero.
+ //
+ // FIXME: We should issue a warning if this is an
+ // allocated section; is this the best place to do it?
+ //
+ // FIXME: The old GNU linker would in some cases look
+ // for the linkonce section which caused this section to
+ // be discarded, and, if the other section was the same
+ // size, change the reloc to refer to the other section.
+ // That seems risky and weird to me, and I don't know of
+ // any case where it is actually required.
+ bool is_discarded = (is_ordinary
+ && shndx != elfcpp::SHN_UNDEF
+ && !object->is_section_included(shndx)
+ && !symtab->is_section_folded(object, shndx));
scan.local(symtab, layout, target, object, data_shndx,
- output_section, reloc, r_type, lsym);
+ output_section, reloc, r_type, lsym, is_discarded);
}
else
{
diff --git a/gold/x86_64.cc b/gold/x86_64.cc
index 1712beb..5914160 100644
--- a/gold/x86_64.cc
+++ b/gold/x86_64.cc
@@ -676,7 +676,8 @@ class Target_x86_64 : public Sized_target<size, false>
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Rela<size, false>& reloc, unsigned int r_type,
- const elfcpp::Sym<size, false>& lsym);
+ const elfcpp::Sym<size, false>& lsym,
+ bool is_discarded);
inline void
global(Symbol_table* symtab, Layout* layout, Target_x86_64* target,
@@ -2270,8 +2271,12 @@ Target_x86_64<size>::Scan::local(Symbol_table* symtab,
Output_section* output_section,
const elfcpp::Rela<size, false>& reloc,
unsigned int r_type,
- const elfcpp::Sym<size, false>& lsym)
+ const elfcpp::Sym<size, false>& lsym,
+ bool is_discarded)
{
+ if (is_discarded)
+ return;
+
// A local STT_GNU_IFUNC symbol may require a PLT entry.
bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))