diff options
author | Alan Modra <amodra@gmail.com> | 2012-09-12 22:43:54 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2012-09-12 22:43:54 +0000 |
commit | bfdfa4cd8da8b878613e3ee98c8268f8aa72983c (patch) | |
tree | 2ee2b0b7b57dafee8585b56c562cef597f3b8266 /gold/powerpc.cc | |
parent | 7c0e90d28c56dd5e0003c921d15aded77c9a639d (diff) | |
download | fsf-binutils-gdb-bfdfa4cd8da8b878613e3ee98c8268f8aa72983c.zip fsf-binutils-gdb-bfdfa4cd8da8b878613e3ee98c8268f8aa72983c.tar.gz fsf-binutils-gdb-bfdfa4cd8da8b878613e3ee98c8268f8aa72983c.tar.bz2 |
* 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.
Diffstat (limited to 'gold/powerpc.cc')
-rw-r--r-- | gold/powerpc.cc | 176 |
1 files changed, 137 insertions, 39 deletions
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 |