From d61c6bd41cb84434590a97fe13be433b418e0bbd Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 9 Nov 2007 19:45:08 +0000 Subject: [PATCH] From Cary Coutant: More shared library support, some refactorization. --- gold/i386.cc | 233 +++++++++++++++++++++++++++++++-------------------------- gold/output.cc | 7 +- gold/symtab.cc | 1 + gold/symtab.h | 82 ++++++++++++++++++++ gold/x86_64.cc | 171 +++++++++++++++++++++++------------------- 5 files changed, 311 insertions(+), 183 deletions(-) diff --git a/gold/i386.cc b/gold/i386.cc index d0b0b51..dcbb839 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -157,7 +157,8 @@ class Target_i386 : public Sized_target<32, false> // Return whether the static relocation needs to be applied. inline bool should_apply_static_reloc(const Sized_symbol<32>* gsym, - bool is_pcrel, + bool is_absolute_ref, + bool is_function_call, bool is_32bit); // Do a relocation. Return false if the caller should not issue @@ -256,6 +257,17 @@ class Target_i386 : public Sized_target<32, false> Reloc_section* rel_dyn_section(Layout*); + // Return true if the symbol may need a COPY relocation. + // References from an executable object to non-function symbols + // defined in a dynamic object may need a COPY relocation. + bool + may_need_copy_reloc(Symbol* gsym) + { + return (!parameters->output_is_shared() + && gsym->is_from_dynobj() + && gsym->type() != elfcpp::STT_FUNC); + } + // Copy a relocation against a global symbol. void copy_reloc(const General_options*, Symbol_table*, Layout*, @@ -771,8 +783,6 @@ Target_i386::Scan::local(const General_options&, break; case elfcpp::R_386_32: - case elfcpp::R_386_16: - case elfcpp::R_386_8: // If building a shared library (or a position-independent // executable), we need to create a dynamic relocation for // this location. The relocation applied at link time will @@ -782,15 +792,24 @@ Target_i386::Scan::local(const General_options&, if (parameters->output_is_position_independent()) { Reloc_section* rel_dyn = target->rel_dyn_section(layout); - if (r_type == elfcpp::R_386_32) - rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx, - reloc.get_r_offset()); - else - { - unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); - rel_dyn->add_local(object, r_sym, r_type, data_shndx, - reloc.get_r_offset()); - } + rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx, + reloc.get_r_offset()); + } + break; + + case elfcpp::R_386_16: + case elfcpp::R_386_8: + // If building a shared library (or a position-independent + // executable), we need to create a dynamic relocation for + // this location. Because the addend needs to remain in the + // data section, we need to be careful not to apply this + // relocation statically. + if (parameters->output_is_position_independent()) + { + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + rel_dyn->add_local(object, r_sym, r_type, data_shndx, + reloc.get_r_offset()); } break; @@ -951,71 +970,67 @@ Target_i386::Scan::global(const General_options& options, break; case elfcpp::R_386_32: - case elfcpp::R_386_PC32: case elfcpp::R_386_16: - case elfcpp::R_386_PC16: case elfcpp::R_386_8: - case elfcpp::R_386_PC8: { - bool is_pcrel = (r_type == elfcpp::R_386_PC32 - || r_type == elfcpp::R_386_PC16 - || r_type == elfcpp::R_386_PC8); - - if (gsym->is_from_dynobj() - || (parameters->output_is_shared() - && gsym->is_preemptible())) - { - // (a) This symbol is defined in a dynamic object. If it is a - // function, we make a PLT entry. Otherwise we need to - // either generate a COPY reloc or copy this reloc. - // (b) We are building a shared object and this symbol is - // preemptible. If it is a function, we make a PLT entry. - // Otherwise, we copy the reloc. - if (gsym->type() == elfcpp::STT_FUNC) - { - target->make_plt_entry(symtab, layout, gsym); - - // If this is not a PC relative reference, then we may - // be taking the address of the function. In that case - // we need to set the entry in the dynamic symbol table - // to the address of the PLT entry. We will also need to - // create a dynamic relocation. - if (!is_pcrel) - { - if (gsym->is_from_dynobj()) - gsym->set_needs_dynsym_value(); - if (parameters->output_is_position_independent()) - { - Reloc_section* rel_dyn = - target->rel_dyn_section(layout); - rel_dyn->add_global(gsym, r_type, object, data_shndx, - reloc.get_r_offset()); - } - } - } - else if (parameters->output_is_shared()) - { - // We do not make COPY relocs in shared objects. + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + { + target->make_plt_entry(symtab, layout, gsym); + // Since this is not a PC-relative relocation, we may be + // taking the address of a function. In that case we need to + // set the entry in the dynamic symbol table to the address of + // the PLT entry. + if (gsym->is_from_dynobj()) + gsym->set_needs_dynsym_value(); + } + // Make a dynamic relocation if necessary. + if (gsym->needs_dynamic_reloc(true, false)) + { + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(&options, symtab, layout, object, data_shndx, + gsym, reloc); + } + else if (r_type == elfcpp::R_386_32 + && gsym->can_use_relative_reloc(false)) + { + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx, + reloc.get_r_offset()); + } + else + { Reloc_section* rel_dyn = target->rel_dyn_section(layout); rel_dyn->add_global(gsym, r_type, object, data_shndx, reloc.get_r_offset()); - } - else - target->copy_reloc(&options, symtab, layout, object, data_shndx, - gsym, reloc); - } - else if (!is_pcrel && parameters->output_is_position_independent()) + } + } + } + break; + + case elfcpp::R_386_PC32: + case elfcpp::R_386_PC16: + case elfcpp::R_386_PC8: + { + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + target->make_plt_entry(symtab, layout, gsym); + // Make a dynamic relocation if necessary. + bool is_function_call = (gsym->type() == elfcpp::STT_FUNC); + if (gsym->needs_dynamic_reloc(false, is_function_call)) { - // This is not a PC-relative reference, so we need to generate - // a dynamic relocation. At this point, we know the symbol - // is not preemptible, so we can use the RELATIVE relocation. - Reloc_section* rel_dyn = target->rel_dyn_section(layout); - if (r_type == elfcpp::R_386_32) - rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, data_shndx, - reloc.get_r_offset()); + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(&options, symtab, layout, object, data_shndx, + gsym, reloc); + } else - rel_dyn->add_global(gsym, r_type, object, data_shndx, - reloc.get_r_offset()); + { + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_global(gsym, r_type, object, data_shndx, + reloc.get_r_offset()); + } } } break; @@ -1031,8 +1046,16 @@ Target_i386::Scan::global(const General_options& options, if (!gsym->final_value_is_known()) { Reloc_section* rel_dyn = target->rel_dyn_section(layout); - rel_dyn->add_global(gsym, elfcpp::R_386_GLOB_DAT, got, - gsym->got_offset()); + if (gsym->is_preemptible()) + rel_dyn->add_global(gsym, elfcpp::R_386_GLOB_DAT, got, + gsym->got_offset()); + else + { + rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, + got, gsym->got_offset()); + // Make sure we write the link-time value to the GOT. + gsym->set_needs_value_in_got(); + } } } } @@ -1246,36 +1269,22 @@ Target_i386::do_finalize_sections(Layout* layout) inline bool Target_i386::Relocate::should_apply_static_reloc(const Sized_symbol<32>* gsym, - bool is_pcrel, + bool is_absolute_ref, + bool is_function_call, bool is_32bit) { - // For local symbols, return FALSE if a non-RELATIVE dynamic - // relocation was created; return TRUE otherwise. + // For local symbols, we will have created a non-RELATIVE dynamic + // relocation only if (a) the output is position independent, + // (b) the relocation is absolute (not pc- or segment-relative), and + // (c) the relocation is not 32 bits wide. if (gsym == NULL) - return (!parameters->output_is_position_independent() || is_32bit); - - // For global symbols, mimic the logic in Scan::global() - // to decide whether a non-RELATIVE dynamic relocation was - // created. - // FIXME: This is ugly. Try to refactor this logic so it can be - // shared by Scan::global() and Relocate::relocate(). - if (gsym->is_from_dynobj() - || (parameters->output_is_shared() - && gsym->is_preemptible())) - { - if (gsym->type() == elfcpp::STT_FUNC) - { - if (!is_pcrel && parameters->output_is_position_independent()) - return false; - } - else - return false; - } - else if (!is_pcrel && parameters->output_is_position_independent()) - return is_32bit; + return !(parameters->output_is_position_independent() + && is_absolute_ref + && !is_32bit); - // For all other cases, return TRUE - return true; + // For global symbols, we use the same helper routines used in the scan pass. + return !(gsym->needs_dynamic_reloc(is_absolute_ref, is_function_call) + && !gsym->can_use_relative_reloc(is_function_call)); } // Perform a relocation. @@ -1355,33 +1364,45 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, break; case elfcpp::R_386_32: - if (should_apply_static_reloc(gsym, false, true)) + if (should_apply_static_reloc(gsym, true, false, true)) Relocate_functions<32, false>::rel32(view, object, psymval); break; case elfcpp::R_386_PC32: - if (should_apply_static_reloc(gsym, true, true)) - Relocate_functions<32, false>::pcrel32(view, object, psymval, address); + { + bool is_function_call = (gsym != NULL + && gsym->type() == elfcpp::STT_FUNC); + if (should_apply_static_reloc(gsym, false, is_function_call, true)) + Relocate_functions<32, false>::pcrel32(view, object, psymval, address); + } break; case elfcpp::R_386_16: - if (should_apply_static_reloc(gsym, false, false)) + if (should_apply_static_reloc(gsym, true, false, false)) Relocate_functions<32, false>::rel16(view, object, psymval); break; case elfcpp::R_386_PC16: - if (should_apply_static_reloc(gsym, true, false)) - Relocate_functions<32, false>::pcrel16(view, object, psymval, address); + { + bool is_function_call = (gsym != NULL + && gsym->type() == elfcpp::STT_FUNC); + if (should_apply_static_reloc(gsym, false, is_function_call, false)) + Relocate_functions<32, false>::pcrel32(view, object, psymval, address); + } break; case elfcpp::R_386_8: - if (should_apply_static_reloc(gsym, false, false)) + if (should_apply_static_reloc(gsym, true, false, false)) Relocate_functions<32, false>::rel8(view, object, psymval); break; case elfcpp::R_386_PC8: - if (should_apply_static_reloc(gsym, true, false)) - Relocate_functions<32, false>::pcrel8(view, object, psymval, address); + { + bool is_function_call = (gsym != NULL + && gsym->type() == elfcpp::STT_FUNC); + if (should_apply_static_reloc(gsym, false, is_function_call, false)) + Relocate_functions<32, false>::pcrel32(view, object, psymval, address); + } break; case elfcpp::R_386_PLT32: diff --git a/gold/output.cc b/gold/output.cc index ca99097..b191725 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -684,8 +684,11 @@ Output_data_got::Got_entry::write(unsigned char* pov) const // If the symbol is resolved locally, we need to write out its // value. Otherwise we just write zero. The target code is // responsible for creating a relocation entry to fill in the - // value at runtime. - if (gsym->final_value_is_known()) + // value at runtime. For non-preemptible symbols in a shared + // library, the target will need to record whether or not the + // value should be written (e.g., it may use a RELATIVE + // relocation type). + if (gsym->final_value_is_known() || gsym->needs_value_in_got()) { Sized_symbol* sgsym; // This cast is a bit ugly. We don't want to put a diff --git a/gold/symtab.cc b/gold/symtab.cc index 3c2a976..e3face7 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -67,6 +67,7 @@ Symbol::init_fields(const char* name, const char* version, this->has_plt_offset_ = false; this->has_warning_ = false; this->is_copied_from_dynobj_ = false; + this->needs_value_in_got_ = false; } // Initialize the fields in the base class Symbol for SYM in OBJECT. diff --git a/gold/symtab.h b/gold/symtab.h index 38320f9..2f43c4b 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -408,9 +408,75 @@ class Symbol return (this->visibility_ != elfcpp::STV_INTERNAL && this->visibility_ != elfcpp::STV_HIDDEN && this->visibility_ != elfcpp::STV_PROTECTED + && parameters->output_is_shared() && !parameters->symbolic()); } + // Return true if this symbol is a function that needs a PLT entry. + // If the symbol is defined in a dynamic object or if it is subject + // to pre-emption, we need to make a PLT entry. + bool + needs_plt_entry() const + { + return (this->type() == elfcpp::STT_FUNC + && (this->is_from_dynobj() || this->is_preemptible())); + } + + // Given a direct absolute or pc-relative static relocation against + // the global symbol, this function returns whether a dynamic relocation + // is needed. + + bool + needs_dynamic_reloc(bool is_absolute_ref, bool is_function_call) const + { + // An absolute reference within a position-independent output file + // will need a dynamic relocaion. + if (is_absolute_ref && parameters->output_is_position_independent()) + return true; + + // A function call that can branch to a local PLT entry does not need + // a dynamic relocation. + if (is_function_call && this->has_plt_offset()) + return false; + + // A reference to any PLT entry in a non-position-independent executable + // does not need a dynamic relocation. + if (!parameters->output_is_position_independent() + && this->has_plt_offset()) + return false; + + // A reference to a symbol defined in a dynamic object or to a + // symbol that is preemptible will need a dynamic relocation. + if (this->is_from_dynobj() || this->is_preemptible()) + return true; + + // For all other cases, return FALSE. + return false; + } + + // Given a direct absolute static relocation against + // the global symbol, where a dynamic relocation is needed, this + // function returns whether a relative dynamic relocation can be used. + // The caller must determine separately whether the static relocation + // is compatible with a relative relocation. + + bool + can_use_relative_reloc(bool is_function_call) const + { + // A function call that can branch to a local PLT entry can + // use a RELATIVE relocation. + if (is_function_call && this->has_plt_offset()) + return true; + + // A reference to a symbol defined in a dynamic object or to a + // symbol that is preemptible can not use a RELATIVE relocaiton. + if (this->is_from_dynobj() || this->is_preemptible()) + return false; + + // For all other cases, return TRUE. + return true; + } + // Return whether there should be a warning for references to this // symbol. bool @@ -433,6 +499,19 @@ class Symbol set_is_copied_from_dynobj() { this->is_copied_from_dynobj_ = true; } + // Mark this symbol as needing its value written to the GOT even when + // the value is subject to dynamic relocation (e.g., when the target + // uses a RELATIVE relocation for the GOT entry). + void + set_needs_value_in_got() + { this->needs_value_in_got_ = true; } + + // Return whether this symbol needs its value written to the GOT even + // when the value is subject to dynamic relocation. + bool + needs_value_in_got() const + { return this->needs_value_in_got_; } + protected: // Instances of this class should always be created at a specific // size. @@ -587,6 +666,9 @@ class Symbol // True if we are using a COPY reloc for this symbol, so that the // real definition lives in a dynamic object. bool is_copied_from_dynobj_ : 1; + // True if the static value should be written to the GOT even + // when the final value is subject to dynamic relocation. + bool needs_value_in_got_ : 1; }; // The parts of a symbol which are size specific. Using a template diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 3be64cf..5bc74bf 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -250,6 +250,17 @@ class Target_x86_64 : public Sized_target<64, false> Reloc_section* rela_dyn_section(Layout*); + // Return true if the symbol may need a COPY relocation. + // References from an executable object to non-function symbols + // defined in a dynamic object may need a COPY relocation. + bool + may_need_copy_reloc(Symbol* gsym) + { + return (!parameters->output_is_shared() + && gsym->is_from_dynobj() + && gsym->type() != elfcpp::STT_FUNC); + } + // Copy a relocation against a global symbol. void copy_reloc(const General_options*, Symbol_table*, Layout*, @@ -734,6 +745,20 @@ Target_x86_64::Scan::local(const General_options&, break; case elfcpp::R_X86_64_64: + // If building a shared library (or a position-independent + // executable), we need to create a dynamic relocation for + // this location. The relocation applied at link time will + // apply the link-time value, so we flag the location with + // an R_386_RELATIVE relocation so the dynamic loader can + // relocate it easily. + if (parameters->output_is_position_independent()) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, + data_shndx, reloc.get_r_offset(), 0); + } + break; + case elfcpp::R_X86_64_32: case elfcpp::R_X86_64_32S: case elfcpp::R_X86_64_16: @@ -747,16 +772,10 @@ Target_x86_64::Scan::local(const General_options&, if (parameters->output_is_position_independent()) { Reloc_section* rela_dyn = target->rela_dyn_section(layout); - if (r_type == elfcpp::R_X86_64_64) - rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, - data_shndx, reloc.get_r_offset(), 0); - else - { - unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info()); - rela_dyn->add_local(object, r_sym, r_type, data_shndx, - reloc.get_r_offset(), - reloc.get_r_addend()); - } + unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info()); + rela_dyn->add_local(object, r_sym, r_type, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend()); } break; @@ -915,81 +934,75 @@ Target_x86_64::Scan::global(const General_options& options, break; case elfcpp::R_X86_64_64: - case elfcpp::R_X86_64_PC64: case elfcpp::R_X86_64_32: case elfcpp::R_X86_64_32S: - case elfcpp::R_X86_64_PC32: case elfcpp::R_X86_64_16: - case elfcpp::R_X86_64_PC16: case elfcpp::R_X86_64_8: - case elfcpp::R_X86_64_PC8: { - bool is_pcrel = (r_type == elfcpp::R_X86_64_PC64 - || r_type == elfcpp::R_X86_64_PC32 - || r_type == elfcpp::R_X86_64_PC16 - || r_type == elfcpp::R_X86_64_PC8); - - if (gsym->is_from_dynobj() - || (parameters->output_is_shared() - && gsym->is_preemptible())) - { - // (a) This symbol is defined in a dynamic object. If it is a - // function, we make a PLT entry. Otherwise we need to - // either generate a COPY reloc or copy this reloc. - // (b) We are building a shared object and this symbol is - // preemptible. If it is a function, we make a PLT entry. - // Otherwise, we copy the reloc. - if (gsym->type() == elfcpp::STT_FUNC) - { - target->make_plt_entry(symtab, layout, gsym); - - // If this is not a PC relative reference, then we may - // be taking the address of the function. In that case - // we need to set the entry in the dynamic symbol table - // to the address of the PLT entry. We will also need to - // create a dynamic relocation. - if (!is_pcrel) - { - if (gsym->is_from_dynobj()) - gsym->set_needs_dynsym_value(); - if (parameters->output_is_position_independent()) - { - Reloc_section* rela_dyn = - target->rela_dyn_section(layout); - rela_dyn->add_global(gsym, r_type, object, data_shndx, - reloc.get_r_offset(), - reloc.get_r_addend()); - } - } - } - else if (parameters->output_is_shared()) - { - // We do not make COPY relocs in shared objects. + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + { + target->make_plt_entry(symtab, layout, gsym); + // Since this is not a PC-relative relocation, we may be + // taking the address of a function. In that case we need to + // set the entry in the dynamic symbol table to the address of + // the PLT entry. + if (gsym->is_from_dynobj()) + gsym->set_needs_dynsym_value(); + } + // Make a dynamic relocation if necessary. + if (gsym->needs_dynamic_reloc(true, false)) + { + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(&options, symtab, layout, object, data_shndx, + gsym, reloc); + } + else if (r_type == elfcpp::R_X86_64_64 + && gsym->can_use_relative_reloc(false)) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, + data_shndx, + reloc.get_r_offset(), 0); + } + else + { Reloc_section* rela_dyn = target->rela_dyn_section(layout); rela_dyn->add_global(gsym, r_type, object, data_shndx, reloc.get_r_offset(), reloc.get_r_addend()); - } - else - target->copy_reloc(&options, symtab, layout, object, data_shndx, - gsym, reloc); - } - else if (!is_pcrel && parameters->output_is_position_independent()) + } + } + } + break; + + case elfcpp::R_X86_64_PC64: + case elfcpp::R_X86_64_PC32: + case elfcpp::R_X86_64_PC16: + case elfcpp::R_X86_64_PC8: + { + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + target->make_plt_entry(symtab, layout, gsym); + // Make a dynamic relocation if necessary. + bool is_function_call = (gsym->type() == elfcpp::STT_FUNC); + if (gsym->needs_dynamic_reloc(true, is_function_call)) { - // This is not a PC-relative reference, so we need to generate - // a dynamic relocation. At this point, we know the symbol - // is not preemptible, so we can use the RELATIVE relocation. - Reloc_section* rela_dyn = target->rela_dyn_section(layout); - if (r_type == elfcpp::R_X86_64_64) - rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, - data_shndx, - reloc.get_r_offset(), 0); + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(&options, symtab, layout, object, data_shndx, + gsym, reloc); + } else - rela_dyn->add_global(gsym, r_type, object, data_shndx, - reloc.get_r_offset(), - reloc.get_r_addend()); + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_global(gsym, r_type, object, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend()); + } } - } + } break; case elfcpp::R_X86_64_GOT64: @@ -1007,8 +1020,16 @@ Target_x86_64::Scan::global(const General_options& options, if (!gsym->final_value_is_known()) { Reloc_section* rela_dyn = target->rela_dyn_section(layout); - rela_dyn->add_global(gsym, elfcpp::R_X86_64_GLOB_DAT, got, - gsym->got_offset(), 0); + if (gsym->is_preemptible()) + rela_dyn->add_global(gsym, elfcpp::R_X86_64_GLOB_DAT, got, + gsym->got_offset(), 0); + else + { + rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, + got, gsym->got_offset(), 0); + // Make sure we write the link-time value to the GOT. + gsym->set_needs_value_in_got(); + } } } // For GOTPLT64, we also need a PLT entry (but only if the -- 2.7.4