From 09ec0418c0d4144afba5947499425e27900d953f Mon Sep 17 00:00:00 2001 From: Cary Coutant Date: Thu, 12 Aug 2010 22:01:11 +0000 Subject: [PATCH] elfcpp/ChangeLog: * elfcpp.h (enum SHT): Add SHT_GNU_INCREMENTAL_SYMTAB, SHT_GNU_INCREMENTAL_RELOCS. gold/ChangeLog: * archive.cc: Include incremental.h. (Archive::Archive): Initialize incremental_info_. (Archive::include_member): Record archive members in incremental info. (Add_archive_symbols::run): Record begin and end of an archive in incremental info. (Lib_group::include_member): Record objects in incremental info. * archive.h (Incremental_archive_entry): Forward declaration. (Archive::set_incremental_info): New member function. (Archive::incremental_info): New member function. (Archive::Unused_symbol_iterator): New class. (Archive::unused_symbols_begin): New member function. (Archive::unused_symbols_end): New member function. (Archive::incremental_info_): New data member. * incremental-dump.cc (find_input_containing_global): New function. (dump_incremental_inputs): Dump new incremental info sections. * incremental.cc: Include symtab.h. (Output_section_incremental_inputs): New class. (Sized_incremental_binary::do_find_incremental_inputs_sections): Support new incremental info sections. (Sized_incremental_binary::do_check_inputs): Likewise. (Incremental_inputs::report_archive): Remove. (Incremental_inputs::report_archive_begin): New function. (Incremental_inputs::report_archive_end): New function. (Incremental_inputs::report_object): New function. (Incremental_inputs::finalize_inputs): Remove. (Incremental_inputs::report_input_section): New function. (Incremental_inputs::report_script): Rewrite. (Incremental_inputs::finalize): Do nothing but finalize string table. (Incremental_inputs::create_incremental_inputs_section_data): Remove. (Incremental_inputs::sized_create_inputs_section_data): Remove. (Incremental_inputs::create_data_sections): New function. (Incremental_inputs::relocs_entsize): New function. (Output_section_incremental_inputs::set_final_data_size): New function. (Output_section_incremental_inputs::do_write): New function. (Output_section_incremental_inputs::write_header): New function. (Output_section_incremental_inputs::write_input_files): New function. (Output_section_incremental_inputs::write_info_blocks): New function. (Output_section_incremental_inputs::write_symtab): New function. * incremental.h (Incremental_script_entry): Forward declaration. (Incremental_object_entry): Forward declaration. (Incremental_archive_entry): Forward declaration. (Incremental_inputs): Forward declaration. (Incremental_inputs_header_data): Remove. (Incremental_inputs_header): Remove. (Incremental_inputs_header_write): Remove. (Incremental_inputs_entry_data): Remove. (Incremental_inputs_entry): Remove. (Incremental_inputs_entry_write): Remove. (enum Incremental_input_type): Add INCREMENTAL_INPUT_ARCHIVE_MEMBER. (Incremental_binary::find_incremental_inputs_sections): Add parameters. (Incremental_binary::do_find_incremental_inputs_sections): Likewise. (Sized_ncremental_binary::do_find_incremental_inputs_sections): Likewise. (Incremental_input_entry): New class. (Incremental_script_entry): New class. (Incremental_object_entry): New class. (Incremental_archive_entry): New class. (Incremental_inputs::Incremental_inputs): Initialize new data members. (Incremental_inputs::report_inputs): Remove. (Incremental_inputs::report_archive): Remove. (Incremental_inputs::report_archive_begin): New function. (Incremental_inputs::report_archive_end): New function. (Incremental_inputs::report_object): Change prototype. (Incremental_inputs::report_input_section): New function. (Incremental_inputs::report_script): Change prototype. (Incremental_inputs::get_reloc_count): New function. (Incremental_inputs::set_reloc_count): New function. (Incremental_inputs::create_data_sections): New function. (Incremental_inputs::create_incremental_inputs_section_data): Remove. (Incremental_inputs::inputs_section): New function. (Incremental_inputs::symtab_section): New function. (Incremental_inputs::relocs_section): New function. (Incremental_inputs::get_stringpool): Add const. (Incremental_inputs::command_line): Add const. (Incremental_inputs::inputs): Remove. (Incremental_inputs::command_line_key): New function. (Incremental_inputs::input_file_count): New function. (Incremental_inputs::input_files): New function. (Incremental_inputs::relocs_entsize): New function. (Incremental_inputs::sized_create_inputs_section_data): Remove. (Incremental_inputs::finalize_inputs): Remove. (Incremental_inputs::Input_info): Remove. (Incremental_inputs::lock_): Remove. (Incremental_inputs::inputs_): Change type. (Incremental_inputs::inputs_map_): Remove. (Incremental_inputs::current_object_entry_): New data member. (Incremental_inputs::inputs_section_): New data member. (Incremental_inputs::symtab_section_): New data member. (Incremental_inputs::relocs_section_): New data member. (Incremental_inputs::reloc_count_): New data member. (Incremental_inputs_reader): New class. (Incremental_symtab_reader): New class. (Incremental_relocs_reader): New class. * layout.cc (Layout::finalize): Move finalization of incremental info and creation of incremental info sections to follow finalization of symbol table. Set offsets for postprocessing sections. (Layout::create_incremental_info_sections): Call Incremental_inputs::create_data_sections. Add incremental symtab and relocs sections. Set sh_entsize and sh_link fields. Arrange for sections to layout after input sections. * layout.h (struct Timespec): Forward declaration. (Layout::incremental_inputs): Add const. (Layout::create_incremental_info_sections): Add parameter. * main.cc (main): Remove call to Incremental_inputs::report_inputs. * object.cc: Include incremental.h. (Relobj::finalize_incremental_relocs): New function. (Sized_relobj::do_layout): Record input sections in incremental info. * object.h (Object::output_section): New function. (Object::output_section_offset): Moved from Relobj. (Object::get_incremental_reloc_base): New function. (Object::get_incremental_reloc_count): New function. (Object::do_output_section): New function. (Object::do_output_section_offset): Moved from Relobj. (Object::do_get_incremental_reloc_base): New function. (Object::do_get_incremental_reloc_count): New function. (Object::Object): Initialize new data members. (Relobj::output_section): Renamed do_output_section and moved to protected. (Relobj::output_section_offset): Moved to Object. (Relobj::do_get_incremental_reloc_base): New function. (Relobj::do_get_incremental_reloc_count): New function. (Relobj::allocate_incremental_reloc_counts): New function. (Relobj::count_incremental_reloc): New function. (Relobj::finalize_incremental_relocs): New function. (Relobj::next_incremental_reloc_index): New function. (Relobj::reloc_counts_): New data member. (Relobj::reloc_bases_): New data member. (Sized_relobj::do_relocate_sections): Add parameter. Change caller. (Sized_relobj::relocate_sections): Add parameter. Change all callers. (Sized_relobj::incremental_relocs_scan): New function. (Sized_relobj::incremental_relocs_scan_reltype): New function. (Sized_relobj::incremental_relocs_write): New function. (Sized_relobj::incremental_relocs_write_reltype): New function. * plugin.cc (Plugin_manager::add_input_file): Rewrite test for incremental link. * readsyms.cc (Read_symbols::do_read_symbols): Move reporting of archives and object files elsewhere. (Add_symbols::run): Report object files here. (Finish_group::run): Report end of archive at end of group. * reloc.cc: Include layout.h, incremental.h. (Sized_relobj::do_read_relocs): Need relocations for incremental link. (Sized_relobj::do_scan_relocs): Record relocations for incremental link. (Sized_relobj::incremental_relocs_scan): New function. (Sized_relobj::incremental_relocs_scan_reltype): New function. (Sized_relobj::do_relocate_sections): Write incremental relocations. (Sized_relobj::incremental_relocs_write): New function. (Sized_relobj::incremental_relocs_write_reltype): New function. * script.cc (read_input_script): Rewrite test for incremental link. Change call to Incremental_inputs::report_script. * symtab.h (Symbol_table::first_global_index): New function. (Symbol_table::output_count): New function. --- elfcpp/ChangeLog | 5 + elfcpp/elfcpp.h | 2 + gold/ChangeLog | 154 ++++++ gold/archive.cc | 21 +- gold/archive.h | 78 +++- gold/incremental-dump.cc | 404 ++++++++++++---- gold/incremental.cc | 786 +++++++++++++++++++++++-------- gold/incremental.h | 982 +++++++++++++++++++++++++++------------ gold/layout.cc | 82 +++- gold/layout.h | 7 +- gold/main.cc | 5 +- gold/object.cc | 32 ++ gold/object.h | 171 ++++++- gold/plugin.cc | 2 +- gold/readsyms.cc | 29 +- gold/reloc.cc | 193 +++++++- gold/script.cc | 12 +- gold/symtab.h | 10 + 18 files changed, 2337 insertions(+), 638 deletions(-) diff --git a/elfcpp/ChangeLog b/elfcpp/ChangeLog index 782b3cf40bd..7488984e9ca 100644 --- a/elfcpp/ChangeLog +++ b/elfcpp/ChangeLog @@ -1,3 +1,8 @@ +2010-08-12 Cary Coutant + + * elfcpp.h (enum SHT): Add SHT_GNU_INCREMENTAL_SYMTAB, + SHT_GNU_INCREMENTAL_RELOCS. + 2010-08-04 Ian Lance Taylor * i386.h (R_386_IRELATIVE): Define. diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h index 52b8b2534ee..c46fbf5a47f 100644 --- a/elfcpp/elfcpp.h +++ b/elfcpp/elfcpp.h @@ -365,6 +365,8 @@ enum SHT // The remaining values are not in the standard. // Incremental build data. SHT_GNU_INCREMENTAL_INPUTS = 0x6fff4700, + SHT_GNU_INCREMENTAL_SYMTAB = 0x6fff4701, + SHT_GNU_INCREMENTAL_RELOCS = 0x6fff4702, // Object attributes. SHT_GNU_ATTRIBUTES = 0x6ffffff5, // GNU style dynamic hash table. diff --git a/gold/ChangeLog b/gold/ChangeLog index 90510b5f6e1..c77f2d7cc1b 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,157 @@ +2010-08-12 Cary Coutant + + * archive.cc: Include incremental.h. + (Archive::Archive): Initialize incremental_info_. + (Archive::include_member): Record archive members in incremental info. + (Add_archive_symbols::run): Record begin and end of an archive in + incremental info. + (Lib_group::include_member): Record objects in incremental info. + * archive.h (Incremental_archive_entry): Forward declaration. + (Archive::set_incremental_info): New member function. + (Archive::incremental_info): New member function. + (Archive::Unused_symbol_iterator): New class. + (Archive::unused_symbols_begin): New member function. + (Archive::unused_symbols_end): New member function. + (Archive::incremental_info_): New data member. + * incremental-dump.cc (find_input_containing_global): New function. + (dump_incremental_inputs): Dump new incremental info sections. + * incremental.cc: Include symtab.h. + (Output_section_incremental_inputs): New class. + (Sized_incremental_binary::do_find_incremental_inputs_sections): Support + new incremental info sections. + (Sized_incremental_binary::do_check_inputs): Likewise. + (Incremental_inputs::report_archive): Remove. + (Incremental_inputs::report_archive_begin): New function. + (Incremental_inputs::report_archive_end): New function. + (Incremental_inputs::report_object): New function. + (Incremental_inputs::finalize_inputs): Remove. + (Incremental_inputs::report_input_section): New function. + (Incremental_inputs::report_script): Rewrite. + (Incremental_inputs::finalize): Do nothing but finalize string table. + (Incremental_inputs::create_incremental_inputs_section_data): Remove. + (Incremental_inputs::sized_create_inputs_section_data): Remove. + (Incremental_inputs::create_data_sections): New function. + (Incremental_inputs::relocs_entsize): New function. + (Output_section_incremental_inputs::set_final_data_size): New function. + (Output_section_incremental_inputs::do_write): New function. + (Output_section_incremental_inputs::write_header): New function. + (Output_section_incremental_inputs::write_input_files): New function. + (Output_section_incremental_inputs::write_info_blocks): New function. + (Output_section_incremental_inputs::write_symtab): New function. + * incremental.h (Incremental_script_entry): Forward declaration. + (Incremental_object_entry): Forward declaration. + (Incremental_archive_entry): Forward declaration. + (Incremental_inputs): Forward declaration. + (Incremental_inputs_header_data): Remove. + (Incremental_inputs_header): Remove. + (Incremental_inputs_header_write): Remove. + (Incremental_inputs_entry_data): Remove. + (Incremental_inputs_entry): Remove. + (Incremental_inputs_entry_write): Remove. + (enum Incremental_input_type): Add INCREMENTAL_INPUT_ARCHIVE_MEMBER. + (Incremental_binary::find_incremental_inputs_sections): Add parameters. + (Incremental_binary::do_find_incremental_inputs_sections): Likewise. + (Sized_ncremental_binary::do_find_incremental_inputs_sections): + Likewise. + (Incremental_input_entry): New class. + (Incremental_script_entry): New class. + (Incremental_object_entry): New class. + (Incremental_archive_entry): New class. + (Incremental_inputs::Incremental_inputs): Initialize new data members. + (Incremental_inputs::report_inputs): Remove. + (Incremental_inputs::report_archive): Remove. + (Incremental_inputs::report_archive_begin): New function. + (Incremental_inputs::report_archive_end): New function. + (Incremental_inputs::report_object): Change prototype. + (Incremental_inputs::report_input_section): New function. + (Incremental_inputs::report_script): Change prototype. + (Incremental_inputs::get_reloc_count): New function. + (Incremental_inputs::set_reloc_count): New function. + (Incremental_inputs::create_data_sections): New function. + (Incremental_inputs::create_incremental_inputs_section_data): Remove. + (Incremental_inputs::inputs_section): New function. + (Incremental_inputs::symtab_section): New function. + (Incremental_inputs::relocs_section): New function. + (Incremental_inputs::get_stringpool): Add const. + (Incremental_inputs::command_line): Add const. + (Incremental_inputs::inputs): Remove. + (Incremental_inputs::command_line_key): New function. + (Incremental_inputs::input_file_count): New function. + (Incremental_inputs::input_files): New function. + (Incremental_inputs::relocs_entsize): New function. + (Incremental_inputs::sized_create_inputs_section_data): Remove. + (Incremental_inputs::finalize_inputs): Remove. + (Incremental_inputs::Input_info): Remove. + (Incremental_inputs::lock_): Remove. + (Incremental_inputs::inputs_): Change type. + (Incremental_inputs::inputs_map_): Remove. + (Incremental_inputs::current_object_entry_): New data member. + (Incremental_inputs::inputs_section_): New data member. + (Incremental_inputs::symtab_section_): New data member. + (Incremental_inputs::relocs_section_): New data member. + (Incremental_inputs::reloc_count_): New data member. + (Incremental_inputs_reader): New class. + (Incremental_symtab_reader): New class. + (Incremental_relocs_reader): New class. + * layout.cc (Layout::finalize): Move finalization of incremental info + and creation of incremental info sections to follow finalization of + symbol table. Set offsets for postprocessing sections. + (Layout::create_incremental_info_sections): Call + Incremental_inputs::create_data_sections. Add incremental symtab + and relocs sections. Set sh_entsize and sh_link fields. Arrange for + sections to layout after input sections. + * layout.h (struct Timespec): Forward declaration. + (Layout::incremental_inputs): Add const. + (Layout::create_incremental_info_sections): Add parameter. + * main.cc (main): Remove call to Incremental_inputs::report_inputs. + * object.cc: Include incremental.h. + (Relobj::finalize_incremental_relocs): New function. + (Sized_relobj::do_layout): Record input sections in incremental info. + * object.h (Object::output_section): New function. + (Object::output_section_offset): Moved from Relobj. + (Object::get_incremental_reloc_base): New function. + (Object::get_incremental_reloc_count): New function. + (Object::do_output_section): New function. + (Object::do_output_section_offset): Moved from Relobj. + (Object::do_get_incremental_reloc_base): New function. + (Object::do_get_incremental_reloc_count): New function. + (Object::Object): Initialize new data members. + (Relobj::output_section): Renamed do_output_section and moved to + protected. + (Relobj::output_section_offset): Moved to Object. + (Relobj::do_get_incremental_reloc_base): New function. + (Relobj::do_get_incremental_reloc_count): New function. + (Relobj::allocate_incremental_reloc_counts): New function. + (Relobj::count_incremental_reloc): New function. + (Relobj::finalize_incremental_relocs): New function. + (Relobj::next_incremental_reloc_index): New function. + (Relobj::reloc_counts_): New data member. + (Relobj::reloc_bases_): New data member. + (Sized_relobj::do_relocate_sections): Add parameter. Change caller. + (Sized_relobj::relocate_sections): Add parameter. Change all callers. + (Sized_relobj::incremental_relocs_scan): New function. + (Sized_relobj::incremental_relocs_scan_reltype): New function. + (Sized_relobj::incremental_relocs_write): New function. + (Sized_relobj::incremental_relocs_write_reltype): New function. + * plugin.cc (Plugin_manager::add_input_file): Rewrite test for + incremental link. + * readsyms.cc (Read_symbols::do_read_symbols): Move reporting of + archives and object files elsewhere. + (Add_symbols::run): Report object files here. + (Finish_group::run): Report end of archive at end of group. + * reloc.cc: Include layout.h, incremental.h. + (Sized_relobj::do_read_relocs): Need relocations for incremental link. + (Sized_relobj::do_scan_relocs): Record relocations for incremental link. + (Sized_relobj::incremental_relocs_scan): New function. + (Sized_relobj::incremental_relocs_scan_reltype): New function. + (Sized_relobj::do_relocate_sections): Write incremental relocations. + (Sized_relobj::incremental_relocs_write): New function. + (Sized_relobj::incremental_relocs_write_reltype): New function. + * script.cc (read_input_script): Rewrite test for incremental link. + Change call to Incremental_inputs::report_script. + * symtab.h (Symbol_table::first_global_index): New function. + (Symbol_table::output_count): New function. + 2010-08-12 Doug Kwan * arm.cc (Target_arm::merge_object_attributes): Check command line diff --git a/gold/archive.cc b/gold/archive.cc index f1000a195de..8c34d5524b5 100644 --- a/gold/archive.cc +++ b/gold/archive.cc @@ -39,6 +39,7 @@ #include "layout.h" #include "archive.h" #include "plugin.h" +#include "incremental.h" namespace gold { @@ -89,7 +90,8 @@ Archive::Archive(const std::string& name, Input_file* input_file, : name_(name), input_file_(input_file), armap_(), armap_names_(), extended_names_(), armap_checked_(), seen_offsets_(), members_(), is_thin_archive_(is_thin_archive), included_member_(false), - nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0) + nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0), + incremental_info_(NULL) { this->no_export_ = parameters->options().check_excluded_libs(input_file->found_name()); @@ -891,6 +893,8 @@ Archive::include_member(Symbol_table* symtab, Layout* layout, else { { + if (layout->incremental_inputs() != NULL) + layout->incremental_inputs()->report_object(obj, this); Read_symbols_data sd; obj->read_symbols(&sd); obj->layout(symtab, layout, &sd); @@ -952,6 +956,11 @@ Add_archive_symbols::locks(Task_locker* tl) void Add_archive_symbols::run(Workqueue* workqueue) { + // For an incremental link, begin recording layout information. + Incremental_inputs* incremental_inputs = this->layout_->incremental_inputs(); + if (incremental_inputs != NULL) + incremental_inputs->report_archive_begin(this->archive_); + bool added = this->archive_->add_symbols(this->symtab_, this->layout_, this->input_objects_, this->mapfile_); @@ -978,6 +987,11 @@ Add_archive_symbols::run(Workqueue* workqueue) this->input_group_->add_archive(this->archive_); else { + // For an incremental link, finish recording the layout information. + Incremental_inputs* incremental_inputs = this->layout_->incremental_inputs(); + if (incremental_inputs != NULL) + incremental_inputs->report_archive_end(this->archive_); + // We no longer need to know about this archive. delete this->archive_; this->archive_ = NULL; @@ -1077,6 +1091,9 @@ Lib_group::include_member(Symbol_table* symtab, Layout* layout, obj->lock(this->task_); if (input_objects->add_object(obj)) { + // FIXME: Record incremental link info for --start-lib/--end-lib. + if (layout->incremental_inputs() != NULL) + layout->incremental_inputs()->report_object(obj, NULL); obj->layout(symtab, layout, sd); obj->add_symbols(symtab, sd, layout); // Unlock the file for the next task. @@ -1116,6 +1133,8 @@ void Add_lib_group_symbols::run(Workqueue*) { this->lib_->add_symbols(this->symtab_, this->layout_, this->input_objects_); + + // FIXME: Record incremental link info for --start_lib/--end_lib. } Add_lib_group_symbols::~Add_lib_group_symbols() diff --git a/gold/archive.h b/gold/archive.h index bff34576eca..9107d1e612f 100644 --- a/gold/archive.h +++ b/gold/archive.h @@ -1,6 +1,6 @@ // archive.h -- archive support for gold -*- C++ -*- -// Copyright 2006, 2007, 2008 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2010 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -42,6 +42,7 @@ class Symbol_table; class Object; class Read_symbols_data; class Input_file_lib; +class Incremental_archive_entry; // An entry in the archive map of offsets to members. struct Archive_member @@ -164,6 +165,16 @@ class Archive no_export() { return this->no_export_; } + // Store a pointer to the incremental link info for the archive. + void + set_incremental_info(Incremental_archive_entry* info) + { this->incremental_info_ = info; } + + // Return the pointer to the incremental link info for the archive. + Incremental_archive_entry* + incremental_info() const + { return this->incremental_info_; } + // When we see a symbol in an archive we might decide to include the member, // not include the member or be undecided. This enum represents these // possibilities. @@ -180,6 +191,69 @@ class Archive Symbol** symp, std::string* why, char** tmpbufp, size_t* tmpbuflen); + private: + struct Armap_entry; + + public: + // Iterator class for unused global symbols. This iterator is used + // for incremental links. + + class Unused_symbol_iterator + { + public: + Unused_symbol_iterator(Archive* arch, + std::vector::const_iterator it) + : arch_(arch), it_(it) + { this->skip_used_symbols(); } + + const char* + operator*() const + { return this->arch_->armap_names_.data() + this->it_->name_offset; } + + Unused_symbol_iterator& + operator++() + { + ++this->it_; + this->skip_used_symbols(); + return *this; + } + + bool + operator==(const Unused_symbol_iterator p) const + { return this->it_ == p.it_; } + + bool + operator!=(const Unused_symbol_iterator p) const + { return this->it_ != p.it_; } + + private: + // Skip over symbols defined by members that have been included. + void + skip_used_symbols() + { + while (this->it_ != this->arch_->armap_.end() + && (this->arch_->seen_offsets_.find(this->it_->file_offset) + != this->arch_->seen_offsets_.end())) + ++it_; + } + + // The underlying archive. + Archive* arch_; + + // The underlying iterator over all entries in the archive map. + std::vector::const_iterator it_; + }; + + // Return an iterator referring to the first unused symbol. + Unused_symbol_iterator + unused_symbols_begin() + { return Unused_symbol_iterator(this, this->armap_.begin()); } + + // Return an iterator referring to the end of the unused symbols. + Unused_symbol_iterator + unused_symbols_end() + { return Unused_symbol_iterator(this, this->armap_.end()); } + private: Archive(const Archive&); Archive& operator=(const Archive&); @@ -312,6 +386,8 @@ class Archive unsigned int num_members_; // True if we exclude this library archive from automatic export. bool no_export_; + // The incremental link information for this archive. + Incremental_archive_entry* incremental_info_; }; // This class is used to read an archive and pick out the desired diff --git a/gold/incremental-dump.cc b/gold/incremental-dump.cc index e174b9969b9..98555cdb146 100644 --- a/gold/incremental-dump.cc +++ b/gold/incremental-dump.cc @@ -1,6 +1,6 @@ -// inremental.cc -- incremental linking test/deubg tool +// incremental.cc -- incremental linking test/debug tool -// Copyright 2009 Free Software Foundation, Inc. +// Copyright 2009, 2010 Free Software Foundation, Inc. // Written by Rafael Avila de Espindola // This file is part of gold. @@ -32,6 +32,7 @@ #include #include +#include #include "incremental.h" @@ -42,16 +43,52 @@ namespace gold using namespace gold; +template +static typename Incremental_inputs_reader:: + Incremental_input_entry_reader +find_input_containing_global( + Incremental_inputs_reader& incremental_inputs, + unsigned int offset, + unsigned int* symndx) +{ + typedef Incremental_inputs_reader Inputs_reader; + for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i) + { + typename Inputs_reader::Incremental_input_entry_reader input_file = + incremental_inputs.input_file(i); + if (input_file.type() != INCREMENTAL_INPUT_OBJECT + && input_file.type() != INCREMENTAL_INPUT_ARCHIVE_MEMBER) + continue; + unsigned int nsyms = input_file.get_global_symbol_count(); + if (offset >= input_file.get_symbol_offset(0) + && offset < input_file.get_symbol_offset(nsyms)) + { + *symndx = (offset - input_file.get_symbol_offset(0)) / 16; + return input_file; + } + } + gold_unreachable(); +} + template static void -dump_incremental_inputs(const char* argv0, - const char* filename, Incremental_binary* inc) +dump_incremental_inputs(const char* argv0, const char* filename, + Incremental_binary* inc) { bool t; - unsigned int strtab_shndx; - Incremental_binary::Location location; - - t = inc->find_incremental_inputs_section(&location, &strtab_shndx); + unsigned int inputs_shndx; + unsigned int isymtab_shndx; + unsigned int irelocs_shndx; + unsigned int istrtab_shndx; + typedef Incremental_binary::Location Location; + typedef Incremental_binary::View View; + typedef Incremental_inputs_reader Inputs_reader; + typedef typename Inputs_reader::Incremental_input_entry_reader Entry_reader; + + // Find the .gnu_incremental_inputs, _symtab, _relocs, and _strtab sections. + + t = inc->find_incremental_inputs_sections(&inputs_shndx, &isymtab_shndx, + &irelocs_shndx, &istrtab_shndx); if (!t) { fprintf(stderr, "%s: %s: no .gnu_incremental_inputs section\n", argv0, @@ -59,105 +96,306 @@ dump_incremental_inputs(const char* argv0, exit (1); } - Incremental_binary::View inputs_view(inc->view(location)); - const unsigned char* p = inputs_view.data(); + elfcpp::Elf_file elf_file(inc); + + // Get a view of the .gnu_incremental_inputs section. + + Location inputs_location(elf_file.section_contents(inputs_shndx)); + View inputs_view(inc->view(inputs_location)); + + // Get the .gnu_incremental_strtab section as a string table. - Incremental_inputs_header incremental_header(p); + Location istrtab_location(elf_file.section_contents(istrtab_shndx)); + View istrtab_view(inc->view(istrtab_location)); + elfcpp::Elf_strtab istrtab(istrtab_view.data(), istrtab_location.data_size); - const unsigned char* incremental_inputs_base = - (p + sizeof(Incremental_inputs_header_data)); + // Create a reader object for the .gnu_incremental_inputs section. - if (incremental_header.get_version() != 1) + Incremental_inputs_reader + incremental_inputs(inputs_view.data(), istrtab); + + if (incremental_inputs.version() != 1) { fprintf(stderr, "%s: %s: unknown incremental version %d\n", argv0, - filename, incremental_header.get_version()); + filename, incremental_inputs.version()); exit(1); } - elfcpp::Elf_file elf_file(inc); - - if (elf_file.section_type(strtab_shndx) != elfcpp::SHT_STRTAB) + const char* command_line = incremental_inputs.command_line(); + if (command_line == NULL) { fprintf(stderr, - "%s: %s: invalid string table section %u (type %d != %d)\n", - argv0, filename, strtab_shndx, - elf_file.section_type(strtab_shndx), elfcpp::SHT_STRTAB); + "%s: %s: failed to get link command line\n", + argv0, filename); exit(1); } + printf("Link command line: %s\n", command_line); - Incremental_binary::Location - strtab_location(elf_file.section_contents(strtab_shndx)); + printf("\nInput files:\n"); + for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i) + { + typedef Incremental_inputs_reader Inputs_reader; + typename Inputs_reader::Incremental_input_entry_reader input_file = + incremental_inputs.input_file(i); + + const char* objname = input_file.filename(); + if (objname == NULL) + { + fprintf(stderr,"%s: %s: failed to get file name for object %u\n", + argv0, filename, i); + exit(1); + } + printf("[%d] %s\n", i, objname); + + Timespec mtime = input_file.get_mtime(); + printf(" Timestamp: %llu.%09d %s", + static_cast(mtime.seconds), + mtime.nanoseconds, + ctime(&mtime.seconds)); + + Incremental_input_type input_type = input_file.type(); + printf(" Type: "); + switch (input_type) + { + case INCREMENTAL_INPUT_OBJECT: + { + printf("Object\n"); + printf(" Input section count: %d\n", + input_file.get_input_section_count()); + printf(" Symbol count: %d\n", + input_file.get_global_symbol_count()); + } + break; + case INCREMENTAL_INPUT_ARCHIVE_MEMBER: + { + printf("Archive member\n"); + printf(" Input section count: %d\n", + input_file.get_input_section_count()); + printf(" Symbol count: %d\n", + input_file.get_global_symbol_count()); + } + break; + case INCREMENTAL_INPUT_ARCHIVE: + { + printf("Archive\n"); + printf(" Member count: %d\n", input_file.get_member_count()); + printf(" Unused symbol count: %d\n", + input_file.get_unused_symbol_count()); + } + break; + case INCREMENTAL_INPUT_SHARED_LIBRARY: + { + printf("Shared library\n"); + printf(" Symbol count: %d\n", + input_file.get_global_symbol_count()); + } + break; + case INCREMENTAL_INPUT_SCRIPT: + printf("Linker script\n"); + break; + default: + fprintf(stderr, "%s: invalid file type for object %u: %d\n", + argv0, i, input_type); + exit(1); + } + } - Incremental_binary::View strtab_view(inc->view(strtab_location)); - p = strtab_view.data(); + printf("\nInput sections:\n"); + for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i) + { + typedef Incremental_inputs_reader Inputs_reader; + typedef typename Inputs_reader::Incremental_input_entry_reader + Entry_reader; + + Entry_reader input_file(incremental_inputs.input_file(i)); + + if (input_file.type() != INCREMENTAL_INPUT_OBJECT + && input_file.type() != INCREMENTAL_INPUT_ARCHIVE_MEMBER) + continue; + + const char* objname = input_file.filename(); + if (objname == NULL) + { + fprintf(stderr,"%s: %s: failed to get file name for object %u\n", + argv0, filename, i); + exit(1); + } + + printf("[%d] %s\n", i, objname); + + printf(" %3s %6s %8s %8s %s\n", + "n", "outndx", "offset", "size", "name"); + unsigned int nsections = input_file.get_input_section_count(); + for (unsigned int shndx = 0; shndx < nsections; ++shndx) + { + typename Entry_reader::Input_section_info info( + input_file.get_input_section(shndx)); + printf(" %3d %6d %8lld %8lld %s\n", shndx, + info.output_shndx, + static_cast(info.sh_offset), + static_cast(info.sh_size), + info.name); + } + } - elfcpp::Elf_strtab strtab(strtab_view.data(), strtab_location.data_size); - const char* command_line; - elfcpp::Elf_Word command_line_offset = - incremental_header.get_command_line_offset(); - t = strtab.get_c_string(command_line_offset, &command_line); + printf("\nGlobal symbols per input file:\n"); + for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i) + { + typedef Incremental_inputs_reader Inputs_reader; + typedef typename Inputs_reader::Incremental_input_entry_reader + Entry_reader; + + Entry_reader input_file(incremental_inputs.input_file(i)); + + if (input_file.type() != INCREMENTAL_INPUT_OBJECT + && input_file.type() != INCREMENTAL_INPUT_ARCHIVE_MEMBER) + continue; + + const char* objname = input_file.filename(); + if (objname == NULL) + { + fprintf(stderr,"%s: %s: failed to get file name for object %u\n", + argv0, filename, i); + exit(1); + } + + printf("[%d] %s\n", i, objname); + + unsigned int nsyms = input_file.get_global_symbol_count(); + if (nsyms > 0) + printf(" %6s %8s %8s %8s %8s\n", + "outndx", "offset", "chain", "#relocs", "rbase"); + for (unsigned int symndx = 0; symndx < nsyms; ++symndx) + { + typename Entry_reader::Global_symbol_info info( + input_file.get_global_symbol_info(symndx)); + printf(" %6d %8d %8d %8d %8d\n", + info.output_symndx, + input_file.get_symbol_offset(symndx), + info.next_offset, + info.reloc_count, + info.reloc_offset); + } + } - if (!t) + // Get a view of the .symtab section. + + unsigned int symtab_shndx = elf_file.find_section_by_type(elfcpp::SHT_SYMTAB); + if (symtab_shndx == elfcpp::SHN_UNDEF) // Not found. { - fprintf(stderr, - "%s: %s: failed to get link command line: %zu out of range\n", - argv0, filename, - static_cast(command_line_offset)); - exit(1); + fprintf(stderr, "%s: %s: no symbol table section\n", argv0, filename); + exit (1); } + Location symtab_location(elf_file.section_contents(symtab_shndx)); + View symtab_view(inc->view(symtab_location)); - printf("Link command line: %s\n", command_line); + // Get a view of the .strtab section. - printf("Input files:\n"); - for (unsigned i = 0; i < incremental_header.get_input_file_count(); ++i) + unsigned int strtab_shndx = elf_file.section_link(symtab_shndx); + if (strtab_shndx == elfcpp::SHN_UNDEF + || strtab_shndx > elf_file.shnum() + || elf_file.section_type(strtab_shndx) != elfcpp::SHT_STRTAB) { - const unsigned char* input_p = incremental_inputs_base + - i * sizeof(Incremental_inputs_entry_data); - Incremental_inputs_entry input(input_p); - const char* objname; + fprintf(stderr, "%s: %s: no string table section\n", argv0, filename); + exit (1); + } + Location strtab_location(elf_file.section_contents(strtab_shndx)); + View strtab_view(inc->view(strtab_location)); + elfcpp::Elf_strtab strtab(strtab_view.data(), strtab_location.data_size); + + // Get a view of the .gnu_incremental_symtab section. - t = strtab.get_c_string(input.get_filename_offset(), &objname); - if (!t) + Location isymtab_location(elf_file.section_contents(isymtab_shndx)); + View isymtab_view(inc->view(isymtab_location)); + + // Get a view of the .gnu_incremental_relocs section. + + Location irelocs_location(elf_file.section_contents(irelocs_shndx)); + View irelocs_view(inc->view(irelocs_location)); + + // The .gnu_incremental_symtab section contains entries that parallel + // the global symbols of the main symbol table. The sh_info field + // of the main symbol table's section header tells us how many global + // symbols there are, but that count does not include any global + // symbols that were forced local during the link. Therefore, we + // use the size of the .gnu_incremental_symtab section to deduce + // the number of global symbols + forced-local symbols there are + // in the symbol table. + unsigned int sym_size = elfcpp::Elf_sizes::sym_size; + unsigned int nsyms = symtab_location.data_size / sym_size; + unsigned int nglobals = isymtab_location.data_size / 4; + unsigned int first_global = nsyms - nglobals; + unsigned const char* sym_p = symtab_view.data() + first_global * sym_size; + unsigned const char* isym_p = isymtab_view.data(); + + Incremental_symtab_reader isymtab(isymtab_view.data()); + Incremental_relocs_reader irelocs(irelocs_view.data()); + + printf("\nGlobal symbol table:\n"); + for (unsigned int i = 0; i < nglobals; i++) + { + elfcpp::Sym sym(sym_p); + const char* symname; + if (!strtab.get_c_string(sym.get_st_name(), &symname)) + symname = ""; + printf("[%d] %s\n", first_global + i, symname); + unsigned int offset = isymtab.get_list_head(i); + while (offset > 0) { - fprintf(stderr,"%s: %s: failed to get file name for object %u:" - " %zu out of range\n", argv0, filename, i, - static_cast(input.get_filename_offset())); - exit(1); - } - printf(" %s\n", objname); - printf(" Timestamp sec = %llu\n", - static_cast(input.get_timestamp_sec())); - printf(" Timestamp nsec = %d\n", input.get_timestamp_nsec()); - printf(" Type = "); - // TODO: print the data at input->data_offset once we have it. - elfcpp::Elf_Word input_type = input.get_input_type(); - switch (input_type) - { - case INCREMENTAL_INPUT_OBJECT: - printf("Object\n"); - break; - case INCREMENTAL_INPUT_ARCHIVE: - printf("Archive\n"); - break; - case INCREMENTAL_INPUT_SHARED_LIBRARY: - printf("Shared library\n"); - break; - case INCREMENTAL_INPUT_SCRIPT: - printf("Linker script\n"); - if (input.get_data_offset() != 0) - { - fprintf(stderr,"%s: %s: %u is a script but offset is not zero", - argv0, filename, i); - exit(1); - } - break; - case INCREMENTAL_INPUT_INVALID: - default: - fprintf(stderr, "%s: invalid file type for object %u: %d\n", - argv0, i, input_type); - exit(1); - } + unsigned int sym_ndx; + Entry_reader input_file = + find_input_containing_global(incremental_inputs, + offset, &sym_ndx); + typename Entry_reader::Global_symbol_info sym_info( + input_file.get_global_symbol_info(sym_ndx)); + printf(" %s (first reloc: %d, reloc count: %d)", + input_file.filename(), sym_info.reloc_offset, + sym_info.reloc_count); + if (sym_info.output_symndx != first_global + i) + printf(" ** wrong output symndx (%d) **", sym_info.output_symndx); + printf("\n"); + // Dump the relocations from this input file for this symbol. + unsigned int r_off = sym_info.reloc_offset; + for (unsigned int j = 0; j < sym_info.reloc_count; j++) + { + printf(" %4d relocation type %3d shndx %d" + " offset %016llx addend %016llx %s\n", + r_off, + irelocs.get_r_type(r_off), + irelocs.get_r_shndx(r_off), + static_cast(irelocs.get_r_offset(r_off)), + static_cast(irelocs.get_r_addend(r_off)), + symname); + r_off += irelocs.reloc_size; + } + offset = sym_info.next_offset; + } + sym_p += sym_size; + isym_p += 4; } + + printf("\nUnused archive symbols:\n"); + for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i) + { + Entry_reader input_file(incremental_inputs.input_file(i)); + + if (input_file.type() != INCREMENTAL_INPUT_ARCHIVE) + continue; + + const char* objname = input_file.filename(); + if (objname == NULL) + { + fprintf(stderr,"%s: %s: failed to get file name for object %u\n", + argv0, filename, i); + exit(1); + } + + printf("[%d] %s\n", i, objname); + unsigned int nsyms = input_file.get_unused_symbol_count(); + for (unsigned int symndx = 0; symndx < nsyms; ++symndx) + printf(" %s\n", input_file.get_unused_symbol(symndx)); + } + } int diff --git a/gold/incremental.cc b/gold/incremental.cc index 01be470dbea..b279c72ead3 100644 --- a/gold/incremental.cc +++ b/gold/incremental.cc @@ -1,6 +1,6 @@ // inremental.cc -- incremental linking support for gold -// Copyright 2009 Free Software Foundation, Inc. +// Copyright 2009, 2010 Free Software Foundation, Inc. // Written by Mikolaj Zalewski . // This file is part of gold. @@ -27,6 +27,7 @@ #include "elfcpp.h" #include "output.h" +#include "symtab.h" #include "incremental.h" #include "archive.h" #include "output.h" @@ -38,6 +39,73 @@ namespace gold { // we could think about backward (and forward?) compatibility. const unsigned int INCREMENTAL_LINK_VERSION = 1; +// This class manages the .gnu_incremental_inputs section, which holds +// the header information, a directory of input files, and separate +// entries for each input file. + +template +class Output_section_incremental_inputs : public Output_section_data +{ + public: + Output_section_incremental_inputs(const Incremental_inputs* inputs, + const Symbol_table* symtab) + : Output_section_data(size / 8), inputs_(inputs), symtab_(symtab) + { } + + protected: + // Set the final data size. + void + set_final_data_size(); + + // Write the data to the file. + void + do_write(Output_file*); + + // Write to a map file. + void + do_print_to_mapfile(Mapfile* mapfile) const + { mapfile->print_output_data(this, _("** incremental_inputs")); } + + private: + // Write the section header. + unsigned char* + write_header(unsigned char* pov, unsigned int input_file_count, + section_offset_type command_line_offset); + + // Write the input file entries. + unsigned char* + write_input_files(unsigned char* oview, unsigned char* pov, + Stringpool* strtab); + + // Write the supplemental information blocks. + unsigned char* + write_info_blocks(unsigned char* oview, unsigned char* pov, + Stringpool* strtab, unsigned int* global_syms, + unsigned int global_sym_count); + + // Write the contents of the .gnu_incremental_symtab section. + void + write_symtab(unsigned char* pov, unsigned int* global_syms, + unsigned int global_sym_count); + + // Typedefs for writing the data to the output sections. + typedef elfcpp::Swap Swap; + typedef elfcpp::Swap<16, big_endian> Swap16; + typedef elfcpp::Swap<32, big_endian> Swap32; + typedef elfcpp::Swap<64, big_endian> Swap64; + + // Sizes of various structures. + static const int sizeof_addr = size / 8; + static const int header_size = 16; + static const int input_entry_size = 24; + + // The Incremental_inputs object. + const Incremental_inputs* inputs_; + + // The symbol table. + const Symbol_table* symtab_; +}; + // Inform the user why we don't do an incremental link. Not called in // the obvious case of missing output file. TODO: Is this helpful? @@ -77,77 +145,101 @@ Incremental_binary::error(const char* format, ...) const va_end(args); } +// Find the .gnu_incremental_inputs section and related sections. + template bool -Sized_incremental_binary::do_find_incremental_inputs_section( - Location* location, - unsigned int* strtab_shndx) +Sized_incremental_binary::do_find_incremental_inputs_sections( + unsigned int* p_inputs_shndx, + unsigned int* p_symtab_shndx, + unsigned int* p_relocs_shndx, + unsigned int* p_strtab_shndx) { - unsigned int shndx = this->elf_file_.find_section_by_type( - elfcpp::SHT_GNU_INCREMENTAL_INPUTS); - if (shndx == elfcpp::SHN_UNDEF) // Not found. + unsigned int inputs_shndx = + this->elf_file_.find_section_by_type(elfcpp::SHT_GNU_INCREMENTAL_INPUTS); + if (inputs_shndx == elfcpp::SHN_UNDEF) // Not found. + return false; + + unsigned int symtab_shndx = + this->elf_file_.find_section_by_type(elfcpp::SHT_GNU_INCREMENTAL_SYMTAB); + if (symtab_shndx == elfcpp::SHN_UNDEF) // Not found. + return false; + if (this->elf_file_.section_link(symtab_shndx) != inputs_shndx) return false; - *strtab_shndx = this->elf_file_.section_link(shndx); - *location = this->elf_file_.section_contents(shndx); + + unsigned int relocs_shndx = + this->elf_file_.find_section_by_type(elfcpp::SHT_GNU_INCREMENTAL_RELOCS); + if (relocs_shndx == elfcpp::SHN_UNDEF) // Not found. + return false; + if (this->elf_file_.section_link(relocs_shndx) != inputs_shndx) + return false; + + unsigned int strtab_shndx = this->elf_file_.section_link(inputs_shndx); + if (strtab_shndx == elfcpp::SHN_UNDEF + || strtab_shndx > this->elf_file_.shnum() + || this->elf_file_.section_type(strtab_shndx) != elfcpp::SHT_STRTAB) + return false; + + if (p_inputs_shndx != NULL) + *p_inputs_shndx = inputs_shndx; + if (p_symtab_shndx != NULL) + *p_symtab_shndx = symtab_shndx; + if (p_relocs_shndx != NULL) + *p_relocs_shndx = relocs_shndx; + if (p_strtab_shndx != NULL) + *p_strtab_shndx = strtab_shndx; return true; } +// Determine whether an incremental link based on the existing output file +// can be done. + template bool Sized_incremental_binary::do_check_inputs( Incremental_inputs* incremental_inputs) { - const int entry_size = - Incremental_inputs_entry_write::data_size; - const int header_size = - Incremental_inputs_header_write::data_size; - + unsigned int inputs_shndx; + unsigned int symtab_shndx; + unsigned int relocs_shndx; unsigned int strtab_shndx; - Location location; - if (!do_find_incremental_inputs_section(&location, &strtab_shndx)) + if (!do_find_incremental_inputs_sections(&inputs_shndx, &symtab_shndx, + &relocs_shndx, &strtab_shndx)) { explain_no_incremental(_("no incremental data from previous build")); return false; } - if (location.data_size < header_size - || strtab_shndx >= this->elf_file_.shnum() - || this->elf_file_.section_type(strtab_shndx) != elfcpp::SHT_STRTAB) - { - explain_no_incremental(_("invalid incremental build data")); - return false; - } + Location inputs_location(this->elf_file_.section_contents(inputs_shndx)); + Location symtab_location(this->elf_file_.section_contents(symtab_shndx)); + Location relocs_location(this->elf_file_.section_contents(relocs_shndx)); Location strtab_location(this->elf_file_.section_contents(strtab_shndx)); - View data_view(view(location)); + + View inputs_view(view(inputs_location)); + View symtab_view(view(symtab_location)); + View relocs_view(view(relocs_location)); View strtab_view(view(strtab_location)); + elfcpp::Elf_strtab strtab(strtab_view.data(), strtab_location.data_size); - Incremental_inputs_header header(data_view.data()); - if (header.get_version() != INCREMENTAL_LINK_VERSION) - { - explain_no_incremental(_("different version of incremental build data")); - return false; - } + Incremental_inputs_reader + incoming_inputs(inputs_view.data(), strtab); - const char* command_line; - // We divide instead of multiplying to make sure there is no integer - // overflow. - size_t max_input_entries = (location.data_size - header_size) / entry_size; - if (header.get_input_file_count() > max_input_entries - || !strtab.get_c_string(header.get_command_line_offset(), &command_line)) + if (incoming_inputs.version() != INCREMENTAL_LINK_VERSION) { - explain_no_incremental(_("invalid incremental build data")); + explain_no_incremental(_("different version of incremental build data")); return false; } - if (incremental_inputs->command_line() != command_line) + if (incremental_inputs->command_line() != incoming_inputs.command_line()) { explain_no_incremental(_("command line changed")); return false; } // TODO: compare incremental_inputs->inputs() with entries in data_view. + return true; } @@ -182,8 +274,8 @@ make_sized_incremental_binary(Output_file* file, } // End of anonymous namespace. -// Create an Incremental_binary object for FILE. Returns NULL is this is not -// possible, e.g. FILE is not an ELF file or has an unsupported target. FILE +// Create an Incremental_binary object for FILE. Returns NULL is this is not +// possible, e.g. FILE is not an ELF file or has an unsupported target. FILE // should be opened. Incremental_binary* @@ -275,6 +367,8 @@ Incremental_checker::can_incrementally_link_output_file() return binary->check_inputs(this->incremental_inputs_); } +// Class Incremental_inputs. + // Add the command line to the string table, setting // command_line_key_. In incremental builds, the command line is // stored in .gnu_incremental_inputs so that the next linker run can @@ -289,7 +383,7 @@ Incremental_inputs::report_command_line(int argc, const char* const* argv) // Copied from collect_argv in main.cc. for (int i = 1; i < argc; ++i) { - // Adding/removing these options should result in a full relink. + // Adding/removing these options should not result in a full relink. if (strcmp(argv[i], "--incremental-changed") == 0 || strcmp(argv[i], "--incremental-unchanged") == 0 || strcmp(argv[i], "--incremental-unknown") == 0) @@ -315,99 +409,104 @@ Incremental_inputs::report_command_line(int argc, const char* const* argv) &this->command_line_key_); } -// Record that the input argument INPUT is an achive ARCHIVE. This is -// called by Read_symbols after finding out the type of the file. +// Record the input archive file ARCHIVE. This is called by the +// Add_archive_symbols task before determining which archive members +// to include. We create the Incremental_archive_entry here and +// attach it to the Archive, but we do not add it to the list of +// input objects until report_archive_end is called. void -Incremental_inputs::report_archive(const Input_argument* input, - Archive* archive) +Incremental_inputs::report_archive_begin(Archive* arch) { - Hold_lock hl(*this->lock_); + Stringpool::Key filename_key; + Timespec mtime = arch->file().get_mtime(); - Input_info info; - info.type = INCREMENTAL_INPUT_ARCHIVE; - info.archive = archive; - info.mtime = archive->file().get_mtime(); - this->inputs_map_.insert(std::make_pair(input, info)); + this->strtab_->add(arch->filename().c_str(), false, &filename_key); + Incremental_archive_entry* entry = + new Incremental_archive_entry(filename_key, arch, mtime); + arch->set_incremental_info(entry); } -// Record that the input argument INPUT is an object OBJ. This is -// called by Read_symbols after finding out the type of the file. +// Finish recording the input archive file ARCHIVE. This is called by the +// Add_archive_symbols task after determining which archive members +// to include. void -Incremental_inputs::report_object(const Input_argument* input, - Object* obj) +Incremental_inputs::report_archive_end(Archive* arch) { - Hold_lock hl(*this->lock_); - - Input_info info; - info.type = (obj->is_dynamic() - ? INCREMENTAL_INPUT_SHARED_LIBRARY - : INCREMENTAL_INPUT_OBJECT); - info.object = obj; - info.mtime = obj->input_file()->file().get_mtime(); - this->inputs_map_.insert(std::make_pair(input, info)); + Incremental_archive_entry* entry = arch->incremental_info(); + + gold_assert(entry != NULL); + + // Collect unused global symbols. + for (Archive::Unused_symbol_iterator p = arch->unused_symbols_begin(); + p != arch->unused_symbols_end(); + ++p) + { + Stringpool::Key symbol_key; + this->strtab_->add(*p, true, &symbol_key); + entry->add_unused_global_symbol(symbol_key); + } + this->inputs_.push_back(entry); } -// Record that the input argument INPUT is an script SCRIPT. This is -// called by read_script after parsing the script and reading the list -// of inputs added by this script. +// Record the input object file OBJ. If ARCH is not NULL, attach +// the object file to the archive. This is called by the +// Add_symbols task after finding out the type of the file. void -Incremental_inputs::report_script(const Input_argument* input, - Timespec mtime, - Script_info* script) +Incremental_inputs::report_object(Object* obj, Archive* arch) { - Hold_lock hl(*this->lock_); + Stringpool::Key filename_key; + Timespec mtime = obj->input_file()->file().get_mtime(); + + this->strtab_->add(obj->name().c_str(), false, &filename_key); + Incremental_object_entry* obj_entry = + new Incremental_object_entry(filename_key, obj, mtime); + this->inputs_.push_back(obj_entry); - Input_info info; - info.type = INCREMENTAL_INPUT_SCRIPT; - info.script = script; - info.mtime = mtime; - this->inputs_map_.insert(std::make_pair(input, info)); + if (arch != NULL) + { + Incremental_archive_entry* arch_entry = arch->incremental_info(); + gold_assert(arch_entry != NULL); + arch_entry->add_object(obj_entry); + } + + this->current_object_ = obj; + this->current_object_entry_ = obj_entry; } -// Compute indexes in the order in which the inputs should appear in -// .gnu_incremental_inputs. This needs to be done after all the -// scripts are parsed. The function is first called for the command -// line inputs arguments and may call itself recursively for e.g. a -// list of elements of a group or a list of inputs added by a script. -// The [BEGIN; END) interval to analyze and *INDEX is the current -// value of the index (that will be updated). +// Record the input object file OBJ. If ARCH is not NULL, attach +// the object file to the archive. This is called by the +// Add_symbols task after finding out the type of the file. void -Incremental_inputs::finalize_inputs( - Input_argument_list::const_iterator begin, - Input_argument_list::const_iterator end, - unsigned int* index) +Incremental_inputs::report_input_section(Object* obj, unsigned int shndx, + const char* name, off_t sh_size) { - for (Input_argument_list::const_iterator p = begin; p != end; ++p) - { - if (p->is_group()) - { - finalize_inputs(p->group()->begin(), p->group()->end(), index); - continue; - } + Stringpool::Key key = 0; - Inputs_info_map::iterator it = this->inputs_map_.find(&(*p)); - // TODO: turn it into an assert when the code will be more stable. - if (it == this->inputs_map_.end()) - { - gold_error("internal error: %s: incremental build info not provided", - (p->is_file() ? p->file().name() : "[group]")); - continue; - } - Input_info* info = &it->second; - info->index = *index; - (*index)++; - this->strtab_->add(p->file().name(), false, &info->filename_key); - if (info->type == INCREMENTAL_INPUT_SCRIPT) - { - finalize_inputs(info->script->inputs()->begin(), - info->script->inputs()->end(), - index); - } - } + if (name != NULL) + this->strtab_->add(name, true, &key); + + gold_assert(obj == this->current_object_); + this->current_object_entry_->add_input_section(shndx, key, sh_size); +} + +// Record that the input argument INPUT is a script SCRIPT. This is +// called by read_script after parsing the script and reading the list +// of inputs added by this script. + +void +Incremental_inputs::report_script(const std::string& filename, + Script_info* script, Timespec mtime) +{ + Stringpool::Key filename_key; + + this->strtab_->add(filename.c_str(), false, &filename_key); + Incremental_script_entry* entry = + new Incremental_script_entry(filename_key, script, mtime); + this->inputs_.push_back(entry); } // Finalize the incremental link information. Called from @@ -416,108 +515,425 @@ Incremental_inputs::finalize_inputs( void Incremental_inputs::finalize() { - unsigned int index = 0; - finalize_inputs(this->inputs_->begin(), this->inputs_->end(), &index); - - // Sanity check. - for (Inputs_info_map::const_iterator p = this->inputs_map_.begin(); - p != this->inputs_map_.end(); - ++p) - { - gold_assert(p->second.filename_key != 0); - } - + // Finalize the string table. this->strtab_->set_string_offsets(); } -// Create the content of the .gnu_incremental_inputs section. +// Create the .gnu_incremental_inputs, _symtab, and _relocs input sections. -Output_section_data* -Incremental_inputs::create_incremental_inputs_section_data() +void +Incremental_inputs::create_data_sections(Symbol_table* symtab) { switch (parameters->size_and_endianness()) { #ifdef HAVE_TARGET_32_LITTLE case Parameters::TARGET_32_LITTLE: - return this->sized_create_inputs_section_data<32, false>(); + this->inputs_section_ = + new Output_section_incremental_inputs<32, false>(this, symtab); + break; #endif #ifdef HAVE_TARGET_32_BIG case Parameters::TARGET_32_BIG: - return this->sized_create_inputs_section_data<32, true>(); + this->inputs_section_ = + new Output_section_incremental_inputs<32, true>(this, symtab); + break; #endif #ifdef HAVE_TARGET_64_LITTLE case Parameters::TARGET_64_LITTLE: - return this->sized_create_inputs_section_data<64, false>(); + this->inputs_section_ = + new Output_section_incremental_inputs<64, false>(this, symtab); + break; #endif #ifdef HAVE_TARGET_64_BIG case Parameters::TARGET_64_BIG: - return this->sized_create_inputs_section_data<64, true>(); + this->inputs_section_ = + new Output_section_incremental_inputs<64, true>(this, symtab); + break; #endif default: gold_unreachable(); } + this->symtab_section_ = new Output_data_space(4, "** incremental_symtab"); + this->relocs_section_ = new Output_data_space(4, "** incremental_relocs"); } -// Sized creation of .gnu_incremental_inputs section. +// Return the sh_entsize value for the .gnu_incremental_relocs section. +unsigned int +Incremental_inputs::relocs_entsize() const +{ + return 8 + 2 * parameters->target().get_size() / 8; +} + +// Class Output_section_incremental_inputs. + +// Finalize the offsets for each input section and supplemental info block, +// and set the final data size of the incremental output sections. template -Output_section_data* -Incremental_inputs::sized_create_inputs_section_data() +void +Output_section_incremental_inputs::set_final_data_size() { - const int entry_size = - Incremental_inputs_entry_write::data_size; - const int header_size = - Incremental_inputs_header_write::data_size; - - unsigned int sz = header_size + entry_size * this->inputs_map_.size(); - unsigned char* buffer = new unsigned char[sz]; - unsigned char* inputs_base = buffer + header_size; - - Incremental_inputs_header_write header_writer(buffer); - gold_assert(this->command_line_key_ > 0); - int cmd_offset = this->strtab_->get_offset_from_key(this->command_line_key_); - - header_writer.put_version(INCREMENTAL_LINK_VERSION); - header_writer.put_input_file_count(this->inputs_map_.size()); - header_writer.put_command_line_offset(cmd_offset); - header_writer.put_reserved(0); - - for (Inputs_info_map::const_iterator it = this->inputs_map_.begin(); - it != this->inputs_map_.end(); - ++it) + const Incremental_inputs* inputs = this->inputs_; + const unsigned int sizeof_addr = size / 8; + const unsigned int rel_size = 8 + 2 * sizeof_addr; + + // Offset of each input entry. + unsigned int input_offset = this->header_size; + + // Offset of each supplemental info block. + unsigned int info_offset = this->header_size; + info_offset += this->input_entry_size * inputs->input_file_count(); + + // Count each input file and its supplemental information block. + for (Incremental_inputs::Input_list::const_iterator p = + inputs->input_files().begin(); + p != inputs->input_files().end(); + ++p) { - gold_assert(it->second.index < this->inputs_map_.size()); - - unsigned char* entry_buffer = - inputs_base + it->second.index * entry_size; - Incremental_inputs_entry_write entry(entry_buffer); - int filename_offset = - this->strtab_->get_offset_from_key(it->second.filename_key); - entry.put_filename_offset(filename_offset); - switch (it->second.type) - { - case INCREMENTAL_INPUT_SCRIPT: - entry.put_data_offset(0); - break; - case INCREMENTAL_INPUT_ARCHIVE: - case INCREMENTAL_INPUT_OBJECT: - case INCREMENTAL_INPUT_SHARED_LIBRARY: - // TODO: add per input data. Currently we store - // an out-of-bounds offset for future version of gold to reject - // such an incremental_inputs section. - entry.put_data_offset(0xffffffff); - break; - default: - gold_unreachable(); - } - entry.put_timestamp_sec(it->second.mtime.seconds); - entry.put_timestamp_nsec(it->second.mtime.nanoseconds); - entry.put_input_type(it->second.type); - entry.put_reserved(0); + // Set the offset of the input file entry. + (*p)->set_offset(input_offset); + input_offset += this->input_entry_size; + + // Set the offset of the supplemental info block. + switch ((*p)->type()) + { + case INCREMENTAL_INPUT_SCRIPT: + // No supplemental info for a script. + (*p)->set_info_offset(0); + break; + case INCREMENTAL_INPUT_OBJECT: + case INCREMENTAL_INPUT_ARCHIVE_MEMBER: + { + Incremental_object_entry *entry = (*p)->object_entry(); + gold_assert(entry != NULL); + (*p)->set_info_offset(info_offset); + // Input section count + global symbol count. + info_offset += 8; + // Each input section. + info_offset += (entry->get_input_section_count() + * (8 + 2 * sizeof_addr)); + // Each global symbol. + const Object::Symbols* syms = entry->object()->get_global_symbols(); + info_offset += syms->size() * 16; + } + break; + case INCREMENTAL_INPUT_SHARED_LIBRARY: + { + Incremental_object_entry *entry = (*p)->object_entry(); + gold_assert(entry != NULL); + (*p)->set_info_offset(info_offset); + // Global symbol count. + info_offset += 4; + // Each global symbol. + const Object::Symbols* syms = entry->object()->get_global_symbols(); + unsigned int nsyms = syms != NULL ? syms->size() : 0; + info_offset += nsyms * 4; + } + break; + case INCREMENTAL_INPUT_ARCHIVE: + { + Incremental_archive_entry *entry = (*p)->archive_entry(); + gold_assert(entry != NULL); + (*p)->set_info_offset(info_offset); + // Member count + unused global symbol count. + info_offset += 8; + // Each member. + info_offset += (entry->get_member_count() * 4); + // Each global symbol. + info_offset += (entry->get_unused_global_symbol_count() * 4); + } + break; + default: + gold_unreachable(); + } } - return new Output_data_const_buffer(buffer, sz, 8, - "** incremental link inputs list"); + this->set_data_size(info_offset); + + // Set the size of the .gnu_incremental_symtab section. + inputs->symtab_section()->set_current_data_size(this->symtab_->output_count() + * sizeof(unsigned int)); + + // Set the size of the .gnu_incremental_relocs section. + inputs->relocs_section()->set_current_data_size(inputs->get_reloc_count() + * rel_size); +} + +// Write the contents of the .gnu_incremental_inputs and +// .gnu_incremental_symtab sections. + +template +void +Output_section_incremental_inputs::do_write(Output_file* of) +{ + const Incremental_inputs* inputs = this->inputs_; + Stringpool* strtab = inputs->get_stringpool(); + + // Get a view into the .gnu_incremental_inputs section. + const off_t off = this->offset(); + const off_t oview_size = this->data_size(); + unsigned char* const oview = of->get_output_view(off, oview_size); + unsigned char* pov = oview; + + // Get a view into the .gnu_incremental_symtab section. + const off_t symtab_off = inputs->symtab_section()->offset(); + const off_t symtab_size = inputs->symtab_section()->data_size(); + unsigned char* const symtab_view = of->get_output_view(symtab_off, + symtab_size); + + // Allocate an array of linked list heads for the .gnu_incremental_symtab + // section. Each element corresponds to a global symbol in the output + // symbol table, and points to the head of the linked list that threads + // through the object file input entries. The value of each element + // is the section-relative offset to a global symbol entry in a + // supplemental information block. + unsigned int global_sym_count = this->symtab_->output_count(); + unsigned int* global_syms = new unsigned int[global_sym_count]; + memset(global_syms, 0, global_sym_count * sizeof(unsigned int)); + + // Write the section header. + Stringpool::Key command_line_key = inputs->command_line_key(); + pov = this->write_header(pov, inputs->input_file_count(), + strtab->get_offset_from_key(command_line_key)); + + // Write the list of input files. + pov = this->write_input_files(oview, pov, strtab); + + // Write the supplemental information blocks for each input file. + pov = this->write_info_blocks(oview, pov, strtab, global_syms, + global_sym_count); + + gold_assert(pov - oview == oview_size); + + // Write the .gnu_incremental_symtab section. + gold_assert(global_sym_count * 4 == symtab_size); + this->write_symtab(symtab_view, global_syms, global_sym_count); + + delete[] global_syms; + + of->write_output_view(off, oview_size, oview); + of->write_output_view(symtab_off, symtab_size, symtab_view); +} + +// Write the section header: version, input file count, offset of command line +// in the string table, and 4 bytes of padding. + +template +unsigned char* +Output_section_incremental_inputs::write_header( + unsigned char* pov, + unsigned int input_file_count, + section_offset_type command_line_offset) +{ + Swap32::writeval(pov, INCREMENTAL_LINK_VERSION); + Swap32::writeval(pov + 4, input_file_count); + Swap32::writeval(pov + 8, command_line_offset); + Swap32::writeval(pov + 12, 0); + return pov + this->header_size; +} + +// Write the input file entries. + +template +unsigned char* +Output_section_incremental_inputs::write_input_files( + unsigned char* oview, + unsigned char* pov, + Stringpool* strtab) +{ + const Incremental_inputs* inputs = this->inputs_; + + for (Incremental_inputs::Input_list::const_iterator p = + inputs->input_files().begin(); + p != inputs->input_files().end(); + ++p) + { + gold_assert(pov - oview == (*p)->get_offset()); + section_offset_type filename_offset = + strtab->get_offset_from_key((*p)->get_filename_key()); + const Timespec& mtime = (*p)->get_mtime(); + Swap32::writeval(pov, filename_offset); + Swap32::writeval(pov + 4, (*p)->get_info_offset()); + Swap64::writeval(pov + 8, mtime.seconds); + Swap32::writeval(pov + 16, mtime.nanoseconds); + Swap16::writeval(pov + 20, (*p)->type()); + Swap16::writeval(pov + 22, 0); + pov += this->input_entry_size; + } + return pov; +} + +// Write the supplemental information blocks. + +template +unsigned char* +Output_section_incremental_inputs::write_info_blocks( + unsigned char* oview, + unsigned char* pov, + Stringpool* strtab, + unsigned int* global_syms, + unsigned int global_sym_count) +{ + const Incremental_inputs* inputs = this->inputs_; + unsigned int first_global_index = this->symtab_->first_global_index(); + + for (Incremental_inputs::Input_list::const_iterator p = + inputs->input_files().begin(); + p != inputs->input_files().end(); + ++p) + { + switch ((*p)->type()) + { + case INCREMENTAL_INPUT_SCRIPT: + // No supplemental info for a script. + break; + + case INCREMENTAL_INPUT_OBJECT: + case INCREMENTAL_INPUT_ARCHIVE_MEMBER: + { + gold_assert(pov - oview == (*p)->get_info_offset()); + Incremental_object_entry* entry = (*p)->object_entry(); + gold_assert(entry != NULL); + const Object* obj = entry->object(); + const Object::Symbols* syms = obj->get_global_symbols(); + // Write the input section count and global symbol count. + unsigned int nsections = entry->get_input_section_count(); + unsigned int nsyms = syms->size(); + Swap32::writeval(pov, nsections); + Swap32::writeval(pov + 4, nsyms); + pov += 8; + + // For each input section, write the name, output section index, + // offset within output section, and input section size. + for (unsigned int i = 0; i < nsections; i++) + { + Stringpool::Key key = entry->get_input_section_name_key(i); + off_t name_offset = 0; + if (key != 0) + name_offset = strtab->get_offset_from_key(key); + int out_shndx = 0; + off_t out_offset = 0; + off_t sh_size = 0; + Output_section* os = obj->output_section(i); + if (os != NULL) + { + out_shndx = os->out_shndx(); + out_offset = obj->output_section_offset(i); + sh_size = entry->get_input_section_size(i); + } + Swap32::writeval(pov, name_offset); + Swap32::writeval(pov + 4, out_shndx); + Swap::writeval(pov + 8, out_offset); + Swap::writeval(pov + 8 + sizeof_addr, sh_size); + pov += 8 + 2 * sizeof_addr; + } + + // For each global symbol, write its associated relocations, + // add it to the linked list of globals, then write the + // supplemental information: global symbol table index, + // linked list chain pointer, relocation count, and offset + // to the relocations. + for (unsigned int i = 0; i < nsyms; i++) + { + const Symbol* sym = (*syms)[i]; + unsigned int symtab_index = sym->symtab_index(); + unsigned int chain = 0; + unsigned int first_reloc = 0; + unsigned int nrelocs = obj->get_incremental_reloc_count(i); + if (nrelocs > 0) + { + gold_assert(symtab_index != -1U + && (symtab_index - first_global_index + < global_sym_count)); + first_reloc = obj->get_incremental_reloc_base(i); + chain = global_syms[symtab_index - first_global_index]; + global_syms[symtab_index - first_global_index] = + pov - oview; + } + Swap32::writeval(pov, symtab_index); + Swap32::writeval(pov + 4, chain); + Swap32::writeval(pov + 8, nrelocs); + Swap32::writeval(pov + 12, first_reloc * 3 * sizeof_addr); + pov += 16; + } + } + break; + + case INCREMENTAL_INPUT_SHARED_LIBRARY: + { + gold_assert(pov - oview == (*p)->get_info_offset()); + Incremental_object_entry* entry = (*p)->object_entry(); + gold_assert(entry != NULL); + const Object* obj = entry->object(); + const Object::Symbols* syms = obj->get_global_symbols(); + + // Write the global symbol count. + unsigned int nsyms = syms != NULL ? syms->size() : 0; + Swap32::writeval(pov, nsyms); + pov += 4; + + // For each global symbol, write the global symbol table index. + for (unsigned int i = 0; i < nsyms; i++) + { + const Symbol* sym = (*syms)[i]; + Swap32::writeval(pov, sym->symtab_index()); + pov += 4; + } + } + break; + + case INCREMENTAL_INPUT_ARCHIVE: + { + gold_assert(pov - oview == (*p)->get_info_offset()); + Incremental_archive_entry* entry = (*p)->archive_entry(); + gold_assert(entry != NULL); + + // Write the member count and unused global symbol count. + unsigned int nmembers = entry->get_member_count(); + unsigned int nsyms = entry->get_unused_global_symbol_count(); + Swap32::writeval(pov, nmembers); + Swap32::writeval(pov + 4, nsyms); + pov += 8; + + // For each member, write the offset to its input file entry. + for (unsigned int i = 0; i < nmembers; ++i) + { + Incremental_object_entry* member = entry->get_member(i); + Swap32::writeval(pov, member->get_offset()); + pov += 4; + } + + // For each global symbol, write the name offset. + for (unsigned int i = 0; i < nsyms; ++i) + { + Stringpool::Key key = entry->get_unused_global_symbol(i); + Swap32::writeval(pov, strtab->get_offset_from_key(key)); + pov += 4; + } + } + break; + + default: + gold_unreachable(); + } + } + return pov; +} + +// Write the contents of the .gnu_incremental_symtab section. + +template +void +Output_section_incremental_inputs::write_symtab( + unsigned char* pov, + unsigned int* global_syms, + unsigned int global_sym_count) +{ + for (unsigned int i = 0; i < global_sym_count; ++i) + { + Swap32::writeval(pov, global_syms[i]); + pov += 4; + } } // Instantiate the templates we need. diff --git a/gold/incremental.h b/gold/incremental.h index a94f39719c9..6fb1a324e74 100644 --- a/gold/incremental.h +++ b/gold/incremental.h @@ -1,6 +1,6 @@ // inremental.h -- incremental linking support for gold -*- C++ -*- -// Copyright 2009 Free Software Foundation, Inc. +// Copyright 2009, 2010 Free Software Foundation, Inc. // Written by Mikolaj Zalewski . // This file is part of gold. @@ -38,221 +38,21 @@ namespace gold class Archive; class Input_argument; class Incremental_inputs_checker; +class Incremental_script_entry; +class Incremental_object_entry; +class Incremental_archive_entry; +class Incremental_inputs; class Object; -class Output_section_data; // Incremental input type as stored in .gnu_incremental_inputs. enum Incremental_input_type { - INCREMENTAL_INPUT_INVALID = 0, INCREMENTAL_INPUT_OBJECT = 1, - INCREMENTAL_INPUT_ARCHIVE = 2, - INCREMENTAL_INPUT_SHARED_LIBRARY = 3, - INCREMENTAL_INPUT_SCRIPT = 4 -}; - -// Header of the .gnu_incremental_input section. -struct Incremental_inputs_header_data -{ - // Incremental linker version. - elfcpp::Elf_Word version; - - // Numer of input files in the link. - elfcpp::Elf_Word input_file_count; - - // Offset of command line options in .gnu_incremental_strtab. - elfcpp::Elf_Word command_line_offset; - - // Padding. - elfcpp::Elf_Word reserved; -}; - -// Reader class for .gnu_incremental_inputs header. See -// Incremental_inputs_header_data for fields descriptions. - -template -class Incremental_inputs_header -{ - private: - typedef Incremental_inputs_header_data Data_type; - typedef elfcpp::Convert<32, big_endian> Convert32; - - public: - Incremental_inputs_header(const unsigned char *p) - : p_(reinterpret_cast(p)) - { } - - static const int data_size = sizeof(Data_type); - - elfcpp::Elf_Word - get_version() const - { return Convert32::convert_host(this->p_->version); } - - elfcpp::Elf_Word - get_input_file_count() const - { return Convert32::convert_host(this->p_->input_file_count); } - - elfcpp::Elf_Word - get_command_line_offset() const - { return Convert32::convert_host(this->p_->command_line_offset); } - - elfcpp::Elf_Word - get_reserved() const - { return Convert32::convert_host(this->p_->reserved); } - - private: - const Data_type* p_; -}; - -// Writer class for .gnu_incremental_inputs header. See -// Incremental_inputs_header_data for fields descriptions. - -template -class Incremental_inputs_header_write -{ - private: - typedef Incremental_inputs_header_data Data_type; - typedef elfcpp::Convert<32, big_endian> Convert32; - - public: - Incremental_inputs_header_write(unsigned char *p) - : p_(reinterpret_cast(p)) - { } - - static const int data_size = sizeof(Data_type); - - void - put_version(elfcpp::Elf_Word v) - { this->p_->version = Convert32::convert_host(v); } - - void - put_input_file_count(elfcpp::Elf_Word v) - { this->p_->input_file_count = Convert32::convert_host(v); } - - void - put_command_line_offset(elfcpp::Elf_Word v) - { this->p_->command_line_offset = Convert32::convert_host(v); } - - void - put_reserved(elfcpp::Elf_Word v) - { this->p_->reserved = Convert32::convert_host(v); } - - private: - Data_type* p_; -}; - -// Data stored in .gnu_incremental_input after the header for each of the -// Incremental_input_header_data::input_file_count input entries. -struct Incremental_inputs_entry_data -{ - // Offset of file name in .gnu_incremental_strtab section. - elfcpp::Elf_Word filename_offset; - - // Offset of data in .gnu_incremental_input. - elfcpp::Elf_Word data_offset; - - // Timestamp (in seconds). - elfcpp::Elf_Xword timestamp_sec; - - // Nano-second part of timestamp (if supported). - elfcpp::Elf_Word timestamp_nsec; - - // Type of the input entry. - elfcpp::Elf_Half input_type; - - // Padding. - elfcpp::Elf_Half reserved; -}; - -// Reader class for an .gnu_incremental_inputs entry. See -// Incremental_inputs_entry_data for fields descriptions. -template -class Incremental_inputs_entry -{ - private: - typedef Incremental_inputs_entry_data Data_type; - typedef elfcpp::Convert<32, big_endian> Convert32; - typedef elfcpp::Convert<64, big_endian> Convert64; - - public: - Incremental_inputs_entry(const unsigned char *p) - : p_(reinterpret_cast(p)) - { } - - static const int data_size = sizeof(Data_type); - - elfcpp::Elf_Word - get_filename_offset() - { return Convert32::convert_host(this->p_->filename_offset); } - - elfcpp::Elf_Word - get_data_offset() - { return Convert32::convert_host(this->p_->data_offset); } - - elfcpp::Elf_Xword - get_timestamp_sec() - { return Convert64::convert_host(this->p_->timestamp_sec); } - - elfcpp::Elf_Word - get_timestamp_nsec() - { return Convert32::convert_host(this->p_->timestamp_nsec); } - - elfcpp::Elf_Word - get_input_type() - { return Convert32::convert_host(this->p_->input_type); } - - elfcpp::Elf_Word - get_reserved() - { return Convert32::convert_host(this->p_->reserved); } - - private: - const Data_type* p_; -}; - -// Writer class for an .gnu_incremental_inputs entry. See -// Incremental_inputs_entry_data for fields descriptions. -template -class Incremental_inputs_entry_write -{ - private: - typedef Incremental_inputs_entry_data Data_type; - typedef elfcpp::Convert<32, big_endian> Convert32; - typedef elfcpp::Convert<64, big_endian> Convert64; - - public: - Incremental_inputs_entry_write(unsigned char *p) - : p_(reinterpret_cast(p)) - { } - - static const int data_size = sizeof(Data_type); - - void - put_filename_offset(elfcpp::Elf_Word v) - { this->p_->filename_offset = Convert32::convert_host(v); } - - void - put_data_offset(elfcpp::Elf_Word v) - { this->p_->data_offset = Convert32::convert_host(v); } - - void - put_timestamp_sec(elfcpp::Elf_Xword v) - { this->p_->timestamp_sec = Convert64::convert_host(v); } - - void - put_timestamp_nsec(elfcpp::Elf_Word v) - { this->p_->timestamp_nsec = Convert32::convert_host(v); } - - void - put_input_type(elfcpp::Elf_Word v) - { this->p_->input_type = Convert32::convert_host(v); } - - void - put_reserved(elfcpp::Elf_Word v) - { this->p_->reserved = Convert32::convert_host(v); } - - private: - Data_type* p_; + INCREMENTAL_INPUT_ARCHIVE_MEMBER = 2, + INCREMENTAL_INPUT_ARCHIVE = 3, + INCREMENTAL_INPUT_SHARED_LIBRARY = 4, + INCREMENTAL_INPUT_SCRIPT = 5 }; // An object representing the ELF file we edit during an incremental build. @@ -315,20 +115,27 @@ class Incremental_binary }; // Get a View given a Location. - View view(Location loc) + View + view(Location loc) { return View(this->view(loc.file_offset, loc.data_size)); } // Report an error. void error(const char* format, ...) const ATTRIBUTE_PRINTF_2; - // Find the .gnu_incremental_inputs section. It selects the first section - // of type SHT_GNU_INCREMENTAL_INPUTS. Returns false if such a section - // is not found. + // Find the .gnu_incremental_inputs and related sections. It selects the + // first section of type SHT_GNU_INCREMENTAL_INPUTS, + // SHT_GNU_INCRMENTAL_SYMTAB, and SHT_GNU_INCREMENTAL_RELOCS. + // Returns false if the sections are not found. bool - find_incremental_inputs_section(Location* location, - unsigned int* strtab_shndx) - { return do_find_incremental_inputs_section(location, strtab_shndx); } + find_incremental_inputs_sections(unsigned int* p_inputs_shndx, + unsigned int* p_symtab_shndx, + unsigned int* p_relocs_shndx, + unsigned int* p_strtab_shndx) + { + return do_find_incremental_inputs_sections(p_inputs_shndx, p_symtab_shndx, + p_relocs_shndx, p_strtab_shndx); + } // Check the .gnu_incremental_inputs section to see whether an incremental // build is possible. @@ -343,8 +150,10 @@ class Incremental_binary protected: // Find incremental inputs section. virtual bool - do_find_incremental_inputs_section(Location* location, - unsigned int* strtab_shndx) = 0; + do_find_incremental_inputs_sections(unsigned int* p_inputs_shndx, + unsigned int* p_symtab_shndx, + unsigned int* p_relocs_shndx, + unsigned int* p_strtab_shndx) = 0; // Check the .gnu_incremental_inputs section to see whether an incremental // build is possible. @@ -370,8 +179,10 @@ class Sized_incremental_binary : public Incremental_binary protected: virtual bool - do_find_incremental_inputs_section(Location* location, - unsigned int* strtab_shndx); + do_find_incremental_inputs_sections(unsigned int* p_inputs_shndx, + unsigned int* p_symtab_shndx, + unsigned int* p_relocs_shndx, + unsigned int* p_strtab_shndx); virtual bool do_check_inputs(Incremental_inputs* incremental_inputs); @@ -383,11 +194,13 @@ class Sized_incremental_binary : public Incremental_binary // Create an Incremental_binary object for FILE. Returns NULL is this is not // possible, e.g. FILE is not an ELF file or has an unsupported target. + Incremental_binary* open_incremental_binary(Output_file* file); // Code invoked early during an incremental link that checks what files need // to be relinked. + class Incremental_checker { public: @@ -415,135 +228,726 @@ class Incremental_checker Incremental_inputs* incremental_inputs_; }; +// Base class for recording each input file. + +class Incremental_input_entry +{ + public: + Incremental_input_entry(Stringpool::Key filename_key, Timespec mtime) + : filename_key_(filename_key), offset_(0), info_offset_(0), mtime_(mtime) + { } + + // Return the type of input file. + Incremental_input_type + type() const + { return this->do_type(); } + + // Set the section offset of this input file entry. + void + set_offset(unsigned int offset) + { 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 section offset of this input file entry. + unsigned int + get_offset() const + { return this->offset_; } + + // Get the section offset of the supplemental information for this entry. + unsigned int + get_info_offset() const + { return this->info_offset_; } + + // Get the stringpool key for the input filename. + Stringpool::Key + get_filename_key() const + { return this->filename_key_; } + + // Get the modification time of the input file. + const Timespec& + get_mtime() const + { return this->mtime_; } + + // Return a pointer to the derived Incremental_script_entry object. + // Return NULL for input entries that are not script files. + Incremental_script_entry* + script_entry() + { return this->do_script_entry(); } + + // Return a pointer to the derived Incremental_object_entry object. + // Return NULL for input entries that are not object files. + Incremental_object_entry* + object_entry() + { return this->do_object_entry(); } + + // Return a pointer to the derived Incremental_archive_entry object. + // Return NULL for input entries that are not archive files. + Incremental_archive_entry* + archive_entry() + { return this->do_archive_entry(); } + + protected: + // Return the type of input file. + virtual Incremental_input_type + do_type() const = 0; + + // Return a pointer to the derived Incremental_script_entry object. + // Return NULL for input entries that are not script files. + virtual Incremental_script_entry* + do_script_entry() + { return NULL; } + + // Return a pointer to the derived Incremental_object_entry object. + // Return NULL for input entries that are not object files. + virtual Incremental_object_entry* + do_object_entry() + { return NULL; } + + // Return a pointer to the derived Incremental_archive_entry object. + // Return NULL for input entries that are not archive files. + virtual Incremental_archive_entry* + do_archive_entry() + { return NULL; } + + private: + // Key of the filename string in the section stringtable. + Stringpool::Key filename_key_; + + // Offset of the entry in the output section. + unsigned int offset_; + + // Offset of the extra information in the output section. + unsigned int info_offset_; + + // Last modification time of the file. + Timespec mtime_; +}; + +// Class for recording input scripts. + +class Incremental_script_entry : public Incremental_input_entry +{ + public: + Incremental_script_entry(Stringpool::Key filename_key, Script_info* script, + Timespec mtime) + : Incremental_input_entry(filename_key, mtime), script_(script) + { } + + protected: + virtual Incremental_input_type + do_type() const + { return INCREMENTAL_INPUT_SCRIPT; } + + // Return a pointer to the derived Incremental_script_entry object. + virtual Incremental_script_entry* + do_script_entry() + { return this; } + + private: + // Information about the script file. + Script_info* script_; +}; + +// Class for recording input object files. + +class Incremental_object_entry : public Incremental_input_entry +{ + public: + Incremental_object_entry(Stringpool::Key filename_key, Object* obj, + Timespec mtime) + : Incremental_input_entry(filename_key, mtime), obj_(obj), + is_member_(false), sections_() + { + if (!obj_->is_dynamic()) + this->sections_.reserve(obj->shnum()); + } + + // Get the object. + Object* + object() const + { return this->obj_; } + + // Record that this object is an archive member. + void + set_is_member() + { this->is_member_ = true; } + + // Return true if this object is an archive member. + bool + is_member() const + { return this->is_member_; } + + // Add an input section. + void + add_input_section(unsigned int shndx, Stringpool::Key name_key, off_t sh_size) + { + if (shndx >= this->sections_.size()) + this->sections_.resize(shndx + 1); + this->sections_[shndx].name_key = name_key; + this->sections_[shndx].sh_size = sh_size; + } + + // Return the number of input sections in this object. + unsigned int + get_input_section_count() const + { return this->sections_.size(); } + + // Return the stringpool key of the Nth input section. + Stringpool::Key + get_input_section_name_key(unsigned int n) const + { return this->sections_[n].name_key; } + + // Return the size of the Nth input section. + off_t + get_input_section_size(unsigned int n) const + { return this->sections_[n].sh_size; } + + protected: + virtual Incremental_input_type + do_type() const + { + return (this->is_member_ + ? INCREMENTAL_INPUT_ARCHIVE_MEMBER + : (this->obj_->is_dynamic() + ? INCREMENTAL_INPUT_SHARED_LIBRARY + : INCREMENTAL_INPUT_OBJECT)); + } + + // Return a pointer to the derived Incremental_object_entry object. + virtual Incremental_object_entry* + do_object_entry() + { return this; } + + private: + // The object file itself. + Object* obj_; + + // Whether this object is an archive member. + bool is_member_; + + // Input sections. + struct Input_section + { + Stringpool::Key name_key; + off_t sh_size; + }; + std::vector sections_; +}; + +// Class for recording archive library input files. + +class Incremental_archive_entry : public Incremental_input_entry +{ + public: + Incremental_archive_entry(Stringpool::Key filename_key, Archive*, + Timespec mtime) + : Incremental_input_entry(filename_key, mtime), members_(), unused_syms_() + { } + + // Add a member object to the archive. + void + add_object(Incremental_object_entry* obj_entry) + { + this->members_.push_back(obj_entry); + obj_entry->set_is_member(); + } + + // Add an unused global symbol to the archive. + void + add_unused_global_symbol(Stringpool::Key symbol_key) + { this->unused_syms_.push_back(symbol_key); } + + // Return the number of member objects included in the link. + unsigned int + get_member_count() + { return this->members_.size(); } + + // Return the Nth member object. + Incremental_object_entry* + get_member(unsigned int n) + { return this->members_[n]; } + + // Return the number of unused global symbols in this archive. + unsigned int + get_unused_global_symbol_count() + { return this->unused_syms_.size(); } + + // Return the Nth unused global symbol. + Stringpool::Key + get_unused_global_symbol(unsigned int n) + { return this->unused_syms_[n]; } + + protected: + virtual Incremental_input_type + do_type() const + { return INCREMENTAL_INPUT_ARCHIVE; } + + // Return a pointer to the derived Incremental_archive_entry object. + virtual Incremental_archive_entry* + do_archive_entry() + { return this; } + + private: + // Members of the archive that have been included in the link. + std::vector members_; + + // Unused global symbols from this archive. + std::vector unused_syms_; +}; + // This class contains the information needed during an incremental // build about the inputs necessary to build the .gnu_incremental_inputs. + class Incremental_inputs { public: + typedef std::vector Input_list; + Incremental_inputs() - : lock_(new Lock()), inputs_(NULL), command_line_key_(0), - strtab_(new Stringpool()) + : inputs_(), command_line_(), command_line_key_(0), + strtab_(new Stringpool()), current_object_(NULL), + current_object_entry_(NULL), inputs_section_(NULL), + symtab_section_(NULL), relocs_section_(NULL), + reloc_count_(0) { } + ~Incremental_inputs() { delete this->strtab_; } // Record the command line. void report_command_line(int argc, const char* const* argv); - // Record the input arguments obtained from parsing the command line. + // Record the initial info for archive file ARCHIVE. + void + report_archive_begin(Archive* arch); + + // Record the final info for archive file ARCHIVE. + void + report_archive_end(Archive* arch); + + // Record the info for object file OBJ. If ARCH is not NULL, + // attach the object file to the archive. void - report_inputs(const Input_arguments& inputs) - { this->inputs_ = &inputs; } + report_object(Object* obj, Archive* arch); - // Record that the input argument INPUT is an archive ARCHIVE. + // Record an input section belonging to object file OBJ. void - report_archive(const Input_argument* input, Archive* archive); + report_input_section(Object* obj, unsigned int shndx, const char* name, + off_t sh_size); - // Record that the input argument INPUT is to an object OBJ. + // Record the info for input script SCRIPT. void - report_object(const Input_argument* input, Object* obj); + report_script(const std::string& filename, Script_info* script, + Timespec mtime); + + // Return the running count of incremental relocations. + unsigned int + get_reloc_count() const + { return this->reloc_count_; } - // Record that the input argument INPUT is to an script SCRIPT. + // Update the running count of incremental relocations. void - report_script(const Input_argument* input, Timespec mtime, - Script_info* script); + set_reloc_count(unsigned int count) + { this->reloc_count_ = count; } // Prepare for layout. Called from Layout::finalize. void finalize(); - // Create the content of the .gnu_incremental_inputs section. + // Create the .gnu_incremental_inputs and related sections. + void + create_data_sections(Symbol_table* symtab); + + // Return the .gnu_incremental_inputs section. Output_section_data* - create_incremental_inputs_section_data(); + inputs_section() const + { return this->inputs_section_; } + + // Return the .gnu_incremental_symtab section. + Output_data_space* + symtab_section() const + { return this->symtab_section_; } + + // Return the .gnu_incremental_relocs section. + Output_data_space* + relocs_section() const + { return this->relocs_section_; } // Return the .gnu_incremental_strtab stringpool. Stringpool* - get_stringpool() + get_stringpool() const { return this->strtab_; } // Return the canonical form of the command line, as will be stored in // .gnu_incremental_strtab. const std::string& - command_line() + command_line() const { return this->command_line_; } - // Return the input files found in the command line. - const Input_arguments* - inputs() + // Return the stringpool key of the command line. + Stringpool::Key + command_line_key() const + { return this->command_line_key_; } + + // Return the number of input files. + int + input_file_count() const + { return this->inputs_.size(); } + + // Return the input files. + const Input_list& + input_files() const { return this->inputs_; } + // Return the sh_entsize value for the .gnu_incremental_relocs section. + unsigned int + relocs_entsize() const; + private: - // Code for each of the four possible variants of create_inputs_section_data. - template - Output_section_data* - sized_create_inputs_section_data(); + // The list of input files. + Input_list inputs_; - // Compute indexes in the order in which the inputs should appear in - // .gnu_incremental_inputs and put file names to the stringtable. - // This needs to be done after all the scripts are parsed. + // Canonical form of the command line, as will be stored in + // .gnu_incremental_strtab. + std::string command_line_; - void - finalize_inputs(Input_argument_list::const_iterator begin, - Input_argument_list::const_iterator end, - unsigned int* index); + // The key of the command line string in the string pool. + Stringpool::Key command_line_key_; + + // The .gnu_incremental_strtab string pool associated with the + // .gnu_incremental_inputs. + Stringpool* strtab_; + + // Keep track of the object currently being processed. + Object* current_object_; + Incremental_object_entry* current_object_entry_; + + // The .gnu_incremental_inputs section. + Output_section_data* inputs_section_; - // Additional data about an input needed for an incremental link. - // None of these pointers is owned by the structure. - struct Input_info + // The .gnu_incremental_symtab section. + Output_data_space* symtab_section_; + + // The .gnu_incremental_relocs section. + Output_data_space* relocs_section_; + + // Total count of incremental relocations. Updated during Scan_relocs + // phase at the completion of each object file. + unsigned int reloc_count_; +}; + +// Reader class for .gnu_incremental_inputs section. + +template +class Incremental_inputs_reader +{ + private: + typedef elfcpp::Swap Swap; + typedef elfcpp::Swap<16, big_endian> Swap16; + typedef elfcpp::Swap<32, big_endian> Swap32; + typedef elfcpp::Swap<64, big_endian> Swap64; + + public: + Incremental_inputs_reader(const unsigned char* p, elfcpp::Elf_strtab& strtab) + : p_(p), strtab_(strtab) + { this->input_file_count_ = Swap32::readval(this->p_ + 4); } + + // Return the version number. + unsigned int + version() const + { return Swap32::readval(this->p_); } + + // Return the count of input file entries. + unsigned int + input_file_count() const + { return this->input_file_count_; } + + // Return the command line. + const char* + command_line() const { - Input_info() - : type(INCREMENTAL_INPUT_INVALID), archive(NULL), filename_key(0), - index(0) - { } + unsigned int offset = Swap32::readval(this->p_ + 8); + return this->get_string(offset); + } - // Type of the file pointed by this argument. - Incremental_input_type type; + // Reader class for an input file entry and its supplemental info. + class Incremental_input_entry_reader + { + public: + Incremental_input_entry_reader(const Incremental_inputs_reader* inputs, + unsigned int offset) + : inputs_(inputs), offset_(offset) + { + this->info_offset_ = Swap32::readval(inputs->p_ + offset + 4); + int type = Swap16::readval(this->inputs_->p_ + offset + 20); + this->type_ = static_cast(type); + } + + // Return the filename. + const char* + filename() const + { + unsigned int offset = Swap32::readval(this->inputs_->p_ + this->offset_); + return this->inputs_->get_string(offset); + } - union + // Return the timestamp. + Timespec + get_mtime() const + { + Timespec t; + const unsigned char* p = this->inputs_->p_ + this->offset_ + 8; + t.seconds = Swap64::readval(p); + t.nanoseconds = Swap32::readval(p+8); + return t; + } + + // Return the type of input file. + Incremental_input_type + type() const + { return this->type_; } + + // Return the input section count -- for objects only. + unsigned int + get_input_section_count() const { - // Present if type == INCREMENTAL_INPUT_ARCHIVE. - Archive* archive; + gold_assert(this->type_ == INCREMENTAL_INPUT_OBJECT + || this->type_ == INCREMENTAL_INPUT_ARCHIVE_MEMBER); + return Swap32::readval(this->inputs_->p_ + this->info_offset_); + } + + // Return the offset of the supplemental info for symbol SYMNDX -- + // for objects only. + unsigned int + get_symbol_offset(unsigned int symndx) const + { + gold_assert(this->type_ == INCREMENTAL_INPUT_OBJECT + || this->type_ == INCREMENTAL_INPUT_ARCHIVE_MEMBER); + + unsigned int section_count = this->get_input_section_count(); + return (this->info_offset_ + 8 + + section_count * input_section_entry_size + + symndx * 16); + } + + // Return the global symbol count -- for objects & shared libraries only. + unsigned int + get_global_symbol_count() const + { + switch (this->type_) + { + case INCREMENTAL_INPUT_OBJECT: + case INCREMENTAL_INPUT_ARCHIVE_MEMBER: + return Swap32::readval(this->inputs_->p_ + this->info_offset_ + 4); + case INCREMENTAL_INPUT_SHARED_LIBRARY: + return Swap32::readval(this->inputs_->p_ + this->info_offset_); + default: + gold_unreachable(); + } + } + + // Return the member count -- for archives only. + unsigned int + get_member_count() const + { + gold_assert(this->type_ == INCREMENTAL_INPUT_ARCHIVE); + return Swap32::readval(this->inputs_->p_ + this->info_offset_); + } - // Present if type == INCREMENTAL_INPUT_OBJECT or - // INCREMENTAL_INPUT_SHARED_LIBRARY. - Object* object; + // Return the unused symbol count -- for archives only. + unsigned int + get_unused_symbol_count() const + { + gold_assert(this->type_ == INCREMENTAL_INPUT_ARCHIVE); + return Swap32::readval(this->inputs_->p_ + this->info_offset_ + 4); + } - // Present if type == INCREMENTAL_INPUT_SCRIPT. - Script_info* script; + // Return the input file offset for archive member N -- for archives only. + unsigned int + get_member_offset(unsigned int n) const + { + gold_assert(this->type_ == INCREMENTAL_INPUT_ARCHIVE); + return Swap32::readval(this->inputs_->p_ + this->info_offset_ + + 8 + n * 4); + } + + // Return the Nth unused global symbol -- for archives only. + const char* + get_unused_symbol(unsigned int n) const + { + gold_assert(this->type_ == INCREMENTAL_INPUT_ARCHIVE); + unsigned int member_count = this->get_member_count(); + unsigned int offset = Swap32::readval(this->inputs_->p_ + + this->info_offset_ + 8 + + member_count * 4 + + n * 4); + return this->inputs_->get_string(offset); + } + + // Information about an input section. + struct Input_section_info + { + const char* name; + unsigned int output_shndx; + off_t sh_offset; + off_t sh_size; }; - // Key of the filename string in the section stringtable. - Stringpool::Key filename_key; + // Return info about the Nth input section -- for objects only. + Input_section_info + get_input_section(unsigned int n) const + { + Input_section_info info; + const unsigned char* p = (this->inputs_->p_ + + this->info_offset_ + 8 + + n * input_section_entry_size); + unsigned int name_offset = Swap32::readval(p); + info.name = this->inputs_->get_string(name_offset); + info.output_shndx = Swap32::readval(p + 4); + info.sh_offset = Swap::readval(p + 8); + info.sh_size = Swap::readval(p + 8 + size / 8); + return info; + } + + // Information about a global symbol. + struct Global_symbol_info + { + unsigned int output_symndx; + unsigned int next_offset; + unsigned int reloc_count; + unsigned int reloc_offset; + }; - // Position of the entry information in the output section. - unsigned int index; + // Return info about the Nth global symbol -- for objects only. + Global_symbol_info + get_global_symbol_info(unsigned int n) const + { + Global_symbol_info info; + unsigned int section_count = this->get_input_section_count(); + const unsigned char* p = (this->inputs_->p_ + + this->info_offset_ + 8 + + section_count * input_section_entry_size + + n * 16); + info.output_symndx = Swap32::readval(p); + info.next_offset = Swap32::readval(p + 4); + info.reloc_count = Swap::readval(p + 8); + info.reloc_offset = Swap::readval(p + 12); + return info; + } - // Last modification time of the file. - Timespec mtime; + private: + // Size of an input section entry. + static const unsigned int input_section_entry_size = 8 + 2 * size / 8; + // The reader instance for the containing section. + const Incremental_inputs_reader* inputs_; + // The type of input file. + Incremental_input_type type_; + // Section offset to the input file entry. + unsigned int offset_; + // Section offset to the supplemental info for the input file. + unsigned int info_offset_; }; - typedef std::map Inputs_info_map; + // Return a reader for the Nth input file entry. + Incremental_input_entry_reader + input_file(unsigned int n) const + { + gold_assert(n < this->input_file_count_); + Incremental_input_entry_reader input(this, 16 + n * 24); + return input; + } + + private: + // Lookup a string in the ELF string table. + const char* get_string(unsigned int offset) const + { + const char* s; + if (this->strtab_.get_c_string(offset, &s)) + return s; + return NULL; + } + + // Base address of the .gnu_incremental_inputs section. + const unsigned char* p_; + // The associated ELF string table. + elfcpp::Elf_strtab strtab_; + // The number of input file entries in this section. + unsigned int input_file_count_; +}; + +// Reader class for the .gnu_incremental_symtab section. - // A lock guarding access to inputs_ during the first phase of linking, when - // report_ function may be called from multiple threads. - Lock* lock_; +template +class Incremental_symtab_reader +{ + public: + Incremental_symtab_reader(const unsigned char* p) : p_(p) + { } - // The list of input arguments obtained from parsing the command line. - const Input_arguments* inputs_; + // Return the list head for symbol table entry N. + unsigned int get_list_head(unsigned int n) + { return elfcpp::Swap<32, big_endian>::readval(this->p_ + 4 * n); } - // A map containing additional information about the input elements. - Inputs_info_map inputs_map_; + private: + // Base address of the .gnu_incremental_relocs section. + const unsigned char* p_; +}; - // Canonical form of the command line, as will be stored in - // .gnu_incremental_strtab. - std::string command_line_; +// Reader class for the .gnu_incremental_relocs section. - // The key of the command line string in the string pool. - Stringpool::Key command_line_key_; +template +class Incremental_relocs_reader +{ + private: + // Size of each field. + static const unsigned int field_size = size / 8; - // The .gnu_incremental_strtab string pool associated with the - // .gnu_incremental_inputs. - Stringpool* strtab_; + public: + typedef typename elfcpp::Elf_types::Elf_Addr Address; + typedef typename elfcpp::Elf_types::Elf_Swxword Addend; + + // Size of each entry. + static const unsigned int reloc_size = 8 + 2 * field_size; + + Incremental_relocs_reader(const unsigned char* p) : p_(p) + { } + + // Return the relocation type for relocation entry at offset OFF. + unsigned int + get_r_type(unsigned int off) + { + return elfcpp::Swap<32, big_endian>::readval(this->p_ + off); + } + + // Return the output section index for relocation entry at offset OFF. + unsigned int + get_r_shndx(unsigned int off) + { + return elfcpp::Swap<32, big_endian>::readval(this->p_ + off + 4); + } + + // Return the output section offset for relocation entry at offset OFF. + Address + get_r_offset(unsigned int off) + { + return elfcpp::Swap::readval(this->p_ + off + 8); + } + + // Return the addend for relocation entry at offset OFF. + Addend + get_r_addend(unsigned int off) + { + return elfcpp::Swap::readval(this->p_ + off + 8 + + this->field_size); + } + + private: + // Base address of the .gnu_incremental_relocs section. + const unsigned char* p_; }; } // End namespace gold. diff --git a/gold/layout.cc b/gold/layout.cc index 3a01e927409..eb1322ae6af 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -974,7 +974,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type, if (this->debug_abbrev_) this->debug_info_->set_abbreviations(this->debug_abbrev_); } - else + else { // FIXME: const_cast is ugly. Target* target = const_cast(¶meters->target()); @@ -1886,12 +1886,6 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab, this->set_dynamic_symbol_size(symtab); } - if (this->incremental_inputs_) - { - this->incremental_inputs_->finalize(); - this->create_incremental_info_sections(); - } - // Create segment headers. Output_segment_headers* segment_headers = (parameters->options().relocatable() @@ -1951,6 +1945,13 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab, // be called after the symbol table has been finalized. this->script_options_->finalize_symbols(symtab, this); + // Create the incremental inputs sections. + if (this->incremental_inputs_) + { + this->incremental_inputs_->finalize(); + this->create_incremental_info_sections(symtab); + } + // Create the .shstrtab section. Output_section* shstrtab_section = this->create_shstrtab(); @@ -1968,8 +1969,13 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab, // If there are no sections which require postprocessing, we can // handle the section names now, and avoid a resize later. if (!this->any_postprocessing_sections_) - off = this->set_section_offsets(off, + { + off = this->set_section_offsets(off, + POSTPROCESSING_SECTIONS_PASS); + off = + this->set_section_offsets(off, STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS); + } file_header->set_section_info(this->section_headers_, shstrtab_section); @@ -2294,37 +2300,65 @@ Layout::link_stabs_sections() } } -// Create .gnu_incremental_inputs and .gnu_incremental_strtab sections needed +// Create .gnu_incremental_inputs and related sections needed // for the next run of incremental linking to check what has changed. void -Layout::create_incremental_info_sections() +Layout::create_incremental_info_sections(Symbol_table* symtab) { - gold_assert(this->incremental_inputs_ != NULL); + Incremental_inputs* incr = this->incremental_inputs_; + + gold_assert(incr != NULL); + + // Create the .gnu_incremental_inputs, _symtab, and _relocs input sections. + incr->create_data_sections(symtab); // Add the .gnu_incremental_inputs section. const char *incremental_inputs_name = this->namepool_.add(".gnu_incremental_inputs", false, NULL); - Output_section* inputs_os = + Output_section* incremental_inputs_os = this->make_output_section(incremental_inputs_name, elfcpp::SHT_GNU_INCREMENTAL_INPUTS, 0, ORDER_INVALID, false); - Output_section_data* posd = - this->incremental_inputs_->create_incremental_inputs_section_data(); - inputs_os->add_output_section_data(posd); - + incremental_inputs_os->add_output_section_data(incr->inputs_section()); + + // Add the .gnu_incremental_symtab section. + const char *incremental_symtab_name = + this->namepool_.add(".gnu_incremental_symtab", false, NULL); + Output_section* incremental_symtab_os = + this->make_output_section(incremental_symtab_name, + elfcpp::SHT_GNU_INCREMENTAL_SYMTAB, 0, + ORDER_INVALID, false); + incremental_symtab_os->add_output_section_data(incr->symtab_section()); + incremental_symtab_os->set_entsize(4); + + // Add the .gnu_incremental_relocs section. + const char *incremental_relocs_name = + this->namepool_.add(".gnu_incremental_relocs", false, NULL); + Output_section* incremental_relocs_os = + this->make_output_section(incremental_relocs_name, + elfcpp::SHT_GNU_INCREMENTAL_RELOCS, 0, + ORDER_INVALID, false); + incremental_relocs_os->add_output_section_data(incr->relocs_section()); + incremental_relocs_os->set_entsize(incr->relocs_entsize()); + // Add the .gnu_incremental_strtab section. const char *incremental_strtab_name = this->namepool_.add(".gnu_incremental_strtab", false, NULL); - Output_section* strtab_os = this->make_output_section(incremental_strtab_name, - elfcpp::SHT_STRTAB, - 0, ORDER_INVALID, - false); + Output_section* incremental_strtab_os = this->make_output_section(incremental_strtab_name, + elfcpp::SHT_STRTAB, 0, + ORDER_INVALID, false); Output_data_strtab* strtab_data = - new Output_data_strtab(this->incremental_inputs_->get_stringpool()); - strtab_os->add_output_section_data(strtab_data); - - inputs_os->set_link_section(strtab_data); + new Output_data_strtab(incr->get_stringpool()); + incremental_strtab_os->add_output_section_data(strtab_data); + + incremental_inputs_os->set_after_input_sections(); + incremental_symtab_os->set_after_input_sections(); + incremental_relocs_os->set_after_input_sections(); + + incremental_inputs_os->set_link_section(incremental_strtab_os); + incremental_symtab_os->set_link_section(incremental_inputs_os); + incremental_relocs_os->set_link_section(incremental_inputs_os); } // Return whether SEG1 should be before SEG2 in the output file. This diff --git a/gold/layout.h b/gold/layout.h index 6a3a16c6f50..87ee09ed961 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -1,6 +1,6 @@ // layout.h -- lay out output file sections for gold -*- C++ -*- -// Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -58,6 +58,7 @@ class Output_reduced_debug_abbrev_section; class Output_reduced_debug_info_section; class Eh_frame; class Target; +struct Timespec; // Return TRUE if SECNAME is the name of a compressed debug section. extern bool @@ -662,7 +663,7 @@ class Layout // Return the object managing inputs in incremental build. NULL in // non-incremental builds. Incremental_inputs* - incremental_inputs() + incremental_inputs() const { return this->incremental_inputs_; } // For the target-specific code to add dynamic tags which are common @@ -800,7 +801,7 @@ class Layout // Create .gnu_incremental_inputs and .gnu_incremental_strtab sections needed // for the next run of incremental linking to check what has changed. void - create_incremental_info_sections(); + create_incremental_info_sections(Symbol_table*); // Find the first read-only PT_LOAD segment, creating one if // necessary. diff --git a/gold/main.cc b/gold/main.cc index 631f1ac2d2c..91d51933691 100644 --- a/gold/main.cc +++ b/gold/main.cc @@ -229,10 +229,7 @@ main(int argc, char** argv) &command_line.script_options()); if (layout.incremental_inputs() != NULL) - { - layout.incremental_inputs()->report_command_line(argc, argv); - layout.incremental_inputs()->report_inputs(command_line.inputs()); - } + layout.incremental_inputs()->report_command_line(argc, argv); if (parameters->options().section_ordering_file()) layout.read_layout_from_file(); diff --git a/gold/object.cc b/gold/object.cc index 1bf73677f62..b3d987056c7 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -40,6 +40,7 @@ #include "dynobj.h" #include "plugin.h" #include "compressed_output.h" +#include "incremental.h" namespace gold { @@ -346,6 +347,30 @@ Relobj::is_section_name_included(const char* name) return false; } +// Finalize the incremental relocation information. Allocates a block +// of relocation entries for each symbol, and sets the reloc_bases_ +// array to point to the first entry in each block. Returns the next +// available reloation index. + +void +Relobj::finalize_incremental_relocs(Layout* layout) +{ + unsigned int nsyms = this->get_global_symbols()->size(); + this->reloc_bases_ = new unsigned int[nsyms]; + + gold_assert(this->reloc_bases_ != NULL); + gold_assert(layout->incremental_inputs() != NULL); + + unsigned int rindex = layout->incremental_inputs()->get_reloc_count(); + for (unsigned int i = 0; i < nsyms; ++i) + { + this->reloc_bases_[i] = rindex; + rindex += this->reloc_counts_[i]; + this->reloc_counts_[i] = 0; + } + layout->incremental_inputs()->set_reloc_count(rindex); +} + // Class Sized_relobj. template @@ -1237,6 +1262,13 @@ Sized_relobj::do_layout(Symbol_table* symtab, } } + // Add the section to the incremental inputs layout. + Incremental_inputs* incremental_inputs = layout->incremental_inputs(); + if (incremental_inputs != NULL) + incremental_inputs->report_input_section(this, i, + discard ? NULL : name, + shdr.get_sh_size()); + if (discard) { // Do not include this section in the link. diff --git a/gold/object.h b/gold/object.h index 59b723fab40..99ceabfb448 100644 --- a/gold/object.h +++ b/gold/object.h @@ -387,6 +387,18 @@ class Object section_addralign(unsigned int shndx) { return this->do_section_addralign(shndx); } + // Return the output section given a section index. + Output_section* + output_section(unsigned int shndx) const + { return this->do_output_section(shndx); } + + // Given a section index, return the offset in the Output_section. + // The return value will be -1U if the section is specially mapped, + // such as a merge section. + uint64_t + output_section_offset(unsigned int shndx) const + { return this->do_output_section_offset(shndx); } + // Read the symbol information. void read_symbols(Read_symbols_data* sd) @@ -525,6 +537,16 @@ class Object section_size_type* uncompressed_size) const { return this->do_section_is_compressed(shndx, uncompressed_size); } + // Return the index of the first incremental relocation for symbol SYMNDX. + unsigned int + get_incremental_reloc_base(unsigned int symndx) const + { return this->do_get_incremental_reloc_base(symndx); } + + // Return the number of incremental relocations for symbol SYMNDX. + unsigned int + get_incremental_reloc_count(unsigned int symndx) const + { return this->do_get_incremental_reloc_count(symndx); } + protected: // Returns NULL for Objects that are not plugin objects. This method // is overridden in the Pluginobj class. @@ -590,6 +612,17 @@ class Object virtual uint64_t do_section_addralign(unsigned int shndx) = 0; + // Return the output section given a section index--implemented + // by child class. + virtual Output_section* + do_output_section(unsigned int) const + { gold_unreachable(); } + + // Get the offset of a section--implemented by child class. + virtual uint64_t + do_output_section_offset(unsigned int) const + { gold_unreachable(); } + // Return the Xindex structure to use. virtual Xindex* do_initialize_xindex() = 0; @@ -641,6 +674,18 @@ class Object do_section_is_compressed(unsigned int, section_size_type*) const { return false; } + // Return the index of the first incremental relocation for symbol SYMNDX-- + // implemented by child class. + virtual unsigned int + do_get_incremental_reloc_base(unsigned int) const + { gold_unreachable(); } + + // Return the number of incremental relocations for symbol SYMNDX-- + // implemented by child class. + virtual unsigned int + do_get_incremental_reloc_count(unsigned int) const + { gold_unreachable(); } + private: // This class may not be copied. Object(const Object&); @@ -685,7 +730,9 @@ class Relobj : public Object map_to_relocatable_relocs_(NULL), object_merge_map_(NULL), relocs_must_follow_section_writes_(false), - sd_(NULL) + sd_(NULL), + reloc_counts_(NULL), + reloc_bases_(NULL) { } // During garbage collection, the Read_symbols_data pass for @@ -781,16 +828,6 @@ class Relobj : public Object return this->output_sections_[shndx] != NULL; } - // Given a section index, return the corresponding Output_section. - // The return value will be NULL if the section is not included in - // the link. - Output_section* - output_section(unsigned int shndx) const - { - gold_assert(shndx < this->output_sections_.size()); - return this->output_sections_[shndx]; - } - // The the output section of the input section with index SHNDX. // This is only used currently to remove a section from the link in // relaxation. @@ -801,13 +838,6 @@ class Relobj : public Object this->output_sections_[shndx] = os; } - // Given a section index, return the offset in the Output_section. - // The return value will be -1U if the section is specially mapped, - // such as a merge section. - uint64_t - output_section_offset(unsigned int shndx) const - { return this->do_output_section_offset(shndx); } - // Set the offset of an input section within its output section. void set_section_offset(unsigned int shndx, uint64_t off) @@ -856,6 +886,16 @@ class Relobj : public Object layout_deferred_sections(Layout* layout) { this->do_layout_deferred_sections(layout); } + // Return the index of the first incremental relocation for symbol SYMNDX. + virtual unsigned int + do_get_incremental_reloc_base(unsigned int symndx) const + { return this->reloc_bases_[symndx]; } + + // Return the number of incremental relocations for symbol SYMNDX. + virtual unsigned int + do_get_incremental_reloc_count(unsigned int symndx) const + { return this->reloc_counts_[symndx]; } + protected: // The output section to be used for each input section, indexed by // the input section number. The output section is NULL if the @@ -902,10 +942,6 @@ class Relobj : public Object virtual void do_relocate(const Symbol_table* symtab, const Layout*, Output_file* of) = 0; - // Get the offset of a section--implemented by child class. - virtual uint64_t - do_output_section_offset(unsigned int shndx) const = 0; - // Set the offset of a section--implemented by child class. virtual void do_set_section_offset(unsigned int shndx, uint64_t off) = 0; @@ -915,6 +951,16 @@ class Relobj : public Object virtual void do_layout_deferred_sections(Layout*) = 0; + // Given a section index, return the corresponding Output_section. + // The return value will be NULL if the section is not included in + // the link. + Output_section* + do_output_section(unsigned int shndx) const + { + gold_assert(shndx < this->output_sections_.size()); + return this->output_sections_[shndx]; + } + // Return the vector mapping input sections to output sections. Output_sections& output_sections() @@ -938,6 +984,46 @@ class Relobj : public Object set_relocs_must_follow_section_writes() { this->relocs_must_follow_section_writes_ = true; } + // Allocate the array for counting incremental relocations. + void + allocate_incremental_reloc_counts() + { + unsigned int nsyms = this->do_get_global_symbols()->size(); + this->reloc_counts_ = new unsigned int[nsyms]; + gold_assert(this->reloc_counts_ != NULL); + memset(this->reloc_counts_, 0, nsyms * sizeof(unsigned int)); + } + + // Record a relocation in this object referencing global symbol SYMNDX. + // Used for tracking incremental link information. + void + count_incremental_reloc(unsigned int symndx) + { + unsigned int nsyms = this->do_get_global_symbols()->size(); + gold_assert(symndx < nsyms); + gold_assert(this->reloc_counts_ != NULL); + ++this->reloc_counts_[symndx]; + } + + // Finalize the incremental relocation information. + void + finalize_incremental_relocs(Layout* layout); + + // Return the index of the next relocation to be written for global symbol + // SYMNDX. Only valid after finalize_incremental_relocs() has been called. + unsigned int + next_incremental_reloc_index(unsigned int symndx) + { + unsigned int nsyms = this->do_get_global_symbols()->size(); + + gold_assert(this->reloc_counts_ != NULL); + gold_assert(this->reloc_bases_ != NULL); + gold_assert(symndx < nsyms); + + unsigned int counter = this->reloc_counts_[symndx]++; + return this->reloc_bases_[symndx] + counter; + } + private: // Mapping from input sections to output section. Output_sections output_sections_; @@ -957,6 +1043,10 @@ class Relobj : public Object // Again used during garbage collection when laying out referenced // sections. gold::Symbols_data *sd_; + // Per-symbol counts of relocations, for incremental links. + unsigned int* reloc_counts_; + // Per-symbol base indexes of relocations, for incremental links. + unsigned int* reloc_bases_; }; // This class is used to handle relocations against a section symbol @@ -1792,7 +1882,8 @@ class Sized_relobj : public Relobj // This may be overriden by a child class. virtual void do_relocate_sections(const Symbol_table* symtab, const Layout* layout, - const unsigned char* pshdrs, Views* pviews); + const unsigned char* pshdrs, Output_file* of, + Views* pviews); // Allow a child to set output local symbol count. void @@ -1880,8 +1971,9 @@ class Sized_relobj : public Relobj // Relocate the sections in the output file. void relocate_sections(const Symbol_table* symtab, const Layout* layout, - const unsigned char* pshdrs, Views* pviews) - { this->do_relocate_sections(symtab, layout, pshdrs, pviews); } + const unsigned char* pshdrs, Output_file* of, + Views* pviews) + { this->do_relocate_sections(symtab, layout, pshdrs, of, pviews); } // Scan the input relocations for --emit-relocs. void @@ -1918,6 +2010,35 @@ class Sized_relobj : public Relobj unsigned char* reloc_view, section_size_type reloc_view_size); + // Scan the input relocations for --incremental. + void + incremental_relocs_scan(const Read_relocs_data::Relocs_list::iterator&); + + // Scan the input relocations for --incremental, templatized on the + // type of the relocation section. + template + void + incremental_relocs_scan_reltype( + const Read_relocs_data::Relocs_list::iterator&); + + void + incremental_relocs_write(const Relocate_info*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section*, + Address output_offset, + Output_file*); + + template + void + incremental_relocs_write_reltype(const Relocate_info*, + const unsigned char* prelocs, + size_t reloc_count, + Output_section*, + Address output_offset, + Output_file*); + // A type shared by split_stack_adjust_reltype and find_functions. typedef std::map Function_offsets; diff --git a/gold/plugin.cc b/gold/plugin.cc index 1c86c03b422..468006701d9 100644 --- a/gold/plugin.cc +++ b/gold/plugin.cc @@ -451,7 +451,7 @@ Plugin_manager::add_input_file(const char *pathname, bool is_lib) Input_argument* input_argument = new Input_argument(file); Task_token* next_blocker = new Task_token(true); next_blocker->add_blocker(); - if (this->layout_->incremental_inputs()) + if (parameters->options().incremental()) gold_error(_("input files added by plug-ins in --incremental mode not " "supported yet")); this->workqueue_->queue_soon(new Read_symbols(this->input_objects_, diff --git a/gold/readsyms.cc b/gold/readsyms.cc index d8af99bd39b..1cec3b37463 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -302,12 +302,6 @@ Read_symbols::do_read_symbols(Workqueue* workqueue) this->dirpath_, this); arch->setup(); - if (this->layout_->incremental_inputs()) - { - const Input_argument* ia = this->input_argument_; - this->layout_->incremental_inputs()->report_archive(ia, arch); - } - // Unlock the archive so it can be used in the next task. arch->unlock(this); @@ -389,12 +383,6 @@ Read_symbols::do_read_symbols(Workqueue* workqueue) Read_symbols_data* sd = new Read_symbols_data; obj->read_symbols(sd); - if (this->layout_->incremental_inputs()) - { - const Input_argument* ia = this->input_argument_; - this->layout_->incremental_inputs()->report_object(ia, obj); - } - // Opening the file locked it, so now we need to unlock it. We // need to unlock it before queuing the Add_symbols task, // because the workqueue doesn't know about our lock on the @@ -599,6 +587,10 @@ Add_symbols::run(Workqueue*) } else { + Incremental_inputs* incremental_inputs = + this->layout_->incremental_inputs(); + if (incremental_inputs != NULL) + incremental_inputs->report_object(this->object_, NULL); this->object_->layout(this->symtab_, this->layout_, this->sd_); this->object_->add_symbols(this->symtab_, this->sd_, this->layout_); delete this->sd_; @@ -688,11 +680,20 @@ Finish_group::run(Workqueue*) } } - // Delete all the archives now that we no longer need them. + // Now that we're done with the archives, record the incremental layout + // information, then delete them. for (Input_group::const_iterator p = this->input_group_->begin(); p != this->input_group_->end(); ++p) - delete *p; + { + // For an incremental link, finish recording the layout information. + Incremental_inputs* incremental_inputs = + this->layout_->incremental_inputs(); + if (incremental_inputs != NULL) + incremental_inputs->report_archive_end(*p); + + delete *p; + } delete this->input_group_; } diff --git a/gold/reloc.cc b/gold/reloc.cc index 9f7355e441c..89389888c3b 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -25,6 +25,7 @@ #include #include "workqueue.h" +#include "layout.h" #include "symtab.h" #include "output.h" #include "merge.h" @@ -33,6 +34,7 @@ #include "reloc.h" #include "icf.h" #include "compressed_output.h" +#include "incremental.h" namespace gold { @@ -300,7 +302,8 @@ Sized_relobj::do_read_relocs(Read_relocs_data* rd) != 0); if (!is_section_allocated && !parameters->options().relocatable() - && !parameters->options().emit_relocs()) + && !parameters->options().emit_relocs() + && !parameters->options().incremental()) continue; if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx_) @@ -424,6 +427,10 @@ Sized_relobj::do_scan_relocs(Symbol_table* symtab, else local_symbols = rd->local_symbols->data(); + // For incremental links, allocate the counters for incremental relocations. + if (layout->incremental_inputs() != NULL) + this->allocate_incremental_reloc_counts(); + for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin(); p != rd->relocs.end(); ++p) @@ -451,6 +458,8 @@ Sized_relobj::do_scan_relocs(Symbol_table* symtab, local_symbols); if (parameters->options().emit_relocs()) this->emit_relocs_scan(symtab, layout, local_symbols, p); + if (layout->incremental_inputs() != NULL) + this->incremental_relocs_scan(p); } else { @@ -472,6 +481,10 @@ Sized_relobj::do_scan_relocs(Symbol_table* symtab, p->contents = NULL; } + // For incremental links, finalize the allocation of relocations. + if (layout->incremental_inputs() != NULL) + this->finalize_incremental_relocs(layout); + if (rd->local_symbols != NULL) { delete rd->local_symbols; @@ -567,6 +580,54 @@ Sized_relobj::emit_relocs_scan_reltype( rr); } +// Scan the input relocations for --incremental. + +template +void +Sized_relobj::incremental_relocs_scan( + const Read_relocs_data::Relocs_list::iterator& p) +{ + if (p->sh_type == elfcpp::SHT_REL) + this->incremental_relocs_scan_reltype(p); + else + { + gold_assert(p->sh_type == elfcpp::SHT_RELA); + this->incremental_relocs_scan_reltype(p); + } +} + +// Scan the input relocation for --emit-relocs, templatized on the +// type of the relocation section. + +template +template +void +Sized_relobj::incremental_relocs_scan_reltype( + const Read_relocs_data::Relocs_list::iterator& p) +{ + typedef typename Reloc_types::Reloc Reltype; + const int reloc_size = Reloc_types::reloc_size; + const unsigned char* prelocs = p->contents->data(); + size_t reloc_count = p->reloc_count; + + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) + { + Reltype reloc(prelocs); + + if (p->needs_special_offset_handling + && !p->output_section->is_input_address_mapped(this, p->data_shndx, + reloc.get_r_offset())) + continue; + + typename elfcpp::Elf_types::Elf_WXword r_info = + reloc.get_r_info(); + const unsigned int r_sym = elfcpp::elf_r_sym(r_info); + + if (r_sym >= this->local_symbol_count_) + this->count_incremental_reloc(r_sym - this->local_symbol_count_); + } +} + // Relocate the input sections and write out the local symbols. template @@ -597,7 +658,7 @@ Sized_relobj::do_relocate(const Symbol_table* symtab, // Apply relocations. - this->relocate_sections(symtab, layout, pshdrs, &views); + this->relocate_sections(symtab, layout, pshdrs, of, &views); // After we've done the relocations, we release the hash tables, // since we no longer need them. @@ -827,6 +888,7 @@ Sized_relobj::do_relocate_sections( const Symbol_table* symtab, const Layout* layout, const unsigned char* pshdrs, + Output_file* of, Views* pviews) { unsigned int shnum = this->shnum(); @@ -938,6 +1000,9 @@ Sized_relobj::do_relocate_sections( this->emit_relocs(&relinfo, i, sh_type, prelocs, reloc_count, os, output_offset, view, address, view_size, (*pviews)[i].view, (*pviews)[i].view_size); + if (parameters->options().incremental()) + this->incremental_relocs_write(&relinfo, sh_type, prelocs, + reloc_count, os, output_offset, of); } else { @@ -1020,6 +1085,126 @@ Sized_relobj::emit_relocs_reltype( reloc_view_size); } +// Write the incremental relocs. + +template +void +Sized_relobj::incremental_relocs_write( + const Relocate_info* relinfo, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + Address output_offset, + Output_file* of) +{ + if (sh_type == elfcpp::SHT_REL) + this->incremental_relocs_write_reltype( + relinfo, + prelocs, + reloc_count, + output_section, + output_offset, + of); + else + { + gold_assert(sh_type == elfcpp::SHT_RELA); + this->incremental_relocs_write_reltype( + relinfo, + prelocs, + reloc_count, + output_section, + output_offset, + of); + } +} + +// Write the incremental relocs, templatized on the type of the +// relocation section. + +template +template +void +Sized_relobj::incremental_relocs_write_reltype( + const Relocate_info* relinfo, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + Address output_offset, + Output_file* of) +{ + typedef typename Reloc_types::Reloc Reloc; + const unsigned int reloc_size = + Reloc_types::reloc_size; + const unsigned int sizeof_addr = size / 8; + const unsigned int incr_reloc_size = 8 + 2 * sizeof_addr; + + unsigned int out_shndx = output_section->out_shndx(); + + // Get a view for the .gnu_incremental_relocs section. + + Incremental_inputs* inputs = relinfo->layout->incremental_inputs(); + gold_assert(inputs != NULL); + const off_t relocs_off = inputs->relocs_section()->offset(); + const off_t relocs_size = inputs->relocs_section()->data_size(); + unsigned char* const view = of->get_output_view(relocs_off, relocs_size); + + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) + { + Reloc reloc(prelocs); + + typename elfcpp::Elf_types::Elf_WXword r_info = reloc.get_r_info(); + const unsigned int r_sym = elfcpp::elf_r_sym(r_info); + const unsigned int r_type = elfcpp::elf_r_type(r_info); + + if (r_sym < this->local_symbol_count_) + continue; + + // Get the new offset--the location in the output section where + // this relocation should be applied. + + Address offset = reloc.get_r_offset(); + if (output_offset != invalid_address) + offset += output_offset; + else + { + section_offset_type sot_offset = + convert_types(offset); + section_offset_type new_sot_offset = + output_section->output_offset(relinfo->object, + relinfo->data_shndx, + sot_offset); + gold_assert(new_sot_offset != -1); + offset += new_sot_offset; + } + + // Get the addend. + typename elfcpp::Elf_types::Elf_Swxword addend; + if (sh_type == elfcpp::SHT_RELA) + addend = + Reloc_types::get_reloc_addend(&reloc); + else + { + // FIXME: Get the addend for SHT_REL. + addend = 0; + } + + // Get the index of the output relocation. + + unsigned int reloc_index = + this->next_incremental_reloc_index(r_sym - this->local_symbol_count_); + + // Write the relocation. + + unsigned char* pov = view + reloc_index * incr_reloc_size; + elfcpp::Swap<32, big_endian>::writeval(pov, r_type); + elfcpp::Swap<32, big_endian>::writeval(pov + 4, out_shndx); + elfcpp::Swap::writeval(pov + 8, offset); + elfcpp::Swap::writeval(pov + 8 + sizeof_addr, addend); + of->write_output_view(pov - view, incr_reloc_size, view); + } +} + // Create merge hash tables for the local symbols. These are used to // speed up relocations. @@ -1556,6 +1741,7 @@ Sized_relobj<32, false>::do_relocate_sections( const Symbol_table* symtab, const Layout* layout, const unsigned char* pshdrs, + Output_file* of, Views* pviews); #endif @@ -1566,6 +1752,7 @@ Sized_relobj<32, true>::do_relocate_sections( const Symbol_table* symtab, const Layout* layout, const unsigned char* pshdrs, + Output_file* of, Views* pviews); #endif @@ -1576,6 +1763,7 @@ Sized_relobj<64, false>::do_relocate_sections( const Symbol_table* symtab, const Layout* layout, const unsigned char* pshdrs, + Output_file* of, Views* pviews); #endif @@ -1586,6 +1774,7 @@ Sized_relobj<64, true>::do_relocate_sections( const Symbol_table* symtab, const Layout* layout, const unsigned char* pshdrs, + Output_file* of, Views* pviews); #endif diff --git a/gold/script.cc b/gold/script.cc index e0b9359abdc..d09781a80b1 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -1462,16 +1462,16 @@ read_input_script(Workqueue* workqueue, Symbol_table* symtab, Layout* layout, this_blocker = nb; } - if (layout->incremental_inputs()) + if (layout->incremental_inputs() != NULL) { - // Like new Read_symbols(...) above, we rely on close.inputs() + // Like new Read_symbols(...) above, we rely on closure.inputs() // getting leaked by closure. + const std::string& filename = input_file->filename(); Script_info* info = new Script_info(closure.inputs()); - layout->incremental_inputs()->report_script( - input_argument, - input_file->file().get_mtime(), - info); + Timespec mtime = input_file->file().get_mtime(); + layout->incremental_inputs()->report_script(filename, info, mtime); } + *used_next_blocker = true; return true; diff --git a/gold/symtab.h b/gold/symtab.h index 8178e2c6053..4e5b7b05ab9 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -1468,6 +1468,16 @@ class Symbol_table compute_final_value(const Sized_symbol* sym, Compute_final_value_status* pstatus) const; + // Return the index of the first global symbol. + unsigned int + first_global_index() const + { return this->first_global_index_; } + + // Return the total number of symbols in the symbol table. + unsigned int + output_count() const + { return this->output_count_; } + // Write out the global symbols. void write_globals(const Stringpool*, const Stringpool*, -- 2.30.2