Add support for COPY relocations. 2011-04-26 Cary Coutant * copy-relocs.cc (Copy_relocs::copy_reloc): Call make_copy_reloc instead of emit_copy_reloc. (Copy_relocs::emit_copy_reloc): Refactor. (Copy_relocs::make_copy_reloc): New function. (Copy_relocs::add_copy_reloc): Remove. * copy-relocs.h (Copy_relocs::emit_copy_reloc): Move to public section. (Copy_relocs::make_copy_reloc): New function. (Copy_relocs::add_copy_reloc): Remove. * gold.cc (queue_middle_tasks): Emit old COPY relocations from unchanged input files. * incremental-dump.cc (dump_incremental_inputs): Print "COPY" flag. * incremental.cc (Sized_incremental_binary::do_reserve_layout): Reserve BSS space for COPY relocations. (Sized_incremental_binary::do_emit_copy_relocs): New function. (Output_section_incremental_inputs::write_info_blocks): Record whether a symbol is copied from a shared object. (Sized_incr_dynobj::do_add_symbols): Record COPY relocations. * incremental.h (Incremental_input_entry_reader::get_output_symbol_index): Add is_copy parameter. (Incremental_binary::emit_copy_relocs): New function. (Incremental_binary::do_emit_copy_relocs): New function. (Sized_incremental_binary::Sized_incremental_binary): Initialize new data member. (Sized_incremental_binary::add_copy_reloc): New function. (Sized_incremental_binary::do_emit_copy_relocs): New function. (Sized_incremental_binary::Copy_reloc): New struct. (Sized_incremental_binary::Copy_relocs): New typedef. (Sized_incremental_binary::copy_relocs_): New data member. * symtab.cc (Symbol_table::add_from_incrobj): Change return type. * symtab.h (Symbol_table::add_from_incrobj): Change return type. * target.h (Sized_target::emit_copy_reloc): New function. * x86_64.cc (Target_x86_64::emit_copy_reloc): New function. diff --git a/gold/copy-relocs.cc b/gold/copy-relocs.cc index 4931aa0..b71dc87 100644 --- a/gold/copy-relocs.cc +++ b/gold/copy-relocs.cc @@ -65,7 +65,7 @@ Copy_relocs::copy_reloc( Output_data_reloc* reloc_section) { if (this->need_copy_reloc(sym, object, shndx)) - this->emit_copy_reloc(symtab, layout, sym, reloc_section); + this->make_copy_reloc(symtab, layout, sym, reloc_section); else { // We may not need a COPY relocation. Save this relocation to @@ -106,6 +106,24 @@ template void Copy_relocs::emit_copy_reloc( Symbol_table* symtab, + Sized_symbol* sym, + Output_data* posd, + off_t offset, + Output_data_reloc* reloc_section) +{ + // Define the symbol as being copied. + symtab->define_with_copy_reloc(sym, posd, offset); + + // Add the COPY relocation to the dynamic reloc section. + reloc_section->add_global(sym, this->copy_reloc_type_, posd, offset, 0); +} + +// Make a COPY relocation for SYM and emit it. + +template +void +Copy_relocs::make_copy_reloc( + Symbol_table* symtab, Layout* layout, Sized_symbol* sym, Output_data_reloc* reloc_section) @@ -164,24 +182,7 @@ Copy_relocs::emit_copy_reloc( section_size_type offset = dynbss_size; dynbss->set_current_data_size(dynbss_size + symsize); - // Define the symbol as being copied. - symtab->define_with_copy_reloc(sym, dynbss, offset); - - // Add the COPY relocation to the dynamic reloc section. - this->add_copy_reloc(sym, offset, reloc_section); -} - -// Add a COPY relocation for SYM to RELOC_SECTION. - -template -void -Copy_relocs::add_copy_reloc( - Symbol* sym, - section_size_type offset, - Output_data_reloc* reloc_section) -{ - reloc_section->add_global(sym, this->copy_reloc_type_, this->dynbss_, - offset, 0); + this->emit_copy_reloc(symtab, sym, dynbss, offset, reloc_section); } // Save a relocation to possibly be emitted later. diff --git a/gold/copy-relocs.h b/gold/copy-relocs.h index 2fe6a24..908bcdb 100644 --- a/gold/copy-relocs.h +++ b/gold/copy-relocs.h @@ -81,6 +81,12 @@ class Copy_relocs void emit(Output_data_reloc*); + // Emit a COPY reloc. + void + emit_copy_reloc(Symbol_table*, Sized_symbol*, + Output_data*, off_t, + Output_data_reloc*); + private: typedef typename elfcpp::Elf_types::Elf_Addr Address; typedef typename elfcpp::Elf_types::Elf_Addr Addend; @@ -126,16 +132,11 @@ class Copy_relocs Sized_relobj* object, unsigned int shndx) const; - // Emit a COPY reloc. + // Make a new COPY reloc and emit it. void - emit_copy_reloc(Symbol_table*, Layout*, Sized_symbol*, + make_copy_reloc(Symbol_table*, Layout*, Sized_symbol*, Output_data_reloc*); - // Add a COPY reloc to the dynamic reloc section. - void - add_copy_reloc(Symbol*, section_size_type, - Output_data_reloc*); - // Save a reloc against SYM for possible emission later. void save(Symbol*, Sized_relobj*, unsigned int shndx, diff --git a/gold/gold.cc b/gold/gold.cc index 85b2907..283cab3 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -632,11 +632,13 @@ queue_middle_tasks(const General_options& options, } } - // For incremental updates, record the existing GOT and PLT entries. + // For incremental updates, record the existing GOT and PLT entries, + // and the COPY relocations. if (parameters->incremental_update()) { Incremental_binary* ibase = layout->incremental_base(); ibase->process_got_plt(symtab, layout); + ibase->emit_copy_relocs(symtab); } if (is_debugging_enabled(DEBUG_SCRIPT)) diff --git a/gold/incremental-dump.cc b/gold/incremental-dump.cc index 3e9bc0a..1147d07 100644 --- a/gold/incremental-dump.cc +++ b/gold/incremental-dump.cc @@ -293,8 +293,9 @@ dump_incremental_inputs(const char* argv0, const char* filename, for (unsigned int symndx = 0; symndx < nsyms; ++symndx) { bool is_def; + bool is_copy; unsigned int output_symndx = - input_file.get_output_symbol_index(symndx, &is_def); + input_file.get_output_symbol_index(symndx, &is_def, &is_copy); sym_p = symtab_view.data() + output_symndx * sym_size; elfcpp::Sym sym(sym_p); const char* symname; @@ -303,7 +304,7 @@ dump_incremental_inputs(const char* argv0, const char* filename, printf(" %6d %6s %8s %8s %8s %8s %-5s %s\n", output_symndx, "", "", "", "", "", - is_def ? "DEF" : "UNDEF", + is_copy ? "COPY" : (is_def ? "DEF" : "UNDEF"), symname); } } diff --git a/gold/incremental.cc b/gold/incremental.cc index 072a1fb..dca3431 100644 --- a/gold/incremental.cc +++ b/gold/incremental.cc @@ -22,6 +22,7 @@ #include "gold.h" +#include #include #include "libiberty.h" @@ -518,11 +519,45 @@ void Sized_incremental_binary::do_reserve_layout( unsigned int input_file_index) { + const int sym_size = elfcpp::Elf_sizes::sym_size; + Input_entry_reader input_file = this->inputs_reader_.input_file(input_file_index); if (input_file.type() == INCREMENTAL_INPUT_SHARED_LIBRARY) - return; + { + // Reserve the BSS space used for COPY relocations. + unsigned int nsyms = input_file.get_global_symbol_count(); + Incremental_binary::View symtab_view(NULL); + unsigned int symtab_count; + elfcpp::Elf_strtab strtab(NULL, 0); + this->get_symtab_view(&symtab_view, &symtab_count, &strtab); + for (unsigned int i = 0; i < nsyms; ++i) + { + bool is_def; + bool is_copy; + unsigned int output_symndx = + input_file.get_output_symbol_index(i, &is_def, &is_copy); + if (is_copy) + { + const unsigned char* sym_p = (symtab_view.data() + + output_symndx * sym_size); + elfcpp::Sym gsym(sym_p); + unsigned int shndx = gsym.get_st_shndx(); + if (shndx < 1 || shndx >= this->section_map_.size()) + continue; + Output_section* os = this->section_map_[shndx]; + off_t offset = gsym.get_st_value() - os->address(); + os->reserve(offset, gsym.get_st_size()); + gold_debug(DEBUG_INCREMENTAL, + "Reserve for COPY reloc: %s, off %d, size %d", + os->name(), + static_cast(offset), + static_cast(gsym.get_st_size())); + } + } + return; + } unsigned int shnum = input_file.get_input_section_count(); for (unsigned int i = 0; i < shnum; i++) @@ -616,6 +651,26 @@ Sized_incremental_binary::do_process_got_plt( } } +// Emit COPY relocations from the existing output file. + +template +void +Sized_incremental_binary::do_emit_copy_relocs( + Symbol_table* symtab) +{ + Sized_target* target = + parameters->sized_target(); + + for (typename Copy_relocs::iterator p = this->copy_relocs_.begin(); + p != this->copy_relocs_.end(); + ++p) + { + if (!(*p).symbol->is_copied_from_dynobj()) + target->emit_copy_reloc(symtab, (*p).symbol, (*p).output_section, + (*p).offset); + } +} + // Apply incremental relocations for symbols whose values have changed. template @@ -1537,7 +1592,9 @@ Output_section_incremental_inputs::write_info_blocks( == (*p)->get_info_offset()); Incremental_dynobj_entry* entry = (*p)->dynobj_entry(); gold_assert(entry != NULL); - const Object* obj = entry->object(); + Object* obj = entry->object(); + Dynobj* dynobj = obj->dynobj(); + gold_assert(dynobj != NULL); const Object::Symbols* syms = obj->get_global_symbols(); // Write the soname string table index. @@ -1566,7 +1623,10 @@ Output_section_incremental_inputs::write_info_blocks( if (sym->source() == Symbol::FROM_OBJECT && sym->object() == obj && sym->is_defined()) - def_flag = 1U << 31; + def_flag = 2U << 30; + else if (sym->is_copied_from_dynobj() + && this->symtab_->get_copy_source(sym) == dynobj) + def_flag = 3U << 30; Swap32::writeval(pov, sym->symtab_index() | def_flag); pov += 4; ++nsyms_out; @@ -2474,19 +2534,26 @@ Sized_incr_dynobj::do_add_symbols( unsigned int isym_count = isymtab.symbol_count(); unsigned int first_global = symtab_count - isym_count; + // We keep a set of symbols that we have generated COPY relocations + // for, indexed by the symbol value. We do not need more than one + // COPY relocation per address. + typedef typename std::set
Copied_symbols; + Copied_symbols copied_symbols; + const unsigned char* sym_p; for (unsigned int i = 0; i < nsyms; ++i) { bool is_def; + bool is_copy; unsigned int output_symndx = - this->input_reader_.get_output_symbol_index(i, &is_def); + this->input_reader_.get_output_symbol_index(i, &is_def, &is_copy); sym_p = symtab_view.data() + output_symndx * sym_size; elfcpp::Sym gsym(sym_p); const char* name; if (!strtab.get_c_string(gsym.get_st_name(), &name)) name = ""; - typename elfcpp::Elf_types::Elf_Addr v; + Address v; unsigned int shndx; elfcpp::STB st_bind = gsym.get_st_bind(); elfcpp::STT st_type = gsym.get_st_type(); @@ -2516,10 +2583,24 @@ Sized_incr_dynobj::do_add_symbols( osym.put_st_other(gsym.get_st_other()); osym.put_st_shndx(shndx); - this->symbols_[i] = - symtab->add_from_incrobj(this, name, NULL, &sym); + Sized_symbol* res = + symtab->add_from_incrobj(this, name, NULL, &sym); + this->symbols_[i] = res; this->ibase_->add_global_symbol(output_symndx - first_global, this->symbols_[i]); + + if (is_copy) + { + std::pair ins = + copied_symbols.insert(v); + if (ins.second) + { + unsigned int shndx = gsym.get_st_shndx(); + Output_section* os = this->ibase_->output_section(shndx); + off_t offset = v - os->address(); + this->ibase_->add_copy_reloc(this->symbols_[i], os, offset); + } + } } } diff --git a/gold/incremental.h b/gold/incremental.h index 998e69e..f5479ac 100644 --- a/gold/incremental.h +++ b/gold/incremental.h @@ -1012,17 +1012,19 @@ class Incremental_inputs_reader // Return the output symbol index for the Nth global symbol -- for shared // libraries only. Sets *IS_DEF to TRUE if the symbol is defined in this - // input file. + // input file. Sets *IS_COPY to TRUE if the symbol was copied from this + // input file with a COPY relocation. unsigned int - get_output_symbol_index(unsigned int n, bool* is_def) + get_output_symbol_index(unsigned int n, bool* is_def, bool* is_copy) { gold_assert(this->type() == INCREMENTAL_INPUT_SHARED_LIBRARY); const unsigned char* p = (this->inputs_->p_ + this->info_offset_ + 8 + n * 4); unsigned int output_symndx = Swap32::readval(p); - *is_def = (output_symndx & (1U << 31)) != 0; - return output_symndx & ((1U << 31) - 1); + *is_def = (output_symndx >> 31) == 1; + *is_copy = (output_symndx >> 30) == 3; + return output_symndx & ((1U << 30) - 1); } private: @@ -1397,6 +1399,11 @@ class Incremental_binary process_got_plt(Symbol_table* symtab, Layout* layout) { this->do_process_got_plt(symtab, layout); } + // Emit COPY relocations from the existing output file. + void + emit_copy_relocs(Symbol_table* symtab) + { this->do_emit_copy_relocs(symtab); } + // Apply incremental relocations for symbols whose values have changed. void apply_incremental_relocs(const Symbol_table* symtab, Layout* layout, @@ -1479,6 +1486,10 @@ class Incremental_binary virtual void do_process_got_plt(Symbol_table* symtab, Layout* layout) = 0; + // Emit COPY relocations from the existing output file. + virtual void + do_emit_copy_relocs(Symbol_table* symtab) = 0; + // Apply incremental relocations for symbols whose values have changed. virtual void do_apply_incremental_relocs(const Symbol_table*, Layout*, Output_file*) = 0; @@ -1514,9 +1525,9 @@ class Sized_incremental_binary : public Incremental_binary const elfcpp::Ehdr& ehdr, Target* target) : Incremental_binary(output, target), elf_file_(this, ehdr), - input_objects_(), section_map_(), symbol_map_(), main_symtab_loc_(), - main_strtab_loc_(), has_incremental_info_(false), inputs_reader_(), - symtab_reader_(), relocs_reader_(), got_plt_reader_(), + input_objects_(), section_map_(), symbol_map_(), copy_relocs_(), + main_symtab_loc_(), main_strtab_loc_(), has_incremental_info_(false), + inputs_reader_(), symtab_reader_(), relocs_reader_(), got_plt_reader_(), input_entry_readers_() { this->setup_readers(); } @@ -1558,6 +1569,11 @@ class Sized_incremental_binary : public Incremental_binary global_symbol(unsigned int symndx) const { return this->symbol_map_[symndx]; } + // Add a COPY relocation for a global symbol. + void + add_copy_reloc(Symbol* gsym, Output_section* os, off_t offset) + { this->copy_relocs_.push_back(Copy_reloc(gsym, os, offset)); } + // Readers for the incremental info sections. const Incremental_inputs_reader& @@ -1606,6 +1622,10 @@ class Sized_incremental_binary : public Incremental_binary virtual void do_process_got_plt(Symbol_table* symtab, Layout* layout); + // Emit COPY relocations from the existing output file. + virtual void + do_emit_copy_relocs(Symbol_table* symtab); + // Apply incremental relocations for symbols whose values have changed. virtual void do_apply_incremental_relocs(const Symbol_table* symtab, Layout* layout, @@ -1664,6 +1684,22 @@ class Sized_incremental_binary : public Incremental_binary } private: + // List of symbols that need COPY relocations. + struct Copy_reloc + { + Copy_reloc(Symbol* sym, Output_section* os, off_t off) + : symbol(sym), output_section(os), offset(off) + { } + + // The global symbol to copy. + Symbol* symbol; + // The output section into which the symbol was copied. + Output_section* output_section; + // The offset within that output section. + off_t offset; + }; + typedef std::vector Copy_relocs; + bool find_incremental_inputs_sections(unsigned int* p_inputs_shndx, unsigned int* p_symtab_shndx, @@ -1687,6 +1723,9 @@ class Sized_incremental_binary : public Incremental_binary // Map global symbols from the input file to the symbol table. std::vector symbol_map_; + // List of symbols that need COPY relocations. + Copy_relocs copy_relocs_; + // Locations of the main symbol table and symbol string table. Location main_symtab_loc_; Location main_strtab_loc_; diff --git a/gold/symtab.cc b/gold/symtab.cc index 3880ce1..0421b66 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -1489,7 +1489,7 @@ Symbol_table::add_from_dynobj( // Add a symbol from a incremental object file. template -Symbol* +Sized_symbol* Symbol_table::add_from_incrobj( Object* obj, const char* name, @@ -3453,7 +3453,7 @@ Symbol_table::add_from_dynobj<64, true>( #ifdef HAVE_TARGET_32_LITTLE template -Symbol* +Sized_symbol<32>* Symbol_table::add_from_incrobj( Object* obj, const char* name, @@ -3463,7 +3463,7 @@ Symbol_table::add_from_incrobj( #ifdef HAVE_TARGET_32_BIG template -Symbol* +Sized_symbol<32>* Symbol_table::add_from_incrobj( Object* obj, const char* name, @@ -3473,7 +3473,7 @@ Symbol_table::add_from_incrobj( #ifdef HAVE_TARGET_64_LITTLE template -Symbol* +Sized_symbol<64>* Symbol_table::add_from_incrobj( Object* obj, const char* name, @@ -3483,7 +3483,7 @@ Symbol_table::add_from_incrobj( #ifdef HAVE_TARGET_64_BIG template -Symbol* +Sized_symbol<64>* Symbol_table::add_from_incrobj( Object* obj, const char* name, diff --git a/gold/symtab.h b/gold/symtab.h index 4196138..e8d8238 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -1341,7 +1341,7 @@ class Symbol_table // Add one external symbol from the incremental object OBJ to the symbol // table. Returns a pointer to the resolved symbol in the symbol table. template - Symbol* + Sized_symbol* add_from_incrobj(Object* obj, const char* name, const char* ver, elfcpp::Sym* sym); diff --git a/gold/target.h b/gold/target.h index e675204..0731544 100644 --- a/gold/target.h +++ b/gold/target.h @@ -833,6 +833,13 @@ class Sized_target : public Target Symbol*) { gold_unreachable(); } + // Force a COPY relocation for a given symbol. + // A target needs to implement this to support incremental linking. + + virtual void + emit_copy_reloc(Symbol_table*, Symbol*, Output_section*, off_t) + { gold_unreachable(); } + // Apply an incremental relocation. virtual void diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 1a54fb0..7294e23 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -405,10 +405,13 @@ class Target_x86_64 : public Target_freebsd<64, false> unsigned int got_type); // Register an existing PLT entry for a global symbol. - // A target needs to implement this to support incremental linking. void register_global_plt_entry(unsigned int plt_index, Symbol* gsym); + // Force a COPY relocation for a given symbol. + void + emit_copy_reloc(Symbol_table*, Symbol*, Output_section*, off_t); + // Apply an incremental relocation. void apply_relocation(const Relocate_info<64, false>* relinfo, @@ -1387,6 +1390,19 @@ Target_x86_64::register_global_plt_entry(unsigned int plt_index, this->plt_->add_relocation(gsym, got_offset); } +// Force a COPY relocation for a given symbol. + +void +Target_x86_64::emit_copy_reloc( + Symbol_table* symtab, Symbol* sym, Output_section* os, off_t offset) +{ + this->copy_relocs_.emit_copy_reloc(symtab, + symtab->get_sized_symbol<64>(sym), + os, + offset, + this->rela_dyn_section(NULL)); +} + // Define the _TLS_MODULE_BASE_ symbol in the TLS segment. void