diff options
author | Ian Lance Taylor <iant@google.com> | 2007-12-07 19:32:59 +0000 |
---|---|---|
committer | Ian Lance Taylor <iant@google.com> | 2007-12-07 19:32:59 +0000 |
commit | e8c846c359fda086b5e550fc795cc1c05b6ee003 (patch) | |
tree | a7efff24a88e4874d11de980263ed408bd5d7a97 | |
parent | c68cf8ad105b1605718ea48e5f0f118b35be5654 (diff) | |
download | gdb-e8c846c359fda086b5e550fc795cc1c05b6ee003.zip gdb-e8c846c359fda086b5e550fc795cc1c05b6ee003.tar.gz gdb-e8c846c359fda086b5e550fc795cc1c05b6ee003.tar.bz2 |
From Cary Coutant: Fix handling of RELATIVE RELA relocs.
-rw-r--r-- | gold/i386.cc | 43 | ||||
-rw-r--r-- | gold/output.cc | 85 | ||||
-rw-r--r-- | gold/output.h | 135 | ||||
-rw-r--r-- | gold/symtab.cc | 1 | ||||
-rw-r--r-- | gold/symtab.h | 16 | ||||
-rw-r--r-- | gold/x86_64.cc | 31 |
6 files changed, 200 insertions, 111 deletions
diff --git a/gold/i386.cc b/gold/i386.cc index 071940b..a03893c 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -811,8 +811,10 @@ Target_i386::Scan::local(const General_options&, if (parameters->output_is_position_independent()) { Reloc_section* rel_dyn = target->rel_dyn_section(layout); - rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, output_section, - data_shndx, reloc.get_r_offset()); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + rel_dyn->add_local_relative(object, r_sym, elfcpp::R_386_RELATIVE, + output_section, data_shndx, + reloc.get_r_offset()); } break; @@ -860,8 +862,11 @@ Target_i386::Scan::local(const General_options&, if (parameters->output_is_position_independent()) { Reloc_section* rel_dyn = target->rel_dyn_section(layout); - rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, - got, object->local_got_offset(r_sym)); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + rel_dyn->add_local_relative(object, r_sym, + elfcpp::R_386_RELATIVE, + got, + object->local_got_offset(r_sym)); } } } @@ -955,9 +960,12 @@ Target_i386::Scan::local(const General_options&, && parameters->output_is_shared()) { Reloc_section* rel_dyn = target->rel_dyn_section(layout); - rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, - output_section, data_shndx, - reloc.get_r_offset()); + unsigned int r_sym + = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + rel_dyn->add_local_relative(object, r_sym, + elfcpp::R_386_RELATIVE, + output_section, data_shndx, + reloc.get_r_offset()); } // Create a GOT entry for the tp-relative offset. Output_data_got<32, false>* got @@ -1070,9 +1078,9 @@ Target_i386::Scan::global(const General_options& options, && 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, - output_section, data_shndx, - reloc.get_r_offset()); + rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE, + output_section, object, + data_shndx, reloc.get_r_offset()); } else { @@ -1136,12 +1144,8 @@ Target_i386::Scan::global(const General_options& options, else { if (got->add_global(gsym)) - { - 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(); - } + rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE, + got, gsym->got_offset()); } } } @@ -1264,9 +1268,10 @@ Target_i386::Scan::global(const General_options& options, && parameters->output_is_shared()) { Reloc_section* rel_dyn = target->rel_dyn_section(layout); - rel_dyn->add_local(object, 0, elfcpp::R_386_RELATIVE, - output_section, data_shndx, - reloc.get_r_offset()); + rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE, + output_section, object, + data_shndx, + reloc.get_r_offset()); } // Create a GOT entry for the tp-relative offset. Output_data_got<32, false>* got diff --git a/gold/output.cc b/gold/output.cc index 7fd901c..922a9e5 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -512,13 +512,14 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( Symbol* gsym, unsigned int type, Output_data* od, - Address address) + Address address, + bool is_relative) : address_(address), local_sym_index_(GSYM_CODE), type_(type), - shndx_(INVALID_CODE) + is_relative_(is_relative), shndx_(INVALID_CODE) { this->u1_.gsym = gsym; this->u2_.od = od; - if (dynamic) + if (dynamic && !is_relative) gsym->set_needs_dynsym_entry(); } @@ -528,14 +529,15 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( unsigned int type, Relobj* relobj, unsigned int shndx, - Address address) + Address address, + bool is_relative) : address_(address), local_sym_index_(GSYM_CODE), type_(type), - shndx_(shndx) + is_relative_(is_relative), shndx_(shndx) { gold_assert(shndx != INVALID_CODE); this->u1_.gsym = gsym; this->u2_.relobj = relobj; - if (dynamic) + if (dynamic && !is_relative) gsym->set_needs_dynsym_entry(); } @@ -547,15 +549,16 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( unsigned int local_sym_index, unsigned int type, Output_data* od, - Address address) + Address address, + bool is_relative) : address_(address), local_sym_index_(local_sym_index), type_(type), - shndx_(INVALID_CODE) + is_relative_(is_relative), shndx_(INVALID_CODE) { gold_assert(local_sym_index != GSYM_CODE && local_sym_index != INVALID_CODE); this->u1_.relobj = relobj; this->u2_.od = od; - if (dynamic && local_sym_index > 0) + if (dynamic && !is_relative) relobj->set_needs_output_dynsym_entry(local_sym_index); } @@ -565,16 +568,17 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( unsigned int local_sym_index, unsigned int type, unsigned int shndx, - Address address) + Address address, + bool is_relative) : address_(address), local_sym_index_(local_sym_index), type_(type), - shndx_(shndx) + is_relative_(is_relative), shndx_(shndx) { gold_assert(local_sym_index != GSYM_CODE && local_sym_index != INVALID_CODE); gold_assert(shndx != INVALID_CODE); this->u1_.relobj = relobj; this->u2_.relobj = relobj; - if (dynamic && local_sym_index > 0) + if (dynamic && !is_relative) relobj->set_needs_output_dynsym_entry(local_sym_index); } @@ -587,7 +591,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( Output_data* od, Address address) : address_(address), local_sym_index_(SECTION_CODE), type_(type), - shndx_(INVALID_CODE) + is_relative_(false), shndx_(INVALID_CODE) { this->u1_.os = os; this->u2_.od = od; @@ -603,7 +607,7 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( unsigned int shndx, Address address) : address_(address), local_sym_index_(SECTION_CODE), type_(type), - shndx_(shndx) + is_relative_(false), shndx_(shndx) { gold_assert(shndx != INVALID_CODE); this->u1_.os = os; @@ -685,8 +689,8 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write_rel( else if (this->u2_.od != NULL) address += this->u2_.od->address(); wr->put_r_offset(address); - wr->put_r_info(elfcpp::elf_r_info<size>(this->get_symbol_index(), - this->type_)); + unsigned int sym_index = this->is_relative_ ? 0 : this->get_symbol_index(); + wr->put_r_info(elfcpp::elf_r_info<size>(sym_index, this->type_)); } // Write out a Rel relocation. @@ -700,6 +704,24 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write( this->write_rel(&orel); } +// Get the value of the symbol referred to by a Rel relocation. + +template<bool dynamic, int size, bool big_endian> +typename elfcpp::Elf_types<size>::Elf_Addr +Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::symbol_value() const +{ + if (this->local_sym_index_ == GSYM_CODE) + { + const Sized_symbol<size>* sym; + sym = static_cast<const Sized_symbol<size>*>(this->u1_.gsym); + return sym->value(); + } + gold_assert(this->local_sym_index_ != SECTION_CODE + && this->local_sym_index_ != INVALID_CODE); + const Sized_relobj<size, big_endian>* relobj = this->u1_.relobj; + return relobj->local_symbol_value(this->local_sym_index_); +} + // Write out a Rela relocation. template<bool dynamic, int size, bool big_endian> @@ -709,7 +731,10 @@ Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>::write( { elfcpp::Rela_write<size, big_endian> orel(pov); this->rel_.write_rel(&orel); - orel.put_r_addend(this->addend_); + Addend addend = this->addend_; + if (rel_.is_relative()) + addend += rel_.symbol_value(); + orel.put_r_addend(addend); } // Output_data_reloc_base methods. @@ -775,24 +800,16 @@ Output_data_got<size, big_endian>::Got_entry::write(unsigned char* pov) const { case GSYM_CODE: { + // If the symbol is resolved locally, we need to write out the + // link-time value, which will be relocated dynamically by a + // RELATIVE relocation. Symbol* gsym = this->u_.gsym; - - // 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. 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<size>* sgsym; - // This cast is a bit ugly. We don't want to put a - // virtual method in Symbol, because we want Symbol to be - // as small as possible. - sgsym = static_cast<Sized_symbol<size>*>(gsym); - val = sgsym->value(); - } + Sized_symbol<size>* sgsym; + // This cast is a bit ugly. We don't want to put a + // virtual method in Symbol, because we want Symbol to be + // as small as possible. + sgsym = static_cast<Sized_symbol<size>*>(gsym); + val = sgsym->value(); } break; diff --git a/gold/output.h b/gold/output.h index 6c80d0a..172be49 100644 --- a/gold/output.h +++ b/gold/output.h @@ -703,20 +703,20 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> // A reloc against a global symbol. Output_reloc(Symbol* gsym, unsigned int type, Output_data* od, - Address address); + Address address, bool is_relative); Output_reloc(Symbol* gsym, unsigned int type, Relobj* relobj, - unsigned int shndx, Address address); + unsigned int shndx, Address address, bool is_relative); // A reloc against a local symbol. Output_reloc(Sized_relobj<size, big_endian>* relobj, unsigned int local_sym_index, unsigned int type, - Output_data* od, Address address); + Output_data* od, Address address, bool is_relative); Output_reloc(Sized_relobj<size, big_endian>* relobj, unsigned int local_sym_index, unsigned int type, - unsigned int shndx, Address address); + unsigned int shndx, Address address, bool is_relative); // A reloc against the STT_SECTION symbol of an output section. @@ -726,6 +726,16 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> Output_reloc(Output_section* os, unsigned int type, Relobj* relobj, unsigned int shndx, Address address); + // Return TRUE if this is a RELATIVE relocation. + bool + is_relative() const + { return this->is_relative_; } + + // Get the value of the symbol referred to by a Rel relocation. + + Address + symbol_value() const; + // Write the reloc entry to an output view. void write(unsigned char* pov) const; @@ -780,7 +790,9 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> // for a global symbol, or INVALID_CODE for an uninitialized value. unsigned int local_sym_index_; // The reloc type--a processor specific code. - unsigned int type_; + unsigned int type_ : 31; + // True if the relocation is a RELATIVE relocation. + bool is_relative_ : 1; // If the reloc address is an input section in an object, the // section index. This is INVALID_CODE if the reloc address is // specified in some other way. @@ -805,31 +817,31 @@ class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> // A reloc against a global symbol. Output_reloc(Symbol* gsym, unsigned int type, Output_data* od, - Address address, Addend addend) - : rel_(gsym, type, od, address), addend_(addend) + Address address, Addend addend, bool is_relative) + : rel_(gsym, type, od, address, is_relative), addend_(addend) { } Output_reloc(Symbol* gsym, unsigned int type, Relobj* relobj, - unsigned int shndx, Address address, Addend addend) - : rel_(gsym, type, relobj, shndx, address), addend_(addend) + unsigned int shndx, Address address, Addend addend, + bool is_relative) + : rel_(gsym, type, relobj, shndx, address, is_relative), addend_(addend) { } // A reloc against a local symbol. Output_reloc(Sized_relobj<size, big_endian>* relobj, - unsigned int local_sym_index, - unsigned int type, Output_data* od, Address address, - Addend addend) - : rel_(relobj, local_sym_index, type, od, address), addend_(addend) + unsigned int local_sym_index, unsigned int type, + Output_data* od, Address address, + Addend addend, bool is_relative) + : rel_(relobj, local_sym_index, type, od, address, is_relative), + addend_(addend) { } Output_reloc(Sized_relobj<size, big_endian>* relobj, - unsigned int local_sym_index, - unsigned int type, - unsigned int shndx, - Address address, - Addend addend) - : rel_(relobj, local_sym_index, type, shndx, address), + unsigned int local_sym_index, unsigned int type, + unsigned int shndx, Address address, + Addend addend, bool is_relative) + : rel_(relobj, local_sym_index, type, shndx, address, is_relative), addend_(addend) { } @@ -928,12 +940,27 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> void add_global(Symbol* gsym, unsigned int type, Output_data* od, Address address) - { this->add(od, Output_reloc_type(gsym, type, od, address)); } + { this->add(od, Output_reloc_type(gsym, type, od, address, false)); } void add_global(Symbol* gsym, unsigned int type, Output_data* od, Relobj* relobj, unsigned int shndx, Address address) - { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address)); } + { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address, + false)); } + + // Add a RELATIVE reloc against a global symbol. The final relocation + // will not reference the symbol. + + void + add_global_relative(Symbol* gsym, unsigned int type, Output_data* od, + Address address) + { this->add(od, Output_reloc_type(gsym, type, od, address, true)); } + + void + add_global_relative(Symbol* gsym, unsigned int type, Output_data* od, + Relobj* relobj, unsigned int shndx, Address address) + { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address, + true)); } // Add a reloc against a local symbol. @@ -942,15 +969,30 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> unsigned int local_sym_index, unsigned int type, Output_data* od, Address address) { this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, - address)); } + address, false)); } void add_local(Sized_relobj<size, big_endian>* relobj, unsigned int local_sym_index, unsigned int type, Output_data* od, unsigned int shndx, Address address) { this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx, - address)); } + address, false)); } + + // Add a RELATIVE reloc against a local symbol. + void + add_local_relative(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + Output_data* od, Address address) + { this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, + address, true)); } + + void + add_local_relative(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + Output_data* od, unsigned int shndx, Address address) + { this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx, + address, true)); } // A reloc against the STT_SECTION symbol of an output section. // OS is the Output_section that the relocation refers to; OD is @@ -991,14 +1033,32 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> void add_global(Symbol* gsym, unsigned int type, Output_data* od, Address address, Addend addend) - { this->add(od, Output_reloc_type(gsym, type, od, address, addend)); } + { this->add(od, Output_reloc_type(gsym, type, od, address, addend, + false)); } void add_global(Symbol* gsym, unsigned int type, Output_data* od, Relobj* relobj, unsigned int shndx, Address address, Addend addend) { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address, - addend)); } + addend, false)); } + + // Add a RELATIVE reloc against a global symbol. The final output + // relocation will not reference the symbol, but we must keep the symbol + // information long enough to set the addend of the relocation correctly + // when it is written. + + void + add_global_relative(Symbol* gsym, unsigned int type, Output_data* od, + Address address, Addend addend) + { this->add(od, Output_reloc_type(gsym, type, od, address, addend, true)); } + + void + add_global_relative(Symbol* gsym, unsigned int type, Output_data* od, + Relobj* relobj, unsigned int shndx, Address address, + Addend addend) + { this->add(od, Output_reloc_type(gsym, type, relobj, shndx, address, + addend, true)); } // Add a reloc against a local symbol. @@ -1008,7 +1068,7 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> Output_data* od, Address address, Addend addend) { this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, address, - addend)); + addend, false)); } void @@ -1018,7 +1078,28 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> Addend addend) { this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx, - address, addend)); + address, addend, false)); + } + + // Add a RELATIVE reloc against a local symbol. + + void + add_local_relative(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + Output_data* od, Address address, Addend addend) + { + this->add(od, Output_reloc_type(relobj, local_sym_index, type, od, address, + addend, true)); + } + + void + add_local_relative(Sized_relobj<size, big_endian>* relobj, + unsigned int local_sym_index, unsigned int type, + Output_data* od, unsigned int shndx, Address address, + Addend addend) + { + this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx, + address, addend, true)); } // A reloc against the STT_SECTION symbol of an output section. diff --git a/gold/symtab.cc b/gold/symtab.cc index a18d3ae..9187f32 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -70,7 +70,6 @@ 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; } // Return the demangled version of the symbol's name, but only diff --git a/gold/symtab.h b/gold/symtab.h index 6d2577b..a4f1106 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -541,19 +541,6 @@ 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. @@ -730,9 +717,6 @@ 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 cb68730..bdc28d4 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -765,10 +765,13 @@ Target_x86_64::Scan::local(const General_options&, // relocate it easily. if (parameters->output_is_position_independent()) { + unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info()); Reloc_section* rela_dyn = target->rela_dyn_section(layout); - rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, - output_section, data_shndx, - reloc.get_r_offset(), 0); + rela_dyn->add_local_relative(object, r_sym, + elfcpp::R_X86_64_RELATIVE, + output_section, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend()); } break; @@ -831,8 +834,10 @@ Target_x86_64::Scan::local(const General_options&, Reloc_section* rela_dyn = target->rela_dyn_section(layout); // R_X86_64_RELATIVE assumes a 64-bit relocation. if (r_type != elfcpp::R_X86_64_GOT32) - rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, - got, object->local_got_offset(r_sym), 0); + rela_dyn->add_local_relative(object, r_sym, + elfcpp::R_X86_64_RELATIVE, got, + object->local_got_offset(r_sym), + 0); else rela_dyn->add_local(object, r_sym, r_type, got, object->local_got_offset(r_sym), 0); @@ -1012,9 +1017,10 @@ Target_x86_64::Scan::global(const General_options& options, && 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, - output_section, data_shndx, - reloc.get_r_offset(), 0); + rela_dyn->add_global_relative(gsym, elfcpp::R_X86_64_RELATIVE, + output_section, object, + data_shndx, reloc.get_r_offset(), + reloc.get_r_addend()); } else { @@ -1076,12 +1082,9 @@ Target_x86_64::Scan::global(const General_options& options, else { if (got->add_global(gsym)) - { - 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(); - } + rela_dyn->add_global_relative(gsym, + elfcpp::R_X86_64_RELATIVE, + got, gsym->got_offset(), 0); } } // For GOTPLT64, we also need a PLT entry (but only if the |