diff --git a/gold/ChangeLog b/gold/ChangeLog index 9ad5a88d8c..153a87d575 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,21 @@ +2012-09-13 Alan Modra + + * 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 PR gold/14570 diff --git a/gold/arm.cc b/gold/arm.cc index 351c6fe148..d874ce0279 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::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 91611a1339..47779d0e8c 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 31f5ddf752..62e850f338 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& ehdr) : Sized_relobj_file(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_; - // 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 - // section and offset specified by these relocations. - std::vector opd_ent_shndx_; - std::vector opd_ent_off_; - // 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. - 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 + + // 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 vector records the + // section and offset specified by these relocations. + std::vector opd_ent_; + + // References made to this object's .opd section when running + // gc_process_relocs for another object, before the opd_ent_ vector + // is valid for this object. + Access_from access_from_map_; }; template @@ -413,6 +438,8 @@ class Target_powerpc : public Sized_target class Scan { public: + typedef typename elfcpp::Elf_types::Elf_Addr Address; + Scan() : issued_non_pic_error_(false) { } @@ -426,7 +453,8 @@ class Target_powerpc : public Sized_target unsigned int data_shndx, Output_section* output_section, const elfcpp::Rela& reloc, unsigned int r_type, - const elfcpp::Sym& lsym); + const elfcpp::Sym& lsym, + bool is_discarded); inline void global(Symbol_table* symtab, Layout* layout, Target_powerpc* target, @@ -2361,11 +2389,21 @@ Target_powerpc::Scan::local( Output_section* output_section, const elfcpp::Rela& reloc, unsigned int r_type, - const elfcpp::Sym& /* lsym */) + const elfcpp::Sym& /* lsym */, + bool is_discarded) { Powerpc_relobj* ppc_object = static_cast*>(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::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* 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::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* symobj = ppc_object; if (data_shndx != ppc_object->opd_shndx()) @@ -2666,14 +2716,22 @@ Target_powerpc::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::relocate_section( gold_assert(sh_type == elfcpp::SHT_RELA); + unsigned char *opd_rel = NULL; + Powerpc_relobj* const object + = static_cast*>(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::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::Reloc + reloc(rrel); + typename elfcpp::Elf_types::Elf_WXword r_info + = reloc.get_r_info(); + unsigned int r_type = elfcpp::elf_r_type(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( relinfo, @@ -4050,6 +4145,9 @@ Target_powerpc::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 04a88bf21b..71dd3d53c3 100644 --- a/gold/sparc.cc +++ b/gold/sparc.cc @@ -237,7 +237,8 @@ class Target_sparc : public Sized_target unsigned int data_shndx, Output_section* output_section, const elfcpp::Rela& reloc, unsigned int r_type, - const elfcpp::Sym& lsym); + const elfcpp::Sym& lsym, + bool is_discarded); inline void global(Symbol_table* symtab, Layout* layout, Target_sparc* target, @@ -2240,8 +2241,12 @@ Target_sparc::Scan::local( Output_section* output_section, const elfcpp::Rela& reloc, unsigned int r_type, - const elfcpp::Sym& lsym) + const elfcpp::Sym& 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 96f2614f00..5e6dba7724 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 1712beb217..5914160b2a 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -676,7 +676,8 @@ class Target_x86_64 : public Sized_target unsigned int data_shndx, Output_section* output_section, const elfcpp::Rela& reloc, unsigned int r_type, - const elfcpp::Sym& lsym); + const elfcpp::Sym& lsym, + bool is_discarded); inline void global(Symbol_table* symtab, Layout* layout, Target_x86_64* target, @@ -2270,8 +2271,12 @@ Target_x86_64::Scan::local(Symbol_table* symtab, Output_section* output_section, const elfcpp::Rela& reloc, unsigned int r_type, - const elfcpp::Sym& lsym) + const elfcpp::Sym& 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))