From 4829d394dedd7953e0ee0cf540ffccc01936499d Mon Sep 17 00:00:00 2001 From: Cary Coutant Date: Mon, 23 May 2011 23:27:11 +0000 Subject: [PATCH] * gold.cc (queue_middle_tasks): Process existing GOT/PLT entries. * incremental-dump.cc (dump_incremental_inputs): Mask high-order bit when checking got_type. * incremental.cc (Sized_incremental_binary::setup_readers): Store symbol table and string table locations; initialize bit vector of file status flags. (Sized_incremental_binary::do_reserve_layout): Set bit flag for unchanged files. (Sized_incremental_binary::do_process_got_plt): New function. (Sized_incremental_binary::get_symtab_view): Use stored locations. (Output_section_incremental_inputs::set_final_data_size): Record file index for each input file. (Output_section_incremental_inputs::write_got_plt): Store file index instead of input entry offset for each GOT entry. * incremental.h (Incremental_input_entry::Incremental_input_entry): Initialize new data member. (Incremental_input_entry::set_offset): Store file index. (Incremental_input_entry::get_file_index): New function. (Incremental_input_entry::file_index_): New data member. (Incremental_binary::process_got_plt): New function. (Incremental_binary::do_process_got_plt): New function. (Sized_incremental_binary::Sized_incremental_binary): Initialize new data members. (Sized_incremental_binary::~Sized_incremental_binary): New destructor. (Sized_incremental_binary::set_file_is_unchanged): New function. (Sized_incremental_binary::file_is_unchanged): New function. (Sized_incremental_binary::do_process_got_plt): New function. (Sized_incremental_binary::file_status_): New data member. (Sized_incremental_binary::main_symtab_loc_): New data member. (Sized_incremental_binary::main_strtab_loc_): New data member. * output.cc (Output_data_got::Got_entry::write): Add case RESERVED_CODE. (Output_data_got::add_global): Call add_got_entry. (Output_data_got::add_global_plt): Likewise. (Output_data_got::add_global_with_rel): Likewise. (Output_data_got::add_global_with_rela): Likewise. (Output_data_got::add_global_pair_with_rel): Call add_got_entry_pair. (Output_data_got::add_global_pair_with_rela): Likewise. (Output_data_got::add_local): Call add_got_entry. (Output_data_got::add_local_plt): Likewise. (Output_data_got::add_local_with_rel): Likewise. (Output_data_got::add_local_with_rela): Likewise. (Output_data_got::add_local_pair_with_rel): Call add_got_entry_pair. (Output_data_got::add_local_pair_with_rela): Likewise. (Output_data_got::reserve_slot): New function. (Output_data_got::reserve_slot_for_global): New function. (Output_data_got::add_got_entry): New function. (Output_data_got::add_got_entry_pair): New function. (Output_section::add_output_section_data): Edit FIXME. * output.h (Output_section_data_build::Output_section_data_build): New constructor with size parameter. (Output_data_space::Output_data_space): Likewise. (Output_data_got::Output_data_got): Initialize new data member; new constructor with size parameter. (Output_data_got::add_constant): Call add_got_entry. (Output_data_got::reserve_slot): New function. (Output_data_got::reserve_slot_for_global): New function. (class Output_data_got::Got_entry): Add RESERVED_CODE. (Output_data_got::add_got_entry): New function. (Output_data_got::add_got_entry_pair): New function. (Output_data_got::free_list_): New data member. * target.h (Sized_target::init_got_plt_for_update): New function. (Sized_target::register_global_plt_entry): New function. * x86_64.cc (Output_data_plt_x86_64::Output_data_plt_x86_64): Initialize new data member; call init; add constructor with PLT count. (Output_data_plt_x86_64::init): New function. (Output_data_plt_x86_64::add_relocation): New function. (Output_data_plt_x86_64::reserve_slot): New function. (Output_data_plt_x86_64::free_list_): New data member. (Target_x86_64::init_got_plt_for_update): New function. (Target_x86_64::register_global_plt_entry): New function. (Output_data_plt_x86_64::add_entry): Allocate from free list for incremental updates. (Output_data_plt_x86_64::add_relocation): New function. * testsuite/object_unittest.cc (Object_test): Set default options. --- gold/ChangeLog | 80 +++++++++++++++ gold/gold.cc | 7 ++ gold/incremental-dump.cc | 2 +- gold/incremental.cc | 122 +++++++++++++++++++--- gold/incremental.h | 73 +++++++++++-- gold/output.cc | 173 ++++++++++++++++++++----------- gold/output.h | 54 ++++++++-- gold/target.h | 20 ++++ gold/testsuite/object_unittest.cc | 5 + gold/x86_64.cc | 208 +++++++++++++++++++++++++++++++++----- 10 files changed, 625 insertions(+), 119 deletions(-) diff --git a/gold/ChangeLog b/gold/ChangeLog index 255bc8a..315b670 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,83 @@ +2011-05-23 Cary Coutant + + * gold.cc (queue_middle_tasks): Process existing GOT/PLT entries. + * incremental-dump.cc (dump_incremental_inputs): Mask high-order + bit when checking got_type. + * incremental.cc (Sized_incremental_binary::setup_readers): + Store symbol table and string table locations; initialize bit vector + of file status flags. + (Sized_incremental_binary::do_reserve_layout): Set bit flag for + unchanged files. + (Sized_incremental_binary::do_process_got_plt): New function. + (Sized_incremental_binary::get_symtab_view): Use stored locations. + (Output_section_incremental_inputs::set_final_data_size): Record + file index for each input file. + (Output_section_incremental_inputs::write_got_plt): Store file index + instead of input entry offset for each GOT entry. + * incremental.h + (Incremental_input_entry::Incremental_input_entry): Initialize new + data member. + (Incremental_input_entry::set_offset): Store file index. + (Incremental_input_entry::get_file_index): New function. + (Incremental_input_entry::file_index_): New data member. + (Incremental_binary::process_got_plt): New function. + (Incremental_binary::do_process_got_plt): New function. + (Sized_incremental_binary::Sized_incremental_binary): Initialize new + data members. + (Sized_incremental_binary::~Sized_incremental_binary): New destructor. + (Sized_incremental_binary::set_file_is_unchanged): New function. + (Sized_incremental_binary::file_is_unchanged): New function. + (Sized_incremental_binary::do_process_got_plt): New function. + (Sized_incremental_binary::file_status_): New data member. + (Sized_incremental_binary::main_symtab_loc_): New data member. + (Sized_incremental_binary::main_strtab_loc_): New data member. + * output.cc (Output_data_got::Got_entry::write): Add case + RESERVED_CODE. + (Output_data_got::add_global): Call add_got_entry. + (Output_data_got::add_global_plt): Likewise. + (Output_data_got::add_global_with_rel): Likewise. + (Output_data_got::add_global_with_rela): Likewise. + (Output_data_got::add_global_pair_with_rel): Call add_got_entry_pair. + (Output_data_got::add_global_pair_with_rela): Likewise. + (Output_data_got::add_local): Call add_got_entry. + (Output_data_got::add_local_plt): Likewise. + (Output_data_got::add_local_with_rel): Likewise. + (Output_data_got::add_local_with_rela): Likewise. + (Output_data_got::add_local_pair_with_rel): Call add_got_entry_pair. + (Output_data_got::add_local_pair_with_rela): Likewise. + (Output_data_got::reserve_slot): New function. + (Output_data_got::reserve_slot_for_global): New function. + (Output_data_got::add_got_entry): New function. + (Output_data_got::add_got_entry_pair): New function. + (Output_section::add_output_section_data): Edit FIXME. + * output.h + (Output_section_data_build::Output_section_data_build): New + constructor with size parameter. + (Output_data_space::Output_data_space): Likewise. + (Output_data_got::Output_data_got): Initialize new data member; new + constructor with size parameter. + (Output_data_got::add_constant): Call add_got_entry. + (Output_data_got::reserve_slot): New function. + (Output_data_got::reserve_slot_for_global): New function. + (class Output_data_got::Got_entry): Add RESERVED_CODE. + (Output_data_got::add_got_entry): New function. + (Output_data_got::add_got_entry_pair): New function. + (Output_data_got::free_list_): New data member. + * target.h (Sized_target::init_got_plt_for_update): New function. + (Sized_target::register_global_plt_entry): New function. + * x86_64.cc (Output_data_plt_x86_64::Output_data_plt_x86_64): + Initialize new data member; call init; add constructor with PLT count. + (Output_data_plt_x86_64::init): New function. + (Output_data_plt_x86_64::add_relocation): New function. + (Output_data_plt_x86_64::reserve_slot): New function. + (Output_data_plt_x86_64::free_list_): New data member. + (Target_x86_64::init_got_plt_for_update): New function. + (Target_x86_64::register_global_plt_entry): New function. + (Output_data_plt_x86_64::add_entry): Allocate from free list for + incremental updates. + (Output_data_plt_x86_64::add_relocation): New function. + * testsuite/object_unittest.cc (Object_test): Set default options. + 2011-05-16 Ian Lance Taylor * options.h (class General_options): Make -i a synonym for -r. diff --git a/gold/gold.cc b/gold/gold.cc index 926e5fb..bf5ac89 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -630,6 +630,13 @@ queue_middle_tasks(const General_options& options, } } + // For incremental updates, record the existing GOT and PLT entries. + if (parameters->incremental_update()) + { + Incremental_binary* ibase = layout->incremental_base(); + ibase->process_got_plt(symtab, layout); + } + if (is_debugging_enabled(DEBUG_SCRIPT)) layout->script_options()->print(stderr); diff --git a/gold/incremental-dump.cc b/gold/incremental-dump.cc index 5ac6e6c..eecf8a8 100644 --- a/gold/incremental-dump.cc +++ b/gold/incremental-dump.cc @@ -370,7 +370,7 @@ dump_incremental_inputs(const char* argv0, const char* filename, unsigned int got_type = igot_plt.get_got_type(i); unsigned int got_desc = igot_plt.get_got_desc(i); printf("[%d] type %02x, ", i, got_type & 0x7f); - if (got_type == 0x7f) + if ((got_type & 0x7f) == 0x7f) printf("reserved"); else if (got_type & 0x80) { diff --git a/gold/incremental.cc b/gold/incremental.cc index 1250d70..b831b97 100644 --- a/gold/incremental.cc +++ b/gold/incremental.cc @@ -258,6 +258,19 @@ Sized_incremental_binary::setup_readers() this->got_plt_reader_ = Incremental_got_plt_reader(got_plt_view.data()); + // Find the main symbol table. + unsigned int main_symtab_shndx = + this->elf_file_.find_section_by_type(elfcpp::SHT_SYMTAB); + gold_assert(main_symtab_shndx != elfcpp::SHN_UNDEF); + this->main_symtab_loc_ = this->elf_file_.section_contents(main_symtab_shndx); + + // Find the main symbol string table. + unsigned int main_strtab_shndx = + this->elf_file_.section_link(main_symtab_shndx); + gold_assert(main_strtab_shndx != elfcpp::SHN_UNDEF + && main_strtab_shndx < this->elf_file_.shnum()); + this->main_strtab_loc_ = this->elf_file_.section_contents(main_strtab_shndx); + // Walk the list of input files (a) to setup an Input_reader for each // input file, and (b) to record maps of files added from archive // libraries and scripts. @@ -314,6 +327,10 @@ Sized_incremental_binary::setup_readers() unsigned int nglobals = this->symtab_reader_.symbol_count(); this->symbol_map_.resize(nglobals); + // Initialize the status of each input file. + this->file_status_ = new unsigned char[(count + 7) / 8]; + memset(this->file_status_, 0, (count + 7) / 8); + this->has_incremental_info_ = true; } @@ -507,6 +524,8 @@ Sized_incremental_binary::do_reserve_layout( Input_entry_reader input_file = this->inputs_reader_.input_file(input_file_index); + this->set_file_is_unchanged(input_file_index); + if (input_file.type() == INCREMENTAL_INPUT_SHARED_LIBRARY) return; @@ -523,6 +542,83 @@ Sized_incremental_binary::do_reserve_layout( } } +// Process the GOT and PLT entries from the existing output file. + +template +void +Sized_incremental_binary::do_process_got_plt( + Symbol_table* symtab, + Layout* layout) +{ + Incremental_got_plt_reader got_plt_reader(this->got_plt_reader()); + Sized_target* target = + parameters->sized_target(); + + // Get the number of symbols in the main symbol table and in the + // incremental symbol table. The difference between the two counts + // is the index of the first forced-local or global symbol in the + // main symbol table. + unsigned int symtab_count = + this->main_symtab_loc_.data_size / elfcpp::Elf_sizes::sym_size; + unsigned int isym_count = this->symtab_reader_.symbol_count(); + unsigned int first_global = symtab_count - isym_count; + + // Tell the target how big the GOT and PLT sections are. + unsigned int got_count = got_plt_reader.get_got_entry_count(); + unsigned int plt_count = got_plt_reader.get_plt_entry_count(); + Output_data_got* got = + target->init_got_plt_for_update(symtab, layout, got_count, plt_count); + + // Read the GOT entries from the base file and build the outgoing GOT. + for (unsigned int i = 0; i < got_count; ++i) + { + unsigned int got_type = got_plt_reader.get_got_type(i); + if ((got_type & 0x7f) == 0x7f) + { + // This is the second entry of a pair. + got->reserve_slot(i); + continue; + } + unsigned int got_desc = got_plt_reader.get_got_desc(i); + if (got_type & 0x80) + { + // This is an entry for a local symbol. GOT_DESC is the index + // of the object file entry in the list of input files. Ignore + // this entry if the object file was replaced. + gold_debug(DEBUG_INCREMENTAL, + "GOT entry %d, type %02x: (local symbol)", + i, got_type & 0x7f); + if (this->file_is_unchanged(got_desc)) + got->reserve_slot(i); + } + else + { + // This is an entry for a global symbol. GOT_DESC is the symbol + // table index. + // FIXME: This should really be a fatal error (corrupt input). + gold_assert(got_desc >= first_global && got_desc < symtab_count); + Symbol* sym = this->global_symbol(got_desc - first_global); + gold_debug(DEBUG_INCREMENTAL, + "GOT entry %d, type %02x: %s", + i, got_type, sym->name()); + got->reserve_slot_for_global(i, sym, got_type); + } + } + + // Read the PLT entries from the base file and pass each to the target. + for (unsigned int i = 0; i < plt_count; ++i) + { + unsigned int plt_desc = got_plt_reader.get_plt_desc(i); + // FIXME: This should really be a fatal error (corrupt input). + gold_assert(plt_desc >= first_global && plt_desc < symtab_count); + Symbol* sym = this->global_symbol(plt_desc - first_global); + gold_debug(DEBUG_INCREMENTAL, + "PLT entry %d: %s", + i, sym->name()); + target->register_global_plt_entry(i, sym); + } +} + // Apply incremental relocations for symbols whose values have changed. template @@ -628,20 +724,12 @@ Sized_incremental_binary::get_symtab_view( unsigned int* nsyms, elfcpp::Elf_strtab* strtab) { - unsigned int symtab_shndx = - this->elf_file_.find_section_by_type(elfcpp::SHT_SYMTAB); - gold_assert(symtab_shndx != elfcpp::SHN_UNDEF); - Location symtab_location(this->elf_file_.section_contents(symtab_shndx)); - *symtab_view = this->view(symtab_location); - *nsyms = symtab_location.data_size / elfcpp::Elf_sizes::sym_size; + *symtab_view = this->view(this->main_symtab_loc_); + *nsyms = this->main_symtab_loc_.data_size / elfcpp::Elf_sizes::sym_size; - unsigned int strtab_shndx = this->elf_file_.section_link(symtab_shndx); - gold_assert(strtab_shndx != elfcpp::SHN_UNDEF - && strtab_shndx < this->elf_file_.shnum()); - - Location strtab_location(this->elf_file_.section_contents(strtab_shndx)); - View strtab_view(this->view(strtab_location)); - *strtab = elfcpp::Elf_strtab(strtab_view.data(), strtab_location.data_size); + View strtab_view(this->view(this->main_strtab_loc_)); + *strtab = elfcpp::Elf_strtab(strtab_view.data(), + this->main_strtab_loc_.data_size); } namespace @@ -1022,6 +1110,7 @@ Output_section_incremental_inputs::set_final_data_size() unsigned int input_offset = this->header_size; // Offset of each supplemental info block. + unsigned int file_index = 0; unsigned int info_offset = this->header_size; info_offset += this->input_entry_size * inputs->input_file_count(); @@ -1031,8 +1120,9 @@ Output_section_incremental_inputs::set_final_data_size() p != inputs->input_files().end(); ++p) { - // Set the offset of the input file entry. - (*p)->set_offset(input_offset); + // Set the index and offset of the input file entry. + (*p)->set_offset(file_index, input_offset); + ++file_index; input_offset += this->input_entry_size; // Set the offset of the supplemental info block. @@ -1655,7 +1745,7 @@ Output_section_incremental_inputs::write_got_plt( gold_assert(entry != NULL); const Object* obj = entry->object(); gold_assert(obj != NULL); - view_info.got_descriptor = (*p)->get_offset(); + view_info.got_descriptor = (*p)->get_file_index(); Got_visitor v(view_info); obj->for_all_local_got_entries(&v); } diff --git a/gold/incremental.h b/gold/incremental.h index 41ae188..5c0f937 100644 --- a/gold/incremental.h +++ b/gold/incremental.h @@ -79,7 +79,7 @@ class Incremental_input_entry public: Incremental_input_entry(Stringpool::Key filename_key, unsigned int arg_serial, Timespec mtime) - : filename_key_(filename_key), offset_(0), info_offset_(0), + : filename_key_(filename_key), file_index_(0), offset_(0), info_offset_(0), arg_serial_(arg_serial), mtime_(mtime), is_in_system_directory_(false) { } @@ -92,16 +92,24 @@ class Incremental_input_entry type() const { return this->do_type(); } - // Set the section offset of this input file entry. + // Set the index and section offset of this input file entry. void - set_offset(unsigned int offset) - { this->offset_ = offset; } + set_offset(unsigned int file_index, unsigned int offset) + { + this->file_index_ = file_index; + this->offset_ = offset; + } // Set the section offset of the supplemental information for this entry. void set_info_offset(unsigned int info_offset) { this->info_offset_ = info_offset; } + // Get the index of this input file entry. + unsigned int + get_file_index() const + { return this->file_index_; } + // Get the section offset of this input file entry. unsigned int get_offset() const @@ -182,6 +190,9 @@ class Incremental_input_entry // Key of the filename string in the section stringtable. Stringpool::Key filename_key_; + // Index of the entry in the output section. + unsigned int file_index_; + // Offset of the entry in the output section. unsigned int offset_; @@ -1235,6 +1246,11 @@ class Incremental_binary reserve_layout(unsigned int input_file_index) { this->do_reserve_layout(input_file_index); } + // Process the GOT and PLT entries from the existing output file. + void + process_got_plt(Symbol_table* symtab, Layout* layout) + { this->do_process_got_plt(symtab, layout); } + // Apply incremental relocations for symbols whose values have changed. void apply_incremental_relocs(const Symbol_table* symtab, Layout* layout, @@ -1313,6 +1329,10 @@ class Incremental_binary virtual void do_reserve_layout(unsigned int input_file_index) = 0; + // Process the GOT and PLT entries from the existing output file. + virtual void + do_process_got_plt(Symbol_table* symtab, Layout* layout) = 0; + // Apply incremental relocations for symbols whose values have changed. virtual void do_apply_incremental_relocs(const Symbol_table*, Layout*, Output_file*) = 0; @@ -1345,29 +1365,53 @@ class Sized_incremental_binary : public Incremental_binary const elfcpp::Ehdr& ehdr, Target* target) : Incremental_binary(output, target), elf_file_(this, ehdr), - section_map_(), symbol_map_(), has_incremental_info_(false), - inputs_reader_(), symtab_reader_(), relocs_reader_(), got_plt_reader_(), + file_status_(NULL), section_map_(), symbol_map_(), 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(); } + virtual + ~Sized_incremental_binary() + { + if (this->file_status_ != NULL) + delete[] this->file_status_; + } + // Returns TRUE if the file contains incremental info. bool has_incremental_info() const { return this->has_incremental_info_; } + // Set the flag for input file N to indicate that the file is unchanged. + void + set_file_is_unchanged(unsigned int n) + { + gold_assert(this->file_status_ != NULL); + this->file_status_[n / 8] |= 1U << (n % 8); + } + + // Returns TRUE if input file N is unchanged. + bool + file_is_unchanged(unsigned int n) const + { + gold_assert(this->file_status_ != NULL); + return (this->file_status_[n / 8] & (1U << (n % 8))) != 0; + } + // Return the Output_section for section index SHNDX. Output_section* output_section(unsigned int shndx) { return this->section_map_[shndx]; } - // Map a symbol table entry from the input file to the output symbol table. + // Map a symbol table entry from the base file to the output symbol table. // SYMNDX is relative to the first forced-local or global symbol in the // input file symbol table. void add_global_symbol(unsigned int symndx, Symbol* gsym) { this->symbol_map_[symndx] = gsym; } - // Map a symbol table entry from the input file to the output symbol table. + // Map a symbol table entry from the base file to the output symbol table. // SYMNDX is relative to the first forced-local or global symbol in the // input file symbol table. Symbol* @@ -1418,6 +1462,10 @@ class Sized_incremental_binary : public Incremental_binary virtual void do_reserve_layout(unsigned int input_file_index); + // Process the GOT and PLT entries from the existing output file. + virtual void + do_process_got_plt(Symbol_table* symtab, Layout* layout); + // Apply incremental relocations for symbols whose values have changed. virtual void do_apply_incremental_relocs(const Symbol_table* symtab, Layout* layout, @@ -1489,12 +1537,21 @@ class Sized_incremental_binary : public Incremental_binary // Output as an ELF file. elfcpp::Elf_file elf_file_; + // Status flags for each input file. Each bit represents one input file; + // 0 indicates that the file was replaced; 1 indicates that the file was + // unchanged. + unsigned char* file_status_; + // Map section index to an Output_section in the updated layout. std::vector section_map_; // Map global symbols from the input file to the symbol table. std::vector symbol_map_; + // Locations of the main symbol table and symbol string table. + Location main_symtab_loc_; + Location main_strtab_loc_; + // Readers for the incremental info sections. bool has_incremental_info_; Incremental_inputs_reader inputs_reader_; diff --git a/gold/output.cc b/gold/output.cc index 70379bb..c3053db 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -1358,6 +1358,13 @@ Output_data_got::Got_entry::write(unsigned char* pov) const val = this->u_.constant; break; + case RESERVED_CODE: + // If we're doing an incremental update, don't touch this GOT entry. + if (parameters->incremental_update()) + return; + val = this->u_.constant; + break; + default: { const Sized_relobj* object = this->u_.object; @@ -1393,9 +1400,8 @@ Output_data_got::add_global( if (gsym->has_got_offset(got_type)) return false; - this->entries_.push_back(Got_entry(gsym, false)); - this->set_got_size(); - gsym->set_got_offset(got_type, this->last_got_offset()); + unsigned int got_offset = this->add_got_entry(Got_entry(gsym, false)); + gsym->set_got_offset(got_type, got_offset); return true; } @@ -1409,9 +1415,8 @@ Output_data_got::add_global_plt(Symbol* gsym, if (gsym->has_got_offset(got_type)) return false; - this->entries_.push_back(Got_entry(gsym, true)); - this->set_got_size(); - gsym->set_got_offset(got_type, this->last_got_offset()); + unsigned int got_offset = this->add_got_entry(Got_entry(gsym, true)); + gsym->set_got_offset(got_type, got_offset); return true; } @@ -1429,9 +1434,7 @@ Output_data_got::add_global_with_rel( if (gsym->has_got_offset(got_type)) return; - this->entries_.push_back(Got_entry()); - this->set_got_size(); - unsigned int got_offset = this->last_got_offset(); + unsigned int got_offset = this->add_got_entry(Got_entry()); gsym->set_got_offset(got_type, got_offset); rel_dyn->add_global(gsym, r_type, this, got_offset); } @@ -1447,9 +1450,7 @@ Output_data_got::add_global_with_rela( if (gsym->has_got_offset(got_type)) return; - this->entries_.push_back(Got_entry()); - this->set_got_size(); - unsigned int got_offset = this->last_got_offset(); + unsigned int got_offset = this->add_got_entry(Got_entry()); gsym->set_got_offset(got_type, got_offset); rela_dyn->add_global(gsym, r_type, this, got_offset, 0); } @@ -1469,19 +1470,12 @@ Output_data_got::add_global_pair_with_rel( if (gsym->has_got_offset(got_type)) return; - this->entries_.push_back(Got_entry()); - unsigned int got_offset = this->last_got_offset(); + unsigned int got_offset = this->add_got_entry_pair(Got_entry(), Got_entry()); gsym->set_got_offset(got_type, got_offset); rel_dyn->add_global(gsym, r_type_1, this, got_offset); - this->entries_.push_back(Got_entry()); if (r_type_2 != 0) - { - got_offset = this->last_got_offset(); - rel_dyn->add_global(gsym, r_type_2, this, got_offset); - } - - this->set_got_size(); + rel_dyn->add_global(gsym, r_type_2, this, got_offset + size / 8); } template @@ -1496,19 +1490,12 @@ Output_data_got::add_global_pair_with_rela( if (gsym->has_got_offset(got_type)) return; - this->entries_.push_back(Got_entry()); - unsigned int got_offset = this->last_got_offset(); + unsigned int got_offset = this->add_got_entry_pair(Got_entry(), Got_entry()); gsym->set_got_offset(got_type, got_offset); rela_dyn->add_global(gsym, r_type_1, this, got_offset, 0); - this->entries_.push_back(Got_entry()); if (r_type_2 != 0) - { - got_offset = this->last_got_offset(); - rela_dyn->add_global(gsym, r_type_2, this, got_offset, 0); - } - - this->set_got_size(); + rela_dyn->add_global(gsym, r_type_2, this, got_offset + size / 8, 0); } // Add an entry for a local symbol to the GOT. This returns true if @@ -1525,9 +1512,9 @@ Output_data_got::add_local( if (object->local_has_got_offset(symndx, got_type)) return false; - this->entries_.push_back(Got_entry(object, symndx, false)); - this->set_got_size(); - object->set_local_got_offset(symndx, got_type, this->last_got_offset()); + unsigned int got_offset = this->add_got_entry(Got_entry(object, symndx, + false)); + object->set_local_got_offset(symndx, got_type, got_offset); return true; } @@ -1543,9 +1530,9 @@ Output_data_got::add_local_plt( if (object->local_has_got_offset(symndx, got_type)) return false; - this->entries_.push_back(Got_entry(object, symndx, true)); - this->set_got_size(); - object->set_local_got_offset(symndx, got_type, this->last_got_offset()); + unsigned int got_offset = this->add_got_entry(Got_entry(object, symndx, + true)); + object->set_local_got_offset(symndx, got_type, got_offset); return true; } @@ -1564,9 +1551,7 @@ Output_data_got::add_local_with_rel( if (object->local_has_got_offset(symndx, got_type)) return; - this->entries_.push_back(Got_entry()); - this->set_got_size(); - unsigned int got_offset = this->last_got_offset(); + unsigned int got_offset = this->add_got_entry(Got_entry()); object->set_local_got_offset(symndx, got_type, got_offset); rel_dyn->add_local(object, symndx, r_type, this, got_offset); } @@ -1583,9 +1568,7 @@ Output_data_got::add_local_with_rela( if (object->local_has_got_offset(symndx, got_type)) return; - this->entries_.push_back(Got_entry()); - this->set_got_size(); - unsigned int got_offset = this->last_got_offset(); + unsigned int got_offset = this->add_got_entry(Got_entry()); object->set_local_got_offset(symndx, got_type, got_offset); rela_dyn->add_local(object, symndx, r_type, this, got_offset, 0); } @@ -1607,20 +1590,15 @@ Output_data_got::add_local_pair_with_rel( if (object->local_has_got_offset(symndx, got_type)) return; - this->entries_.push_back(Got_entry()); - unsigned int got_offset = this->last_got_offset(); + unsigned int got_offset = + this->add_got_entry_pair(Got_entry(), + Got_entry(object, symndx, false)); object->set_local_got_offset(symndx, got_type, got_offset); Output_section* os = object->output_section(shndx); rel_dyn->add_output_section(os, r_type_1, this, got_offset); - this->entries_.push_back(Got_entry(object, symndx, false)); if (r_type_2 != 0) - { - got_offset = this->last_got_offset(); - rel_dyn->add_output_section(os, r_type_2, this, got_offset); - } - - this->set_got_size(); + rel_dyn->add_output_section(os, r_type_2, this, got_offset + size / 8); } template @@ -1637,20 +1615,37 @@ Output_data_got::add_local_pair_with_rela( if (object->local_has_got_offset(symndx, got_type)) return; - this->entries_.push_back(Got_entry()); - unsigned int got_offset = this->last_got_offset(); + unsigned int got_offset = + this->add_got_entry_pair(Got_entry(), + Got_entry(object, symndx, false)); object->set_local_got_offset(symndx, got_type, got_offset); Output_section* os = object->output_section(shndx); rela_dyn->add_output_section(os, r_type_1, this, got_offset, 0); - this->entries_.push_back(Got_entry(object, symndx, false)); if (r_type_2 != 0) - { - got_offset = this->last_got_offset(); - rela_dyn->add_output_section(os, r_type_2, this, got_offset, 0); - } + rela_dyn->add_output_section(os, r_type_2, this, got_offset + size / 8, 0); +} + +// Reserve a slot in the GOT for a local symbol or the second slot of a pair. - this->set_got_size(); +template +void +Output_data_got::reserve_slot(unsigned int i) +{ + this->free_list_.remove(i * size / 8, (i + 1) * size / 8); +} + +// Reserve a slot in the GOT for a global symbol. + +template +void +Output_data_got::reserve_slot_for_global( + unsigned int i, + Symbol* gsym, + unsigned int got_type) +{ + this->free_list_.remove(i * size / 8, (i + 1) * size / 8); + gsym->set_got_offset(got_type, this->got_offset(i)); } // Write out the GOT. @@ -1682,6 +1677,63 @@ Output_data_got::do_write(Output_file* of) this->entries_.clear(); } +// Create a new GOT entry and return its offset. + +template +unsigned int +Output_data_got::add_got_entry(Got_entry got_entry) +{ + if (!this->is_data_size_valid()) + { + this->entries_.push_back(got_entry); + this->set_got_size(); + return this->last_got_offset(); + } + else + { + // For an incremental update, find an available slot. + off_t got_offset = this->free_list_.allocate(size / 8, size / 8, 0); + if (got_offset == -1) + gold_fatal(_("out of patch space (GOT);" + " relink with --incremental-full")); + unsigned int got_index = got_offset / (size / 8); + gold_assert(got_index < this->entries_.size()); + this->entries_[got_index] = got_entry; + return static_cast(got_offset); + } +} + +// Create a pair of new GOT entries and return the offset of the first. + +template +unsigned int +Output_data_got::add_got_entry_pair(Got_entry got_entry_1, + Got_entry got_entry_2) +{ + if (!this->is_data_size_valid()) + { + unsigned int got_offset; + this->entries_.push_back(got_entry_1); + got_offset = this->last_got_offset(); + this->entries_.push_back(got_entry_2); + this->set_got_size(); + return got_offset; + } + else + { + // For an incremental update, find an available pair of slots. + off_t got_offset = this->free_list_.allocate(2 * size / 8, size / 8, 0); + if (got_offset == -1) + gold_fatal(_("out of patch space (GOT);" + " relink with --incremental-full")); + unsigned int got_index = got_offset / (size / 8); + gold_assert(got_index < this->entries_.size()); + this->entries_[got_index] = got_entry_1; + this->entries_[got_index + 1] = got_entry_2; + return static_cast(got_offset); + } +} + // Output_data_dynamic::Dynamic_entry methods. // Write out the entry. @@ -2335,7 +2387,8 @@ Output_section::add_output_section_data(Output_section_data* posd) uint64_t addr = this->address(); posd->set_address(addr); posd->set_file_offset(0); - // FIXME: Mark *POSD as part of a fixed-layout section. + // FIXME: This should eventually be unreachable. + // gold_unreachable(); } } diff --git a/gold/output.h b/gold/output.h index f47c724..418f016 100644 --- a/gold/output.h +++ b/gold/output.h @@ -764,6 +764,10 @@ class Output_section_data_build : public Output_section_data : Output_section_data(addralign) { } + Output_section_data_build(off_t data_size, uint64_t addralign) + : Output_section_data(data_size, addralign, false) + { } + // Set the current data size. void set_current_data_size(off_t data_size) @@ -890,6 +894,12 @@ class Output_data_space : public Output_section_data_build map_name_(map_name) { } + explicit Output_data_space(off_t data_size, uint64_t addralign, + const char* map_name) + : Output_section_data_build(data_size, addralign), + map_name_(map_name) + { } + // Set the alignment. void set_space_alignment(uint64_t align) @@ -1942,9 +1952,20 @@ class Output_data_got : public Output_section_data_build Output_data_got() : Output_section_data_build(Output_data::default_alignment_for_size(size)), - entries_() + entries_(), free_list_() { } + Output_data_got(off_t data_size) + : Output_section_data_build(data_size, + Output_data::default_alignment_for_size(size)), + entries_(), free_list_() + { + // For an incremental update, we have an existing GOT section. + // Initialize the list of entries and the free list. + this->entries_.resize(data_size / (size / 8)); + this->free_list_.init(data_size, false); + } + // Add an entry for a global symbol to the GOT. Return true if this // is a new GOT entry, false if the symbol was already in the GOT. bool @@ -2021,11 +2042,18 @@ class Output_data_got : public Output_section_data_build unsigned int add_constant(Valtype constant) { - this->entries_.push_back(Got_entry(constant)); - this->set_got_size(); - return this->last_got_offset(); + unsigned int got_offset = this->add_got_entry(Got_entry(constant)); + return got_offset; } + // Reserve a slot in the GOT for a local symbol or the second slot of a pair. + void + reserve_slot(unsigned int i); + + // Reserve a slot in the GOT for a global symbol. + void + reserve_slot_for_global(unsigned int i, Symbol* gsym, unsigned int got_type); + protected: // Write out the GOT table. void @@ -2043,7 +2071,7 @@ class Output_data_got : public Output_section_data_build public: // Create a zero entry. Got_entry() - : local_sym_index_(CONSTANT_CODE), use_plt_offset_(false) + : local_sym_index_(RESERVED_CODE), use_plt_offset_(false) { this->u_.constant = 0; } // Create a global symbol entry. @@ -2058,6 +2086,7 @@ class Output_data_got : public Output_section_data_build { gold_assert(local_sym_index != GSYM_CODE && local_sym_index != CONSTANT_CODE + && local_sym_index != RESERVED_CODE && local_sym_index == this->local_sym_index_); this->u_.object = object; } @@ -2076,7 +2105,8 @@ class Output_data_got : public Output_section_data_build enum { GSYM_CODE = 0x7fffffff, - CONSTANT_CODE = 0x7ffffffe + CONSTANT_CODE = 0x7ffffffe, + RESERVED_CODE = 0x7ffffffd }; union @@ -2097,6 +2127,14 @@ class Output_data_got : public Output_section_data_build typedef std::vector Got_entries; + // Create a new GOT entry and return its offset. + unsigned int + add_got_entry(Got_entry got_entry); + + // Create a pair of new GOT entries and return the offset of the first. + unsigned int + add_got_entry_pair(Got_entry got_entry_1, Got_entry got_entry_2); + // Return the offset into the GOT of GOT entry I. unsigned int got_offset(unsigned int i) const @@ -2114,6 +2152,10 @@ class Output_data_got : public Output_section_data_build // The list of GOT entries. Got_entries entries_; + + // List of available regions within the section, for incremental + // update links. + Free_list free_list_; }; // Output_data_dynamic is used to hold the data in SHT_DYNAMIC diff --git a/gold/target.h b/gold/target.h index b86efc4..6b25d5f 100644 --- a/gold/target.h +++ b/gold/target.h @@ -54,6 +54,8 @@ template class Sized_symbol; class Symbol_table; class Output_data; +template +class Output_data_got; class Output_section; class Input_objects; class Task; @@ -795,6 +797,24 @@ class Sized_target : public Target plt_entry_size() const { gold_unreachable(); } + // Create the GOT and PLT sections for an incremental update. + // A target needs to implement this to support incremental linking. + + virtual Output_data_got* + init_got_plt_for_update(Symbol_table*, + Layout*, + unsigned int /* got_count */, + unsigned int /* plt_count */) + { gold_unreachable(); } + + // Register an existing PLT entry for a global symbol. + // A target needs to implement this to support incremental linking. + + virtual void + register_global_plt_entry(unsigned int /* plt_index */, + Symbol*) + { gold_unreachable(); } + // Apply an incremental relocation. virtual void diff --git a/gold/testsuite/object_unittest.cc b/gold/testsuite/object_unittest.cc index 0451add..7dedeae 100644 --- a/gold/testsuite/object_unittest.cc +++ b/gold/testsuite/object_unittest.cc @@ -23,6 +23,8 @@ #include "gold.h" #include "object.h" +#include "options.h" +#include "parameters.h" #include "test.h" #include "testfile.h" @@ -62,8 +64,11 @@ Sized_object_test(const unsigned char* test_file, unsigned int test_file_size) bool Object_test(Test_report*) { + General_options options; int fail = 0; + set_parameters_options(&options); + #ifdef HAVE_TARGET_32_LITTLE if (!Sized_object_test<32, false>(test_file_1_32_little, test_file_1_size_32_little)) diff --git a/gold/x86_64.cc b/gold/x86_64.cc index e4a7043..9b9a3b1 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -53,8 +53,31 @@ class Output_data_plt_x86_64 : public Output_section_data public: typedef Output_data_reloc Reloc_section; - Output_data_plt_x86_64(Symbol_table*, Layout*, Output_data_got<64, false>*, - Output_data_space*); + Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout, + Output_data_got<64, false>* got, + Output_data_space* got_plt) + : Output_section_data(8), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), + count_(0), tlsdesc_got_offset_(-1U), free_list_() + { this->init(symtab, layout); } + + Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout, + Output_data_got<64, false>* got, + Output_data_space* got_plt, + unsigned int plt_count) + : Output_section_data((plt_count + 1) * plt_entry_size, 8, false), + tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), + count_(plt_count), tlsdesc_got_offset_(-1U), free_list_() + { + this->init(symtab, layout); + + // Initialize the free list and reserve the first entry. + this->free_list_.init((plt_count + 1) * plt_entry_size, false); + this->free_list_.remove(0, plt_entry_size); + } + + // Initialize the PLT section. + void + init(Symbol_table* symtab, Layout* layout); // Add an entry to the PLT. void @@ -65,6 +88,10 @@ class Output_data_plt_x86_64 : public Output_section_data add_local_ifunc_entry(Sized_relobj<64, false>* relobj, unsigned int local_sym_index); + // Add the relocation for a PLT entry. + void + add_relocation(Symbol* gsym, unsigned int got_offset); + // Add the reserved TLSDESC_PLT entry to the PLT. void reserve_tlsdesc_entry(unsigned int got_offset) @@ -109,6 +136,14 @@ class Output_data_plt_x86_64 : public Output_section_data get_plt_entry_size() { return plt_entry_size; } + // Reserve a slot in the PLT for an existing symbol in an incremental update. + void + reserve_slot(unsigned int plt_index) + { + this->free_list_.remove((plt_index + 1) * plt_entry_size, + (plt_index + 2) * plt_entry_size); + } + protected: void do_adjust_output_section(Output_section* os); @@ -154,6 +189,9 @@ class Output_data_plt_x86_64 : public Output_section_data unsigned int count_; // Offset of the reserved TLSDESC_GOT entry when needed. unsigned int tlsdesc_got_offset_; + // List of available regions within the section, for incremental + // update links. + Free_list free_list_; }; // The x86_64 target class. @@ -345,6 +383,18 @@ class Target_x86_64 : public Target_freebsd<64, false> unsigned int plt_entry_size() const; + // Create the GOT section for an incremental update. + Output_data_got<64, false>* + init_got_plt_for_update(Symbol_table* symtab, + Layout* layout, + unsigned int got_count, + unsigned int plt_count); + + // 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); + // Apply an incremental relocation. void apply_relocation(const Relocate_info<64, false>* relinfo, @@ -779,16 +829,10 @@ Target_x86_64::rela_dyn_section(Layout* layout) return this->rela_dyn_; } -// Create the PLT section. The ordinary .got section is an argument, -// since we need to refer to the start. We also create our own .got -// section just for PLT entries. +// Initialize the PLT section. -Output_data_plt_x86_64::Output_data_plt_x86_64(Symbol_table* symtab, - Layout* layout, - Output_data_got<64, false>* got, - Output_data_space* got_plt) - : Output_section_data(8), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), - count_(0), tlsdesc_got_offset_(-1U) +void +Output_data_plt_x86_64::init(Symbol_table* symtab, Layout* layout) { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, @@ -827,31 +871,47 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym) { gold_assert(!gsym->has_plt_offset()); - // Note that when setting the PLT offset we skip the initial - // reserved PLT entry. - gsym->set_plt_offset((this->count_ + 1) * plt_entry_size); + unsigned int plt_index; + off_t plt_offset; + section_offset_type got_offset; - ++this->count_; + if (!this->is_data_size_valid()) + { + // Note that when setting the PLT offset we skip the initial + // reserved PLT entry. + plt_index = this->count_ + 1; + plt_offset = plt_index * plt_entry_size; - section_offset_type got_offset = this->got_plt_->current_data_size(); + ++this->count_; - // Every PLT entry needs a GOT entry which points back to the PLT - // entry (this will be changed by the dynamic linker, normally - // lazily when the function is called). - this->got_plt_->set_current_data_size(got_offset + 8); + got_offset = (plt_index - 1 + 3) * 8; + gold_assert(got_offset == this->got_plt_->current_data_size()); - // Every PLT entry needs a reloc. - if (gsym->type() == elfcpp::STT_GNU_IFUNC - && gsym->can_use_relative_reloc(false)) - this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_X86_64_IRELATIVE, - this->got_plt_, got_offset, 0); + // Every PLT entry needs a GOT entry which points back to the PLT + // entry (this will be changed by the dynamic linker, normally + // lazily when the function is called). + this->got_plt_->set_current_data_size(got_offset + 8); + } else { - gsym->set_needs_dynsym_entry(); - this->rel_->add_global(gsym, elfcpp::R_X86_64_JUMP_SLOT, this->got_plt_, - got_offset, 0); + // For incremental updates, find an available slot. + plt_offset = this->free_list_.allocate(plt_entry_size, plt_entry_size, 0); + if (plt_offset == -1) + gold_fatal(_("out of patch space (PLT);" + " relink with --incremental-full")); + + // The GOT and PLT entries have a 1-1 correspondance, so the GOT offset + // can be calculated from the PLT index, adjusting for the three + // reserved entries at the beginning of the GOT. + plt_index = plt_offset / plt_entry_size - 1; + got_offset = (plt_index - 1 + 3) * 8; } + gsym->set_plt_offset(plt_offset); + + // Every PLT entry needs a reloc. + this->add_relocation(gsym, got_offset); + // Note that we don't need to save the symbol. The contents of the // PLT are independent of which symbols are used. The symbols only // appear in the relocations. @@ -881,6 +941,23 @@ Output_data_plt_x86_64::add_local_ifunc_entry(Sized_relobj<64, false>* relobj, return plt_offset; } +// Add the relocation for a PLT entry. + +void +Output_data_plt_x86_64::add_relocation(Symbol* gsym, unsigned int got_offset) +{ + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_X86_64_IRELATIVE, + this->got_plt_, got_offset, 0); + else + { + gsym->set_needs_dynsym_entry(); + this->rel_->add_global(gsym, elfcpp::R_X86_64_JUMP_SLOT, this->got_plt_, + got_offset, 0); + } +} + // Return where the TLSDESC relocations should go, creating it if // necessary. These follow the JUMP_SLOT relocations. @@ -1129,6 +1206,81 @@ Target_x86_64::plt_entry_size() const return Output_data_plt_x86_64::get_plt_entry_size(); } +// Create the GOT and PLT sections for an incremental update. + +Output_data_got<64, false>* +Target_x86_64::init_got_plt_for_update(Symbol_table* symtab, + Layout* layout, + unsigned int got_count, + unsigned int plt_count) +{ + gold_assert(this->got_ == NULL); + + this->got_ = new Output_data_got<64, false>(got_count * 8); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_, ORDER_RELRO_LAST, + true); + + // Add the three reserved entries. + this->got_plt_ = new Output_data_space((plt_count + 3) * 8, 8, "** GOT PLT"); + layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_plt_, ORDER_NON_RELRO_FIRST, + false); + + // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT. + this->global_offset_table_ = + symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, + Symbol_table::PREDEFINED, + this->got_plt_, + 0, 0, elfcpp::STT_OBJECT, + elfcpp::STB_LOCAL, + elfcpp::STV_HIDDEN, 0, + false, false); + + // If there are any TLSDESC relocations, they get GOT entries in + // .got.plt after the jump slot entries. + // FIXME: Get the count for TLSDESC entries. + this->got_tlsdesc_ = new Output_data_got<64, false>(0); + layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, + this->got_tlsdesc_, + ORDER_NON_RELRO_FIRST, false); + + // Create the PLT section. + this->plt_ = new Output_data_plt_x86_64(symtab, layout, this->got_, + this->got_plt_, plt_count); + layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR, + this->plt_, ORDER_PLT, false); + + // Make the sh_info field of .rela.plt point to .plt. + Output_section* rela_plt_os = this->plt_->rela_plt()->output_section(); + rela_plt_os->set_info_section(this->plt_->output_section()); + + return this->got_; +} + +// Register an existing PLT entry for a global symbol. + +void +Target_x86_64::register_global_plt_entry(unsigned int plt_index, + Symbol* gsym) +{ + gold_assert(this->plt_ != NULL); + gold_assert(!gsym->has_plt_offset()); + + this->plt_->reserve_slot(plt_index); + + gsym->set_plt_offset((plt_index + 1) * this->plt_entry_size()); + + unsigned int got_offset = (plt_index + 3) * 8; + this->plt_->add_relocation(gsym, got_offset); +} + // Define the _TLS_MODULE_BASE_ symbol in the TLS segment. void -- 2.7.4