X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gold%2Fincremental.cc;h=f9471f87b9d264d0065b0045d1d006bdbdd7972e;hb=b61121178ec07f9da1242e439fe1a23a314ad30e;hp=2ef7c859c98a361e4f1afba7c24d92da94d8b225;hpb=7c3afe08c728dec4d843a777e6b2f294c27338be;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/incremental.cc b/gold/incremental.cc index 2ef7c859c9..f9471f87b9 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 (C) 2009-2019 Free Software Foundation, Inc. // Written by Mikolaj Zalewski . // This file is part of gold. @@ -22,212 +22,114 @@ #include "gold.h" +#include #include +#include "libiberty.h" #include "elfcpp.h" +#include "options.h" #include "output.h" +#include "symtab.h" #include "incremental.h" #include "archive.h" -#include "output.h" +#include "object.h" #include "target-select.h" - -using elfcpp::Convert; +#include "target.h" +#include "fileread.h" +#include "script.h" namespace gold { -// Version information. Will change frequently during the development, later -// we could think about backward (and forward?) compatibility. -const unsigned int INCREMENTAL_LINK_VERSION = 1; - -namespace internal { - -// 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; -}; - -// 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; -}; - -} - -// Accessors. - -// Reader class for .gnu_incremental_inputs header. See -// internal::Incremental_inputs_header_data for fields descriptions. - -template -class Incremental_inputs_header -{ - public: - Incremental_inputs_header(const unsigned char *p) - : p_(reinterpret_cast(p)) - { } - - static const int data_size = sizeof(internal::Incremental_inputs_header_data); - - elfcpp::Elf_Word - get_version() const - { return Convert<32, big_endian>::convert_host(this->p_->version); } - - elfcpp::Elf_Word - get_input_file_count() const - { return Convert<32, big_endian>::convert_host(this->p_->input_file_count); } - - elfcpp::Elf_Word - get_command_line_offset() const - { return Convert<32, big_endian>::convert_host(this->p_->command_line_offset); } - - elfcpp::Elf_Word - get_reserved() const - { return Convert<32, big_endian>::convert_host(this->p_->reserved); } - - private: - const internal::Incremental_inputs_header_data* p_; -}; +// Version number for the .gnu_incremental_inputs section. +// Version 1 was the initial checkin. +// Version 2 adds some padding to ensure 8-byte alignment where necessary. +const unsigned int INCREMENTAL_LINK_VERSION = 2; -// Writer class for .gnu_incremental_inputs header. See -// internal::Incremental_inputs_header_data for fields descriptions. +// 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 Incremental_inputs_header_write +class Output_section_incremental_inputs : public Output_section_data { public: - Incremental_inputs_header_write(unsigned char *p) - : p_(reinterpret_cast(p)) + Output_section_incremental_inputs(const Incremental_inputs* inputs, + const Symbol_table* symtab) + : Output_section_data(size / 8), inputs_(inputs), symtab_(symtab) { } - static const int data_size = sizeof(internal::Incremental_inputs_header_data); - + protected: + // This is called to update the section size prior to assigning + // the address and file offset. void - put_version(elfcpp::Elf_Word v) - { this->p_->version = Convert<32, big_endian>::convert_host(v); } + update_data_size() + { this->set_final_data_size(); } + // Set the final data size. void - put_input_file_count(elfcpp::Elf_Word v) - { this->p_->input_file_count = Convert<32, big_endian>::convert_host(v); } + set_final_data_size(); + // Write the data to the file. void - put_command_line_offset(elfcpp::Elf_Word v) - { this->p_->command_line_offset = Convert<32, big_endian>::convert_host(v); } + do_write(Output_file*); + // Write to a map file. void - put_reserved(elfcpp::Elf_Word v) - { this->p_->reserved = Convert<32, big_endian>::convert_host(v); } - - private: - internal::Incremental_inputs_header_data* p_; -}; - -// Reader class for an .gnu_incremental_inputs entry. See -// internal::Incremental_inputs_entry_data for fields descriptions. -template -class Incremental_inputs_entry -{ - public: - Incremental_inputs_entry(const unsigned char *p) - : p_(reinterpret_cast(p)) - { } - - static const int data_size = sizeof(internal::Incremental_inputs_entry_data); - - elfcpp::Elf_Word - get_filename_offset(elfcpp::Elf_Word v) - { return Convert<32, big_endian>::convert_host(this->p_->filename_offset); } - - elfcpp::Elf_Word - get_data_offset(elfcpp::Elf_Word v) - { return Convert<32, big_endian>::convert_host(this->p_->data_offset); } - - elfcpp::Elf_Xword - get_timestamp_sec(elfcpp::Elf_Xword v) - { return Convert<64, big_endian>::convert_host(this->p_->timestamp_sec); } - - elfcpp::Elf_Word - get_timestamp_nsec(elfcpp::Elf_Word v) - { return Convert<32, big_endian>::convert_host(this->p_->timestamp_nsec); } - - elfcpp::Elf_Word - get_input_type(elfcpp::Elf_Word v) - { return Convert<32, big_endian>::convert_host(this->p_->input_type); } - - elfcpp::Elf_Word - get_reserved(elfcpp::Elf_Word v) - { return Convert<32, big_endian>::convert_host(this->p_->reserved); } + do_print_to_mapfile(Mapfile* mapfile) const + { mapfile->print_output_data(this, _("** incremental_inputs")); } private: - const internal::Incremental_inputs_entry_data* p_; -}; - -// Writer class for an .gnu_incremental_inputs entry. See -// internal::Incremental_inputs_entry_data for fields descriptions. -template -class Incremental_inputs_entry_write -{ - public: - Incremental_inputs_entry_write(unsigned char *p) - : p_(reinterpret_cast(p)) - { } - - static const int data_size = sizeof(internal::Incremental_inputs_entry_data); - - void - put_filename_offset(elfcpp::Elf_Word v) - { this->p_->filename_offset = Convert<32, big_endian>::convert_host(v); } - - void - put_data_offset(elfcpp::Elf_Word v) - { this->p_->data_offset = Convert<32, big_endian>::convert_host(v); } - - void - put_timestamp_sec(elfcpp::Elf_Xword v) - { this->p_->timestamp_sec = Convert<64, big_endian>::convert_host(v); } - + // 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 - put_timestamp_nsec(elfcpp::Elf_Word v) - { this->p_->timestamp_nsec = Convert<32, big_endian>::convert_host(v); } + write_symtab(unsigned char* pov, unsigned int* global_syms, + unsigned int global_sym_count); + // Write the contents of the .gnu_incremental_got_plt section. void - put_input_type(elfcpp::Elf_Word v) - { this->p_->input_type = Convert<32, big_endian>::convert_host(v); } - - void - put_reserved(elfcpp::Elf_Word v) - { this->p_->reserved = Convert<32, big_endian>::convert_host(v); } - - private: - internal::Incremental_inputs_entry_data* p_; + write_got_plt(unsigned char* pov, off_t view_size); + + // 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 = + Incremental_inputs_reader::header_size; + static const int input_entry_size = + Incremental_inputs_reader::input_entry_size; + static const unsigned int object_info_size = + Incremental_inputs_reader::object_info_size; + static const unsigned int input_section_entry_size = + Incremental_inputs_reader::input_section_entry_size; + static const unsigned int global_sym_entry_size = + Incremental_inputs_reader::global_sym_entry_size; + static const unsigned int incr_reloc_size = + Incremental_relocs_reader::reloc_size; + + // 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 @@ -240,7 +142,7 @@ vexplain_no_incremental(const char* format, va_list args) if (vasprintf(&buf, format, args) < 0) gold_nomem(); gold_info(_("the link might take longer: " - "cannot perform incremental link: %s"), buf); + "cannot perform incremental link: %s"), buf); free(buf); } @@ -269,80 +171,678 @@ Incremental_binary::error(const char* format, ...) const va_end(args); } +// Return TRUE if a section of type SH_TYPE can be updated in place +// during an incremental update. We can update sections of type PROGBITS, +// NOBITS, INIT_ARRAY, FINI_ARRAY, PREINIT_ARRAY, NOTE, and +// (processor-specific) unwind sections. All others will be regenerated. + +bool +can_incremental_update(unsigned int sh_type) +{ + return (sh_type == elfcpp::SHT_PROGBITS + || sh_type == elfcpp::SHT_NOBITS + || sh_type == elfcpp::SHT_INIT_ARRAY + || sh_type == elfcpp::SHT_FINI_ARRAY + || sh_type == elfcpp::SHT_PREINIT_ARRAY + || sh_type == elfcpp::SHT_NOTE + || sh_type == parameters->target().unwind_section_type()); +} + +// 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::find_incremental_inputs_sections( + unsigned int* p_inputs_shndx, + unsigned int* p_symtab_shndx, + unsigned int* p_relocs_shndx, + unsigned int* p_got_plt_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; + + 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; - *strtab_shndx = this->elf_file_.section_link(shndx); - *location = this->elf_file_.section_contents(shndx); + + unsigned int got_plt_shndx = + this->elf_file_.find_section_by_type(elfcpp::SHT_GNU_INCREMENTAL_GOT_PLT); + if (got_plt_shndx == elfcpp::SHN_UNDEF) // Not found. + return false; + if (this->elf_file_.section_link(got_plt_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_got_plt_shndx != NULL) + *p_got_plt_shndx = got_plt_shndx; + if (p_strtab_shndx != NULL) + *p_strtab_shndx = strtab_shndx; return true; } +// Set up the readers into the incremental info sections. + template -bool -Sized_incremental_binary::do_check_inputs( - Incremental_inputs* incremental_inputs) +void +Sized_incremental_binary::setup_readers() { - 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 got_plt_shndx; unsigned int strtab_shndx; - Location location; - if (!do_find_incremental_inputs_section(&location, &strtab_shndx)) + if (!this->find_incremental_inputs_sections(&inputs_shndx, &symtab_shndx, + &relocs_shndx, &got_plt_shndx, + &strtab_shndx)) + return; + + 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 got_plt_location(this->elf_file_.section_contents(got_plt_shndx)); + Location strtab_location(this->elf_file_.section_contents(strtab_shndx)); + + View inputs_view = this->view(inputs_location); + View symtab_view = this->view(symtab_location); + View relocs_view = this->view(relocs_location); + View got_plt_view = this->view(got_plt_location); + View strtab_view = this->view(strtab_location); + + elfcpp::Elf_strtab strtab(strtab_view.data(), strtab_location.data_size); + + this->inputs_reader_ = + Incremental_inputs_reader(inputs_view.data(), strtab); + this->symtab_reader_ = + Incremental_symtab_reader(symtab_view.data(), + symtab_location.data_size); + this->relocs_reader_ = + Incremental_relocs_reader(relocs_view.data(), + relocs_location.data_size); + this->got_plt_reader_ = + Incremental_got_plt_reader(got_plt_view.data()); + + // Find the main symbol table. + unsigned int main_symtab_shndx = + this->elf_file_.find_section_by_type(elfcpp::SHT_SYMTAB); + gold_assert(main_symtab_shndx != elfcpp::SHN_UNDEF); + this->main_symtab_loc_ = this->elf_file_.section_contents(main_symtab_shndx); + + // Find the main symbol string table. + unsigned int main_strtab_shndx = + this->elf_file_.section_link(main_symtab_shndx); + gold_assert(main_strtab_shndx != elfcpp::SHN_UNDEF + && main_strtab_shndx < this->elf_file_.shnum()); + this->main_strtab_loc_ = this->elf_file_.section_contents(main_strtab_shndx); + + // Walk the list of input files (a) to setup an Input_reader for each + // input file, and (b) to record maps of files added from archive + // libraries and scripts. + Incremental_inputs_reader& inputs = this->inputs_reader_; + unsigned int count = inputs.input_file_count(); + this->input_objects_.resize(count); + this->input_entry_readers_.reserve(count); + this->library_map_.resize(count); + this->script_map_.resize(count); + for (unsigned int i = 0; i < count; i++) { - explain_no_incremental(_("no incremental data from previous build")); - return false; + Input_entry_reader input_file = inputs.input_file(i); +#if __cplusplus >= 2001103L + this->input_entry_readers_.emplace_back(input_file); +#else + this->input_entry_readers_.push_back(Sized_input_reader(input_file)); +#endif + switch (input_file.type()) + { + case INCREMENTAL_INPUT_OBJECT: + case INCREMENTAL_INPUT_ARCHIVE_MEMBER: + case INCREMENTAL_INPUT_SHARED_LIBRARY: + // No special treatment necessary. + break; + case INCREMENTAL_INPUT_ARCHIVE: + { + Incremental_library* lib = + new Incremental_library(input_file.filename(), i, + &this->input_entry_readers_[i]); + this->library_map_[i] = lib; + unsigned int member_count = input_file.get_member_count(); + for (unsigned int j = 0; j < member_count; j++) + { + int member_offset = input_file.get_member_offset(j); + int member_index = inputs.input_file_index(member_offset); + this->library_map_[member_index] = lib; + } + } + break; + case INCREMENTAL_INPUT_SCRIPT: + { + Script_info* script = new Script_info(input_file.filename(), i); + this->script_map_[i] = script; + unsigned int object_count = input_file.get_object_count(); + for (unsigned int j = 0; j < object_count; j++) + { + int object_offset = input_file.get_object_offset(j); + int object_index = inputs.input_file_index(object_offset); + this->script_map_[object_index] = script; + } + } + break; + default: + gold_unreachable(); + } } - if (location.data_size < header_size - || strtab_shndx >= this->elf_file_.shnum() - || this->elf_file_.section_type(strtab_shndx) != elfcpp::SHT_STRTAB) + + // Initialize the map of global symbols. + unsigned int nglobals = this->symtab_reader_.symbol_count(); + this->symbol_map_.resize(nglobals); + + this->has_incremental_info_ = true; +} + +// Walk the list of input files given on the command line, and build +// a direct map of file index to the corresponding input argument. + +void +check_input_args(std::vector& input_args_map, + Input_arguments::const_iterator begin, + Input_arguments::const_iterator end) +{ + for (Input_arguments::const_iterator p = begin; + p != end; + ++p) { - explain_no_incremental(_("invalid incremental build data")); - return false; + if (p->is_group()) + { + const Input_file_group* group = p->group(); + check_input_args(input_args_map, group->begin(), group->end()); + } + else if (p->is_lib()) + { + const Input_file_lib* lib = p->lib(); + check_input_args(input_args_map, lib->begin(), lib->end()); + } + else + { + gold_assert(p->is_file()); + unsigned int arg_serial = p->file().arg_serial(); + if (arg_serial > 0) + { + gold_assert(arg_serial <= input_args_map.size()); + gold_assert(input_args_map[arg_serial - 1] == 0); + input_args_map[arg_serial - 1] = &*p; + } + } } +} - Location strtab_location(this->elf_file_.section_contents(strtab_shndx)); - View data_view(view(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()); +// Determine whether an incremental link based on the existing output file +// can be done. + +template +bool +Sized_incremental_binary::do_check_inputs( + const Command_line& cmdline, + Incremental_inputs* incremental_inputs) +{ + Incremental_inputs_reader& inputs = this->inputs_reader_; - if (header.get_version() != INCREMENTAL_LINK_VERSION) + if (!this->has_incremental_info_) { - explain_no_incremental(_("different version of incremental build data")); + explain_no_incremental(_("no incremental data from previous build")); return false; } - 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 (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() != inputs.command_line()) { + gold_debug(DEBUG_INCREMENTAL, + "old command line: %s", + inputs.command_line()); + gold_debug(DEBUG_INCREMENTAL, + "new command line: %s", + incremental_inputs->command_line().c_str()); explain_no_incremental(_("command line changed")); return false; } - // TODO: compare incremental_inputs->inputs() with entries in data_view. + // Walk the list of input files given on the command line, and build + // a direct map of argument serial numbers to the corresponding input + // arguments. + this->input_args_map_.resize(cmdline.number_of_input_files()); + check_input_args(this->input_args_map_, cmdline.begin(), cmdline.end()); + + // Walk the list of input files to check for conditions that prevent + // an incremental update link. + unsigned int count = inputs.input_file_count(); + for (unsigned int i = 0; i < count; i++) + { + Input_entry_reader input_file = inputs.input_file(i); + switch (input_file.type()) + { + case INCREMENTAL_INPUT_OBJECT: + case INCREMENTAL_INPUT_ARCHIVE_MEMBER: + case INCREMENTAL_INPUT_SHARED_LIBRARY: + case INCREMENTAL_INPUT_ARCHIVE: + // No special treatment necessary. + break; + case INCREMENTAL_INPUT_SCRIPT: + if (this->do_file_has_changed(i)) + { + explain_no_incremental(_("%s: script file changed"), + input_file.filename()); + return false; + } + break; + default: + gold_unreachable(); + } + } + return true; } +// Return TRUE if input file N has changed since the last incremental link. + +template +bool +Sized_incremental_binary::do_file_has_changed( + unsigned int n) const +{ + Input_entry_reader input_file = this->inputs_reader_.input_file(n); + Incremental_disposition disp = INCREMENTAL_CHECK; + + // For files named in scripts, find the file that was actually named + // on the command line, so that we can get the incremental disposition + // flag. + Script_info* script = this->get_script_info(n); + if (script != NULL) + n = script->input_file_index(); + + const Input_argument* input_argument = this->get_input_argument(n); + if (input_argument != NULL) + disp = input_argument->file().options().incremental_disposition(); + + // For files at the beginning of the command line (i.e., those added + // implicitly by gcc), check whether the --incremental-startup-unchanged + // option was used. + if (disp == INCREMENTAL_STARTUP) + disp = parameters->options().incremental_startup_disposition(); + + if (disp != INCREMENTAL_CHECK) + return disp == INCREMENTAL_CHANGED; + + const char* filename = input_file.filename(); + Timespec old_mtime = input_file.get_mtime(); + Timespec new_mtime; + if (!get_mtime(filename, &new_mtime)) + { + // If we can't open get the current modification time, assume it has + // changed. If the file doesn't exist, we'll issue an error when we + // try to open it later. + return true; + } + + if (new_mtime.seconds > old_mtime.seconds) + return true; + if (new_mtime.seconds == old_mtime.seconds + && new_mtime.nanoseconds > old_mtime.nanoseconds) + return true; + return false; +} + +// Initialize the layout of the output file based on the existing +// output file. + +template +void +Sized_incremental_binary::do_init_layout(Layout* layout) +{ + typedef elfcpp::Shdr Shdr; + const int shdr_size = elfcpp::Elf_sizes::shdr_size; + + // Get views of the section headers and the section string table. + const off_t shoff = this->elf_file_.shoff(); + const unsigned int shnum = this->elf_file_.shnum(); + const unsigned int shstrndx = this->elf_file_.shstrndx(); + Location shdrs_location(shoff, shnum * shdr_size); + Location shstrndx_location(this->elf_file_.section_contents(shstrndx)); + View shdrs_view = this->view(shdrs_location); + View shstrndx_view = this->view(shstrndx_location); + elfcpp::Elf_strtab shstrtab(shstrndx_view.data(), + shstrndx_location.data_size); + + layout->set_incremental_base(this); + + // Initialize the layout. + this->section_map_.resize(shnum); + const unsigned char* pshdr = shdrs_view.data() + shdr_size; + for (unsigned int i = 1; i < shnum; i++) + { + Shdr shdr(pshdr); + const char* name; + if (!shstrtab.get_c_string(shdr.get_sh_name(), &name)) + name = NULL; + gold_debug(DEBUG_INCREMENTAL, + "Output section: %2d %08lx %08lx %08lx %3d %s", + i, + static_cast(shdr.get_sh_addr()), + static_cast(shdr.get_sh_offset()), + static_cast(shdr.get_sh_size()), + shdr.get_sh_type(), name ? name : ""); + this->section_map_[i] = layout->init_fixed_output_section(name, shdr); + pshdr += shdr_size; + } +} + +// Mark regions of the input file that must be kept unchanged. + +template +void +Sized_incremental_binary::do_reserve_layout( + unsigned int input_file_index) +{ + const int sym_size = elfcpp::Elf_sizes::sym_size; + + Input_entry_reader input_file = + this->inputs_reader_.input_file(input_file_index); + + if (input_file.type() == INCREMENTAL_INPUT_SHARED_LIBRARY) + { + // Reserve the BSS space used for COPY relocations. + unsigned int nsyms = input_file.get_global_symbol_count(); + Incremental_binary::View symtab_view(NULL); + unsigned int symtab_count; + elfcpp::Elf_strtab strtab(NULL, 0); + this->get_symtab_view(&symtab_view, &symtab_count, &strtab); + for (unsigned int i = 0; i < nsyms; ++i) + { + bool is_def; + bool is_copy; + unsigned int output_symndx = + input_file.get_output_symbol_index(i, &is_def, &is_copy); + if (is_copy) + { + const unsigned char* sym_p = (symtab_view.data() + + output_symndx * sym_size); + elfcpp::Sym gsym(sym_p); + unsigned int shndx = gsym.get_st_shndx(); + if (shndx < 1 || shndx >= this->section_map_.size()) + continue; + Output_section* os = this->section_map_[shndx]; + off_t offset = gsym.get_st_value() - os->address(); + os->reserve(offset, gsym.get_st_size()); + gold_debug(DEBUG_INCREMENTAL, + "Reserve for COPY reloc: %s, off %d, size %d", + os->name(), + static_cast(offset), + static_cast(gsym.get_st_size())); + } + } + return; + } + + unsigned int shnum = input_file.get_input_section_count(); + for (unsigned int i = 0; i < shnum; i++) + { + typename Input_entry_reader::Input_section_info sect = + input_file.get_input_section(i); + if (sect.output_shndx == 0 || sect.sh_offset == -1) + continue; + Output_section* os = this->section_map_[sect.output_shndx]; + gold_assert(os != NULL); + os->reserve(sect.sh_offset, sect.sh_size); + } +} + +// Process the GOT and PLT entries from the existing output file. + +template +void +Sized_incremental_binary::do_process_got_plt( + Symbol_table* symtab, + Layout* layout) +{ + Incremental_got_plt_reader got_plt_reader(this->got_plt_reader()); + Sized_target* target = + parameters->sized_target(); + + // Get the number of symbols in the main symbol table and in the + // incremental symbol table. The difference between the two counts + // is the index of the first forced-local or global symbol in the + // main symbol table. + unsigned int symtab_count = + this->main_symtab_loc_.data_size / elfcpp::Elf_sizes::sym_size; + unsigned int isym_count = this->symtab_reader_.symbol_count(); + unsigned int first_global = symtab_count - isym_count; + + // Tell the target how big the GOT and PLT sections are. + unsigned int got_count = got_plt_reader.get_got_entry_count(); + unsigned int plt_count = got_plt_reader.get_plt_entry_count(); + Output_data_got_base* got = + target->init_got_plt_for_update(symtab, layout, got_count, plt_count); + + // Read the GOT entries from the base file and build the outgoing GOT. + for (unsigned int i = 0; i < got_count; ++i) + { + unsigned int got_type = got_plt_reader.get_got_type(i); + if ((got_type & 0x7f) == 0x7f) + { + // This is the second entry of a pair. + got->reserve_slot(i); + continue; + } + unsigned int symndx = got_plt_reader.get_got_symndx(i); + if (got_type & 0x80) + { + // This is an entry for a local symbol. Ignore this entry if + // the object file was replaced. + unsigned int input_index = got_plt_reader.get_got_input_index(i); + gold_debug(DEBUG_INCREMENTAL, + "GOT entry %d, type %02x: (local symbol)", + i, got_type & 0x7f); + Sized_relobj_incr* obj = + this->input_object(input_index); + if (obj != NULL) + target->reserve_local_got_entry(i, obj, symndx, got_type & 0x7f); + } + else + { + // This is an entry for a global symbol. GOT_DESC is the symbol + // table index. + // FIXME: This should really be a fatal error (corrupt input). + gold_assert(symndx >= first_global && symndx < symtab_count); + Symbol* sym = this->global_symbol(symndx - first_global); + // Add the GOT entry only if the symbol is still referenced. + if (sym != NULL && sym->in_reg()) + { + gold_debug(DEBUG_INCREMENTAL, + "GOT entry %d, type %02x: %s", + i, got_type, sym->name()); + target->reserve_global_got_entry(i, sym, got_type); + } + } + } + + // Read the PLT entries from the base file and pass each to the target. + for (unsigned int i = 0; i < plt_count; ++i) + { + unsigned int plt_desc = got_plt_reader.get_plt_desc(i); + // FIXME: This should really be a fatal error (corrupt input). + gold_assert(plt_desc >= first_global && plt_desc < symtab_count); + Symbol* sym = this->global_symbol(plt_desc - first_global); + // Add the PLT entry only if the symbol is still referenced. + if (sym != NULL && sym->in_reg()) + { + gold_debug(DEBUG_INCREMENTAL, + "PLT entry %d: %s", + i, sym->name()); + target->register_global_plt_entry(symtab, layout, i, sym); + } + } +} + +// Emit COPY relocations from the existing output file. + +template +void +Sized_incremental_binary::do_emit_copy_relocs( + Symbol_table* symtab) +{ + Sized_target* target = + parameters->sized_target(); + + for (typename Copy_relocs::iterator p = this->copy_relocs_.begin(); + p != this->copy_relocs_.end(); + ++p) + { + if (!(*p).symbol->is_copied_from_dynobj()) + target->emit_copy_reloc(symtab, (*p).symbol, (*p).output_section, + (*p).offset); + } +} + +// Apply incremental relocations for symbols whose values have changed. + +template +void +Sized_incremental_binary::do_apply_incremental_relocs( + const Symbol_table* symtab, + Layout* layout, + Output_file* of) +{ + typedef typename elfcpp::Elf_types::Elf_Addr Address; + typedef typename elfcpp::Elf_types::Elf_Swxword Addend; + Incremental_symtab_reader isymtab(this->symtab_reader()); + Incremental_relocs_reader irelocs(this->relocs_reader()); + unsigned int nglobals = isymtab.symbol_count(); + const unsigned int incr_reloc_size = irelocs.reloc_size; + + Relocate_info relinfo; + relinfo.symtab = symtab; + relinfo.layout = layout; + relinfo.object = NULL; + relinfo.reloc_shndx = 0; + relinfo.reloc_shdr = NULL; + relinfo.data_shndx = 0; + relinfo.data_shdr = NULL; + + Sized_target* target = + parameters->sized_target(); + + for (unsigned int i = 0; i < nglobals; i++) + { + const Symbol* gsym = this->global_symbol(i); + + // If the symbol is not referenced from any unchanged input files, + // we do not need to reapply any of its relocations. + if (gsym == NULL) + continue; + + // If the symbol is defined in an unchanged file, we do not need to + // reapply any of its relocations. + if (gsym->source() == Symbol::FROM_OBJECT + && gsym->object()->is_incremental()) + continue; + + gold_debug(DEBUG_INCREMENTAL, + "Applying incremental relocations for global symbol %s [%d]", + gsym->name(), i); + + // Follow the linked list of input symbol table entries for this symbol. + // We don't bother to figure out whether the symbol table entry belongs + // to a changed or unchanged file because it's easier just to apply all + // the relocations -- although we might scribble over an area that has + // been reallocated, we do this before copying any new data into the + // output file. + unsigned int offset = isymtab.get_list_head(i); + while (offset > 0) + { + Incremental_global_symbol_reader sym_info = + this->inputs_reader().global_symbol_reader_at_offset(offset); + unsigned int r_base = sym_info.reloc_offset(); + unsigned int r_count = sym_info.reloc_count(); + + // Apply each relocation for this symbol table entry. + for (unsigned int j = 0; j < r_count; + ++j, r_base += incr_reloc_size) + { + unsigned int r_type = irelocs.get_r_type(r_base); + unsigned int r_shndx = irelocs.get_r_shndx(r_base); + Address r_offset = irelocs.get_r_offset(r_base); + Addend r_addend = irelocs.get_r_addend(r_base); + Output_section* os = this->output_section(r_shndx); + Address address = os->address(); + off_t section_offset = os->offset(); + size_t view_size = os->data_size(); + unsigned char* const view = of->get_output_view(section_offset, + view_size); + + gold_debug(DEBUG_INCREMENTAL, + " %08lx: %s + %d: type %d addend %ld", + (long)(section_offset + r_offset), + os->name(), + (int)r_offset, + r_type, + (long)r_addend); + + target->apply_relocation(&relinfo, r_offset, r_type, r_addend, + gsym, view, address, view_size); + + // FIXME: Do something more efficient if write_output_view + // ever becomes more than a no-op. + of->write_output_view(section_offset, view_size, view); + } + offset = sym_info.next_offset(); + } + } +} + +// Get a view of the main symbol table and the symbol string table. + +template +void +Sized_incremental_binary::get_symtab_view( + View* symtab_view, + unsigned int* nsyms, + elfcpp::Elf_strtab* strtab) +{ + *symtab_view = this->view(this->main_symtab_loc_); + *nsyms = this->main_symtab_loc_.data_size / elfcpp::Elf_sizes::sym_size; + + View strtab_view(this->view(this->main_strtab_loc_)); + *strtab = elfcpp::Elf_strtab(strtab_view.data(), + this->main_strtab_loc_.data_size); +} + namespace { @@ -352,25 +852,31 @@ namespace template Incremental_binary* make_sized_incremental_binary(Output_file* file, - const elfcpp::Ehdr& ehdr) + const elfcpp::Ehdr& ehdr) { - Target* target = select_target(ehdr.get_e_machine(), size, big_endian, - ehdr.get_e_ident()[elfcpp::EI_OSABI], - ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]); + Target* target = select_target(NULL, 0, // XXX + ehdr.get_e_machine(), size, big_endian, + ehdr.get_e_ident()[elfcpp::EI_OSABI], + ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]); if (target == NULL) { explain_no_incremental(_("unsupported ELF machine number %d"), - ehdr.get_e_machine()); + ehdr.get_e_machine()); return NULL; } + if (!parameters->target_valid()) + set_parameters_target(target); + else if (target != ¶meters->target()) + gold_error(_("%s: incompatible target"), file->filename()); + return new Sized_incremental_binary(file, ehdr, target); } } // 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* @@ -392,7 +898,7 @@ open_incremental_binary(Output_file* file) bool big_endian = false; std::string error; if (!elfcpp::Elf_recognizer::is_valid_header(p, want, &size, &big_endian, - &error)) + &error)) { explain_no_incremental(error.c_str()); return NULL; @@ -402,44 +908,44 @@ open_incremental_binary(Output_file* file) if (size == 32) { if (big_endian) - { + { #ifdef HAVE_TARGET_32_BIG - result = make_sized_incremental_binary<32, true>( - file, elfcpp::Ehdr<32, true>(p)); + result = make_sized_incremental_binary<32, true>( + file, elfcpp::Ehdr<32, true>(p)); #else - explain_no_incremental(_("unsupported file: 32-bit, big-endian")); + explain_no_incremental(_("unsupported file: 32-bit, big-endian")); #endif - } + } else - { + { #ifdef HAVE_TARGET_32_LITTLE - result = make_sized_incremental_binary<32, false>( - file, elfcpp::Ehdr<32, false>(p)); + result = make_sized_incremental_binary<32, false>( + file, elfcpp::Ehdr<32, false>(p)); #else - explain_no_incremental(_("unsupported file: 32-bit, little-endian")); + explain_no_incremental(_("unsupported file: 32-bit, little-endian")); #endif - } + } } else if (size == 64) { if (big_endian) - { + { #ifdef HAVE_TARGET_64_BIG - result = make_sized_incremental_binary<64, true>( - file, elfcpp::Ehdr<64, true>(p)); + result = make_sized_incremental_binary<64, true>( + file, elfcpp::Ehdr<64, true>(p)); #else - explain_no_incremental(_("unsupported file: 64-bit, big-endian")); + explain_no_incremental(_("unsupported file: 64-bit, big-endian")); #endif - } + } else - { + { #ifdef HAVE_TARGET_64_LITTLE - result = make_sized_incremental_binary<64, false>( - file, elfcpp::Ehdr<64, false>(p)); + result = make_sized_incremental_binary<64, false>( + file, elfcpp::Ehdr<64, false>(p)); #else - explain_no_incremental(_("unsupported file: 64-bit, little-endian")); + explain_no_incremental(_("unsupported file: 64-bit, little-endian")); #endif - } + } } else gold_unreachable(); @@ -447,20 +953,7 @@ open_incremental_binary(Output_file* file) return result; } -// Analyzes the output file to check if incremental linking is possible and -// (to be done) what files need to be relinked. - -bool -Incremental_checker::can_incrementally_link_output_file() -{ - Output_file output(this->output_name_); - if (!output.open_for_modification()) - return false; - Incremental_binary* binary = open_incremental_binary(&output); - if (binary == NULL) - return false; - 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 @@ -476,223 +969,2114 @@ 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. - if (strcmp(argv[i], "--incremental-changed") == 0 + // Adding/removing these options should not result in a full relink. + if (strcmp(argv[i], "--incremental") == 0 + || strcmp(argv[i], "--incremental-full") == 0 + || strcmp(argv[i], "--incremental-update") == 0 + || strcmp(argv[i], "--incremental-changed") == 0 || strcmp(argv[i], "--incremental-unchanged") == 0 - || strcmp(argv[i], "--incremental-unknown") == 0) - continue; + || strcmp(argv[i], "--incremental-unknown") == 0 + || strcmp(argv[i], "--incremental-startup-unchanged") == 0 + || is_prefix_of("--incremental-base=", argv[i]) + || is_prefix_of("--incremental-patch=", argv[i]) + || is_prefix_of("--debug=", argv[i])) + continue; + if (strcmp(argv[i], "--incremental-base") == 0 + || strcmp(argv[i], "--incremental-patch") == 0 + || strcmp(argv[i], "--debug") == 0) + { + // When these options are used without the '=', skip the + // following parameter as well. + ++i; + continue; + } args.append(" '"); // Now append argv[i], but with all single-quotes escaped const char* argpos = argv[i]; while (1) - { - const int len = strcspn(argpos, "'"); - args.append(argpos, len); - if (argpos[len] == '\0') - break; - args.append("'\"'\"'"); - argpos += len + 1; - } + { + const int len = strcspn(argpos, "'"); + args.append(argpos, len); + if (argpos[len] == '\0') + break; + args.append("'\"'\"'"); + argpos += len + 1; + } args.append("'"); } this->command_line_ = args; this->strtab_->add(this->command_line_.c_str(), false, - &this->command_line_key_); + &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(Library_base* arch, + unsigned int arg_serial, + Script_info* script_info) { - Hold_lock hl(*this->lock_); + Stringpool::Key filename_key; + Timespec mtime = arch->get_mtime(); + + // For a file loaded from a script, don't record its argument serial number. + if (script_info != NULL) + arg_serial = 0; - 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, arg_serial, mtime); + arch->set_incremental_info(entry); + + if (script_info != NULL) + { + Incremental_script_entry* script_entry = script_info->incremental_info(); + gold_assert(script_entry != NULL); + script_entry->add_object(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. +// Visitor class for processing the unused global symbols in a library. +// An instance of this class is passed to the library's +// for_all_unused_symbols() iterator, which will call the visit() +// function for each global symbol defined in each unused library +// member. We add those symbol names to the incremental info for the +// library. -void -Incremental_inputs::report_object(const Input_argument* input, - Object* obj) +class Unused_symbol_visitor : public Library_base::Symbol_visitor_base { - Hold_lock hl(*this->lock_); + public: + Unused_symbol_visitor(Incremental_archive_entry* entry, Stringpool* strtab) + : entry_(entry), strtab_(strtab) + { } - 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)); -} + void + visit(const char* sym) + { + Stringpool::Key symbol_key; + this->strtab_->add(sym, true, &symbol_key); + this->entry_->add_unused_global_symbol(symbol_key); + } -// 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. + private: + Incremental_archive_entry* entry_; + Stringpool* strtab_; +}; + +// 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_script(const Input_argument* input, - Timespec mtime, - Script_info* script) +Incremental_inputs::report_archive_end(Library_base* arch) { - Hold_lock hl(*this->lock_); + Incremental_archive_entry* entry = arch->incremental_info(); + + gold_assert(entry != NULL); + this->inputs_.push_back(entry); - Input_info info; - info.type = INCREMENTAL_INPUT_SCRIPT; - info.script = script; - info.mtime = mtime; - this->inputs_map_.insert(std::make_pair(input, info)); + // Collect unused global symbols. + Unused_symbol_visitor v(entry, this->strtab_); + arch->for_all_unused_symbols(&v); } -// 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_object(Object* obj, unsigned int arg_serial, + Library_base* arch, Script_info* script_info) { - 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; - } - - 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); - } - } -} + Stringpool::Key filename_key; + Timespec mtime = obj->get_mtime(); -// Finalize the incremental link information. Called from -// Layout::finalize. + // For a file loaded from a script, don't record its argument serial number. + if (script_info != NULL) + arg_serial = 0; -void -Incremental_inputs::finalize() -{ - unsigned int index = 0; - finalize_inputs(this->inputs_->begin(), this->inputs_->end(), &index); + this->strtab_->add(obj->name().c_str(), false, &filename_key); - // Sanity check. - for (Inputs_info_map::const_iterator p = this->inputs_map_.begin(); - p != this->inputs_map_.end(); - ++p) + Incremental_input_entry* input_entry; + + this->current_object_ = obj; + + if (!obj->is_dynamic()) { - gold_assert(p->second.filename_key != 0); + this->current_object_entry_ = + new Incremental_object_entry(filename_key, obj, arg_serial, mtime); + input_entry = this->current_object_entry_; + if (arch != NULL) + { + Incremental_archive_entry* arch_entry = arch->incremental_info(); + gold_assert(arch_entry != NULL); + arch_entry->add_object(this->current_object_entry_); + } + } + else + { + this->current_object_entry_ = NULL; + Stringpool::Key soname_key; + Dynobj* dynobj = obj->dynobj(); + gold_assert(dynobj != NULL); + this->strtab_->add(dynobj->soname(), false, &soname_key); + input_entry = new Incremental_dynobj_entry(filename_key, soname_key, obj, + arg_serial, mtime); } - this->strtab_->set_string_offsets(); -} + if (obj->is_in_system_directory()) + input_entry->set_is_in_system_directory(); -// Create the content of the .gnu_incremental_inputs section. + if (obj->as_needed()) + input_entry->set_as_needed(); -Output_section_data* -Incremental_inputs::create_incremental_inputs_section_data() -{ - switch (parameters->size_and_endianness()) + this->inputs_.push_back(input_entry); + + if (script_info != NULL) { -#ifdef HAVE_TARGET_32_LITTLE - case Parameters::TARGET_32_LITTLE: - return this->sized_create_inputs_section_data<32, false>(); -#endif -#ifdef HAVE_TARGET_32_BIG - case Parameters::TARGET_32_BIG: - return this->sized_create_inputs_section_data<32, true>(); -#endif -#ifdef HAVE_TARGET_64_LITTLE - case Parameters::TARGET_64_LITTLE: - return this->sized_create_inputs_section_data<64, false>(); -#endif -#ifdef HAVE_TARGET_64_BIG - case Parameters::TARGET_64_BIG: - return this->sized_create_inputs_section_data<64, true>(); -#endif - default: - gold_unreachable(); + Incremental_script_entry* script_entry = script_info->incremental_info(); + gold_assert(script_entry != NULL); + script_entry->add_object(input_entry); } } -// Sized creation of .gnu_incremental_inputs section. +// Record an input section SHNDX from object file OBJ. -template -Output_section_data* -Incremental_inputs::sized_create_inputs_section_data() +void +Incremental_inputs::report_input_section(Object* obj, unsigned int shndx, + const char* name, off_t sh_size) { - const int entry_size = - Incremental_inputs_entry_write::data_size; - const int header_size = - Incremental_inputs_header_write::data_size; + Stringpool::Key key = 0; - 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; + if (name != NULL) + this->strtab_->add(name, true, &key); - 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_); + gold_assert(obj == this->current_object_); + gold_assert(this->current_object_entry_ != NULL); + this->current_object_entry_->add_input_section(shndx, key, sh_size); +} + +// Record a kept COMDAT group belonging to object file OBJ. + +void +Incremental_inputs::report_comdat_group(Object* obj, const char* name) +{ + Stringpool::Key key = 0; + + if (name != NULL) + this->strtab_->add(name, true, &key); + gold_assert(obj == this->current_object_); + gold_assert(this->current_object_entry_ != NULL); + this->current_object_entry_->add_comdat_group(key); +} + +// 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(Script_info* script, + unsigned int arg_serial, + Timespec mtime) +{ + Stringpool::Key filename_key; + + this->strtab_->add(script->filename().c_str(), false, &filename_key); + Incremental_script_entry* entry = + new Incremental_script_entry(filename_key, arg_serial, script, mtime); + this->inputs_.push_back(entry); + script->set_incremental_info(entry); +} + +// Finalize the incremental link information. Called from +// Layout::finalize. + +void +Incremental_inputs::finalize() +{ + // Finalize the string table. + this->strtab_->set_string_offsets(); +} + +// Create the .gnu_incremental_inputs, _symtab, and _relocs input sections. + +void +Incremental_inputs::create_data_sections(Symbol_table* symtab) +{ + int reloc_align = 4; + + switch (parameters->size_and_endianness()) + { +#ifdef HAVE_TARGET_32_LITTLE + case Parameters::TARGET_32_LITTLE: + this->inputs_section_ = + new Output_section_incremental_inputs<32, false>(this, symtab); + reloc_align = 4; + break; +#endif +#ifdef HAVE_TARGET_32_BIG + case Parameters::TARGET_32_BIG: + this->inputs_section_ = + new Output_section_incremental_inputs<32, true>(this, symtab); + reloc_align = 4; + break; +#endif +#ifdef HAVE_TARGET_64_LITTLE + case Parameters::TARGET_64_LITTLE: + this->inputs_section_ = + new Output_section_incremental_inputs<64, false>(this, symtab); + reloc_align = 8; + break; +#endif +#ifdef HAVE_TARGET_64_BIG + case Parameters::TARGET_64_BIG: + this->inputs_section_ = + new Output_section_incremental_inputs<64, true>(this, symtab); + reloc_align = 8; + break; +#endif + default: + gold_unreachable(); + } + this->symtab_section_ = new Output_data_space(4, "** incremental_symtab"); + this->relocs_section_ = new Output_data_space(reloc_align, + "** incremental_relocs"); + this->got_plt_section_ = new Output_data_space(4, "** incremental_got_plt"); +} + +// 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 +void +Output_section_incremental_inputs::set_final_data_size() +{ + const Incremental_inputs* inputs = this->inputs_; + + // Offset of each input entry. + unsigned int input_offset = this->header_size; + + // Offset of each supplemental info block. + unsigned int file_index = 0; + unsigned int info_offset = this->header_size; + info_offset += this->input_entry_size * inputs->input_file_count(); + + // 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) + { + // Set the index and offset of the input file entry. + (*p)->set_offset(file_index, input_offset); + ++file_index; + input_offset += this->input_entry_size; + + // Set the offset of the supplemental info block. + switch ((*p)->type()) + { + case INCREMENTAL_INPUT_SCRIPT: + { + Incremental_script_entry *entry = (*p)->script_entry(); + gold_assert(entry != NULL); + (*p)->set_info_offset(info_offset); + // Object count. + info_offset += 4; + // Each member. + info_offset += (entry->get_object_count() * 4); + } + 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, local symbol offset, + // local symbol count, first dynamic reloc, dynamic reloc count, + // comdat group count. + info_offset += this->object_info_size; + // Each input section. + info_offset += (entry->get_input_section_count() + * this->input_section_entry_size); + // Each global symbol. + const Object::Symbols* syms = entry->object()->get_global_symbols(); + info_offset += syms->size() * this->global_sym_entry_size; + // Each comdat group. + info_offset += entry->get_comdat_group_count() * 4; + } + break; + case INCREMENTAL_INPUT_SHARED_LIBRARY: + { + Incremental_dynobj_entry* entry = (*p)->dynobj_entry(); + gold_assert(entry != NULL); + (*p)->set_info_offset(info_offset); + // Global symbol count, soname index. + info_offset += 8; + // Each global symbol. + const Object::Symbols* syms = entry->object()->get_global_symbols(); + gold_assert(syms != NULL); + unsigned int nsyms = syms->size(); + unsigned int nsyms_out = 0; + for (unsigned int i = 0; i < nsyms; ++i) + { + const Symbol* sym = (*syms)[i]; + if (sym == NULL) + continue; + if (sym->is_forwarder()) + sym = this->symtab_->resolve_forwards(sym); + if (sym->symtab_index() != -1U) + ++nsyms_out; + } + info_offset += nsyms_out * 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(); + } + + // Pad so each supplemental info block begins at an 8-byte boundary. + if (info_offset & 4) + info_offset += 4; + } + + 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() + * this->incr_reloc_size); + + // Set the size of the .gnu_incremental_got_plt section. + Sized_target* target = + parameters->sized_target(); + unsigned int got_count = target->got_entry_count(); + unsigned int plt_count = target->plt_entry_count(); + unsigned int got_plt_size = 8; // GOT entry count, PLT entry count. + got_plt_size = (got_plt_size + got_count + 3) & ~3; // GOT type array. + got_plt_size += got_count * 8 + plt_count * 4; // GOT array, PLT array. + inputs->got_plt_section()->set_current_data_size(got_plt_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(static_cast(global_sym_count) * 4 == symtab_size); + this->write_symtab(symtab_view, global_syms, global_sym_count); + + delete[] global_syms; + + // Write the .gnu_incremental_got_plt section. + const off_t got_plt_off = inputs->got_plt_section()->offset(); + const off_t got_plt_size = inputs->got_plt_section()->data_size(); + unsigned char* const got_plt_view = of->get_output_view(got_plt_off, + got_plt_size); + this->write_got_plt(got_plt_view, got_plt_size); + + of->write_output_view(off, oview_size, oview); + of->write_output_view(symtab_off, symtab_size, symtab_view); + of->write_output_view(got_plt_off, got_plt_size, got_plt_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); + gold_assert(this->header_size == 16); + 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(static_cast(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(); + unsigned int flags = (*p)->type(); + if ((*p)->is_in_system_directory()) + flags |= INCREMENTAL_INPUT_IN_SYSTEM_DIR; + if ((*p)->as_needed()) + flags |= INCREMENTAL_INPUT_AS_NEEDED; + 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, flags); + Swap16::writeval(pov + 22, (*p)->arg_serial()); + gold_assert(this->input_entry_size == 24); + 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: + { + gold_assert(static_cast(pov - oview) + == (*p)->get_info_offset()); + Incremental_script_entry* entry = (*p)->script_entry(); + gold_assert(entry != NULL); + + // Write the object count. + unsigned int nobjects = entry->get_object_count(); + Swap32::writeval(pov, nobjects); + pov += 4; + + // For each object, write the offset to its input file entry. + for (unsigned int i = 0; i < nobjects; ++i) + { + Incremental_input_entry* obj = entry->get_object(i); + Swap32::writeval(pov, obj->get_offset()); + pov += 4; + } + } + break; + + case INCREMENTAL_INPUT_OBJECT: + case INCREMENTAL_INPUT_ARCHIVE_MEMBER: + { + gold_assert(static_cast(pov - oview) + == (*p)->get_info_offset()); + Incremental_object_entry* entry = (*p)->object_entry(); + gold_assert(entry != NULL); + const Object* obj = entry->object(); + const Relobj* relobj = static_cast(obj); + 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(); + off_t locals_offset = relobj->local_symbol_offset(); + unsigned int nlocals = relobj->output_local_symbol_count(); + unsigned int first_dynrel = relobj->first_dyn_reloc(); + unsigned int ndynrel = relobj->dyn_reloc_count(); + unsigned int ncomdat = entry->get_comdat_group_count(); + Swap32::writeval(pov, nsections); + Swap32::writeval(pov + 4, nsyms); + Swap32::writeval(pov + 8, static_cast(locals_offset)); + Swap32::writeval(pov + 12, nlocals); + Swap32::writeval(pov + 16, first_dynrel); + Swap32::writeval(pov + 20, ndynrel); + Swap32::writeval(pov + 24, ncomdat); + Swap32::writeval(pov + 28, 0); + gold_assert(this->object_info_size == 32); + pov += this->object_info_size; + + // Build a temporary array to map input section indexes + // from the original object file index to the index in the + // incremental info table. + unsigned int* index_map = new unsigned int[obj->shnum()]; + memset(index_map, 0, obj->shnum() * sizeof(unsigned int)); + + // 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++) + { + unsigned int shndx = entry->get_input_section_index(i); + index_map[shndx] = i + 1; + 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(shndx); + if (os != NULL) + { + out_shndx = os->out_shndx(); + out_offset = obj->output_section_offset(shndx); + 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); + gold_assert(this->input_section_entry_size + == 8 + 2 * sizeof_addr); + pov += this->input_section_entry_size; + } + + // 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, + // input section 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]; + if (sym->is_forwarder()) + sym = this->symtab_->resolve_forwards(sym); + unsigned int shndx = 0; + if (sym->source() != Symbol::FROM_OBJECT) + { + // The symbol was defined by the linker (e.g., common). + // We mark these symbols with a special SHNDX of -1, + // but exclude linker-predefined symbols and symbols + // copied from shared objects. + if (!sym->is_predefined() + && !sym->is_copied_from_dynobj()) + shndx = -1U; + } + else if (sym->object() == obj && sym->is_defined()) + { + bool is_ordinary; + unsigned int orig_shndx = sym->shndx(&is_ordinary); + if (is_ordinary) + shndx = index_map[orig_shndx]; + else + shndx = 1; + } + 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, shndx); + Swap32::writeval(pov + 8, chain); + Swap32::writeval(pov + 12, nrelocs); + Swap32::writeval(pov + 16, + first_reloc * (8 + 2 * sizeof_addr)); + gold_assert(this->global_sym_entry_size == 20); + pov += this->global_sym_entry_size; + } + + // For each kept COMDAT group, write the group signature. + for (unsigned int i = 0; i < ncomdat; i++) + { + Stringpool::Key key = entry->get_comdat_signature_key(i); + off_t name_offset = 0; + if (key != 0) + name_offset = strtab->get_offset_from_key(key); + Swap32::writeval(pov, name_offset); + pov += 4; + } + + delete[] index_map; + } + break; + + case INCREMENTAL_INPUT_SHARED_LIBRARY: + { + gold_assert(static_cast(pov - oview) + == (*p)->get_info_offset()); + Incremental_dynobj_entry* entry = (*p)->dynobj_entry(); + gold_assert(entry != NULL); + Object* obj = entry->object(); + Dynobj* dynobj = obj->dynobj(); + gold_assert(dynobj != NULL); + const Object::Symbols* syms = obj->get_global_symbols(); + + // Write the soname string table index. + section_offset_type soname_offset = + strtab->get_offset_from_key(entry->get_soname_key()); + Swap32::writeval(pov, soname_offset); + pov += 4; + + // Skip the global symbol count for now. + unsigned char* orig_pov = pov; + pov += 4; + + // For each global symbol, write the global symbol table index. + unsigned int nsyms = syms->size(); + unsigned int nsyms_out = 0; + for (unsigned int i = 0; i < nsyms; i++) + { + const Symbol* sym = (*syms)[i]; + if (sym == NULL) + continue; + if (sym->is_forwarder()) + sym = this->symtab_->resolve_forwards(sym); + if (sym->symtab_index() == -1U) + continue; + unsigned int flags = 0; + // If the symbol has hidden or internal visibility, we + // mark it as defined in the shared object so we don't + // try to resolve it during an incremental update. + if (sym->visibility() == elfcpp::STV_HIDDEN + || sym->visibility() == elfcpp::STV_INTERNAL) + flags = INCREMENTAL_SHLIB_SYM_DEF; + else if (sym->source() == Symbol::FROM_OBJECT + && sym->object() == obj + && sym->is_defined()) + flags = INCREMENTAL_SHLIB_SYM_DEF; + else if (sym->is_copied_from_dynobj() + && this->symtab_->get_copy_source(sym) == dynobj) + flags = INCREMENTAL_SHLIB_SYM_COPY; + flags <<= INCREMENTAL_SHLIB_SYM_FLAGS_SHIFT; + Swap32::writeval(pov, sym->symtab_index() | flags); + pov += 4; + ++nsyms_out; + } + + // Now write the global symbol count. + Swap32::writeval(orig_pov, nsyms_out); + } + break; + + case INCREMENTAL_INPUT_ARCHIVE: + { + gold_assert(static_cast(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(); + } + + // Pad the info block to a multiple of 8 bytes. + if (static_cast(pov - oview) & 4) + { + Swap32::writeval(pov, 0); + pov += 4; + } + } + 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; + } +} + +// This struct holds the view information needed to write the +// .gnu_incremental_got_plt section. + +struct Got_plt_view_info +{ + // Start of the GOT type array in the output view. + unsigned char* got_type_p; + // Start of the GOT descriptor array in the output view. + unsigned char* got_desc_p; + // Start of the PLT descriptor array in the output view. + unsigned char* plt_desc_p; + // Number of GOT entries. + unsigned int got_count; + // Number of PLT entries. + unsigned int plt_count; + // Offset of the first non-reserved PLT entry (this is a target-dependent value). + unsigned int first_plt_entry_offset; + // Size of a PLT entry (this is a target-dependent value). + unsigned int plt_entry_size; + // Size of a GOT entry (this is a target-dependent value). + unsigned int got_entry_size; + // Symbol index to write in the GOT descriptor array. For global symbols, + // this is the global symbol table index; for local symbols, it is the + // local symbol table index. + unsigned int sym_index; + // Input file index to write in the GOT descriptor array. For global + // symbols, this is 0; for local symbols, it is the index of the input + // file entry in the .gnu_incremental_inputs section. + unsigned int input_index; +}; + +// Functor class for processing a GOT offset list for local symbols. +// Writes the GOT type and symbol index into the GOT type and descriptor +// arrays in the output section. + +template +class Local_got_offset_visitor : public Got_offset_list::Visitor +{ + public: + Local_got_offset_visitor(struct Got_plt_view_info& info) + : info_(info) + { } + + void + visit(unsigned int got_type, unsigned int got_offset) + { + unsigned int got_index = got_offset / this->info_.got_entry_size; + gold_assert(got_index < this->info_.got_count); + // We can only handle GOT entry types in the range 0..0x7e + // because we use a byte array to store them, and we use the + // high bit to flag a local symbol. + gold_assert(got_type < 0x7f); + this->info_.got_type_p[got_index] = got_type | 0x80; + unsigned char* pov = this->info_.got_desc_p + got_index * 8; + elfcpp::Swap<32, big_endian>::writeval(pov, this->info_.sym_index); + elfcpp::Swap<32, big_endian>::writeval(pov + 4, this->info_.input_index); + } + + private: + struct Got_plt_view_info& info_; +}; + +// Functor class for processing a GOT offset list. Writes the GOT type +// and symbol index into the GOT type and descriptor arrays in the output +// section. + +template +class Global_got_offset_visitor : public Got_offset_list::Visitor +{ + public: + Global_got_offset_visitor(struct Got_plt_view_info& info) + : info_(info) + { } + + void + visit(unsigned int got_type, unsigned int got_offset) + { + unsigned int got_index = got_offset / this->info_.got_entry_size; + gold_assert(got_index < this->info_.got_count); + // We can only handle GOT entry types in the range 0..0x7e + // because we use a byte array to store them, and we use the + // high bit to flag a local symbol. + gold_assert(got_type < 0x7f); + this->info_.got_type_p[got_index] = got_type; + unsigned char* pov = this->info_.got_desc_p + got_index * 8; + elfcpp::Swap<32, big_endian>::writeval(pov, this->info_.sym_index); + elfcpp::Swap<32, big_endian>::writeval(pov + 4, 0); + } + + private: + struct Got_plt_view_info& info_; +}; + +// Functor class for processing the global symbol table. Processes the +// GOT offset list for the symbol, and writes the symbol table index +// into the PLT descriptor array in the output section. + +template +class Global_symbol_visitor_got_plt +{ + public: + Global_symbol_visitor_got_plt(struct Got_plt_view_info& info) + : info_(info) + { } + + void + operator()(const Sized_symbol* sym) + { + typedef Global_got_offset_visitor Got_visitor; + const Got_offset_list* got_offsets = sym->got_offset_list(); + if (got_offsets != NULL) + { + this->info_.sym_index = sym->symtab_index(); + this->info_.input_index = 0; + Got_visitor v(this->info_); + got_offsets->for_all_got_offsets(&v); + } + if (sym->has_plt_offset()) + { + unsigned int plt_index = + ((sym->plt_offset() - this->info_.first_plt_entry_offset) + / this->info_.plt_entry_size); + gold_assert(plt_index < this->info_.plt_count); + unsigned char* pov = this->info_.plt_desc_p + plt_index * 4; + elfcpp::Swap<32, big_endian>::writeval(pov, sym->symtab_index()); + } + } - 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); + private: + struct Got_plt_view_info& info_; +}; - for (Inputs_info_map::const_iterator it = this->inputs_map_.begin(); - it != this->inputs_map_.end(); - ++it) +// Write the contents of the .gnu_incremental_got_plt section. + +template +void +Output_section_incremental_inputs::write_got_plt( + unsigned char* pov, + off_t view_size) +{ + Sized_target* target = + parameters->sized_target(); + + // Set up the view information for the functors. + struct Got_plt_view_info view_info; + view_info.got_count = target->got_entry_count(); + view_info.plt_count = target->plt_entry_count(); + view_info.first_plt_entry_offset = target->first_plt_entry_offset(); + view_info.plt_entry_size = target->plt_entry_size(); + view_info.got_entry_size = target->got_entry_size(); + view_info.got_type_p = pov + 8; + view_info.got_desc_p = (view_info.got_type_p + + ((view_info.got_count + 3) & ~3)); + view_info.plt_desc_p = view_info.got_desc_p + view_info.got_count * 8; + + gold_assert(pov + view_size == + view_info.plt_desc_p + view_info.plt_count * 4); + + // Write the section header. + Swap32::writeval(pov, view_info.got_count); + Swap32::writeval(pov + 4, view_info.plt_count); + + // Initialize the GOT type array to 0xff (reserved). + memset(view_info.got_type_p, 0xff, view_info.got_count); + + // Write the incremental GOT descriptors for local symbols. + typedef Local_got_offset_visitor Got_visitor; + for (Incremental_inputs::Input_list::const_iterator p = + this->inputs_->input_files().begin(); + p != this->inputs_->input_files().end(); + ++p) { - gold_assert(it->second.index < this->inputs_map_.size()); + if ((*p)->type() != INCREMENTAL_INPUT_OBJECT + && (*p)->type() != INCREMENTAL_INPUT_ARCHIVE_MEMBER) + continue; + Incremental_object_entry* entry = (*p)->object_entry(); + gold_assert(entry != NULL); + const Object* obj = entry->object(); + gold_assert(obj != NULL); + view_info.input_index = (*p)->get_file_index(); + Got_visitor v(view_info); + obj->for_all_local_got_entries(&v); + } - 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); - // TODO: add per input data and timestamp. 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); - 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); + // Write the incremental GOT and PLT descriptors for global symbols. + typedef Global_symbol_visitor_got_plt Symbol_visitor; + symtab_->for_all_symbols(Symbol_visitor(view_info)); +} + +// Class Sized_relobj_incr. Most of these methods are not used for +// Incremental objects, but are required to be implemented by the +// base class Object. + +template +Sized_relobj_incr::Sized_relobj_incr( + const std::string& name, + Sized_incremental_binary* ibase, + unsigned int input_file_index) + : Sized_relobj(name, NULL), ibase_(ibase), + input_file_index_(input_file_index), + input_reader_(ibase->inputs_reader().input_file(input_file_index)), + local_symbol_count_(0), output_local_dynsym_count_(0), + local_symbol_index_(0), local_symbol_offset_(0), local_dynsym_offset_(0), + symbols_(), defined_count_(0), incr_reloc_offset_(-1U), + incr_reloc_count_(0), incr_reloc_output_index_(0), incr_relocs_(NULL), + local_symbols_() +{ + if (this->input_reader_.is_in_system_directory()) + this->set_is_in_system_directory(); + const unsigned int shnum = this->input_reader_.get_input_section_count() + 1; + this->set_shnum(shnum); + ibase->set_input_object(input_file_index, this); +} + +// Read the symbols. + +template +void +Sized_relobj_incr::do_read_symbols(Read_symbols_data*) +{ + gold_unreachable(); +} + +// Lay out the input sections. + +template +void +Sized_relobj_incr::do_layout( + Symbol_table*, + Layout* layout, + Read_symbols_data*) +{ + const unsigned int shnum = this->shnum(); + Incremental_inputs* incremental_inputs = layout->incremental_inputs(); + gold_assert(incremental_inputs != NULL); + Output_sections& out_sections(this->output_sections()); + out_sections.resize(shnum); + this->section_offsets().resize(shnum); + + // Keep track of .debug_info and .debug_types sections. + std::vector debug_info_sections; + std::vector debug_types_sections; + + for (unsigned int i = 1; i < shnum; i++) + { + typename Input_entry_reader::Input_section_info sect = + this->input_reader_.get_input_section(i - 1); + // Add the section to the incremental inputs layout. + incremental_inputs->report_input_section(this, i, sect.name, + sect.sh_size); + if (sect.output_shndx == 0 || sect.sh_offset == -1) + continue; + Output_section* os = this->ibase_->output_section(sect.output_shndx); + gold_assert(os != NULL); + out_sections[i] = os; + this->section_offsets()[i] = static_cast
(sect.sh_offset); + + // When generating a .gdb_index section, we do additional + // processing of .debug_info and .debug_types sections after all + // the other sections. + if (parameters->options().gdb_index()) + { + const char* name = os->name(); + if (strcmp(name, ".debug_info") == 0) + debug_info_sections.push_back(i); + else if (strcmp(name, ".debug_types") == 0) + debug_types_sections.push_back(i); + } + } + + // Process the COMDAT groups. + unsigned int ncomdat = this->input_reader_.get_comdat_group_count(); + for (unsigned int i = 0; i < ncomdat; i++) + { + const char* signature = this->input_reader_.get_comdat_group_signature(i); + if (signature == NULL || signature[0] == '\0') + this->error(_("COMDAT group has no signature")); + bool keep = layout->find_or_add_kept_section(signature, this, i, true, + true, NULL); + if (keep) + incremental_inputs->report_comdat_group(this, signature); + else + this->error(_("COMDAT group %s included twice in incremental link"), + signature); + } + + // When building a .gdb_index section, scan the .debug_info and + // .debug_types sections. + for (std::vector::const_iterator p + = debug_info_sections.begin(); + p != debug_info_sections.end(); + ++p) + { + unsigned int i = *p; + layout->add_to_gdb_index(false, this, NULL, 0, i, 0, 0); } + for (std::vector::const_iterator p + = debug_types_sections.begin(); + p != debug_types_sections.end(); + ++p) + { + unsigned int i = *p; + layout->add_to_gdb_index(true, this, 0, 0, i, 0, 0); + } +} - return new Output_data_const_buffer(buffer, sz, 8, - "** incremental link inputs list"); +// Layout sections whose layout was deferred while waiting for +// input files from a plugin. +template +void +Sized_relobj_incr::do_layout_deferred_sections(Layout*) +{ +} + +// Add the symbols to the symbol table. + +template +void +Sized_relobj_incr::do_add_symbols( + Symbol_table* symtab, + Read_symbols_data*, + Layout*) +{ + const int sym_size = elfcpp::Elf_sizes::sym_size; + unsigned char symbuf[sym_size]; + elfcpp::Sym sym(symbuf); + elfcpp::Sym_write osym(symbuf); + + typedef typename elfcpp::Elf_types::Elf_WXword Elf_size_type; + + unsigned int nsyms = this->input_reader_.get_global_symbol_count(); + this->symbols_.resize(nsyms); + + Incremental_binary::View symtab_view(NULL); + unsigned int symtab_count; + elfcpp::Elf_strtab strtab(NULL, 0); + this->ibase_->get_symtab_view(&symtab_view, &symtab_count, &strtab); + + Incremental_symtab_reader isymtab(this->ibase_->symtab_reader()); + unsigned int isym_count = isymtab.symbol_count(); + unsigned int first_global = symtab_count - isym_count; + + const unsigned char* sym_p; + for (unsigned int i = 0; i < nsyms; ++i) + { + Incremental_global_symbol_reader info = + this->input_reader_.get_global_symbol_reader(i); + unsigned int output_symndx = info.output_symndx(); + sym_p = symtab_view.data() + output_symndx * sym_size; + elfcpp::Sym gsym(sym_p); + const char* name; + if (!strtab.get_c_string(gsym.get_st_name(), &name)) + name = ""; + + typename elfcpp::Elf_types::Elf_Addr v = gsym.get_st_value(); + unsigned int shndx = gsym.get_st_shndx(); + elfcpp::STB st_bind = gsym.get_st_bind(); + elfcpp::STT st_type = gsym.get_st_type(); + + // Local hidden symbols start out as globals, but get converted to + // to local during output. + if (st_bind == elfcpp::STB_LOCAL) + st_bind = elfcpp::STB_GLOBAL; + + unsigned int input_shndx = info.shndx(); + if (input_shndx == 0 || input_shndx == -1U) + { + shndx = elfcpp::SHN_UNDEF; + v = 0; + } + else if (shndx != elfcpp::SHN_ABS) + { + // Find the input section and calculate the section-relative value. + gold_assert(shndx != elfcpp::SHN_UNDEF); + Output_section* os = this->ibase_->output_section(shndx); + gold_assert(os != NULL && os->has_fixed_layout()); + typename Input_entry_reader::Input_section_info sect = + this->input_reader_.get_input_section(input_shndx - 1); + gold_assert(sect.output_shndx == shndx); + if (st_type != elfcpp::STT_TLS) + v -= os->address(); + v -= sect.sh_offset; + shndx = input_shndx; + } + + osym.put_st_name(0); + osym.put_st_value(v); + osym.put_st_size(gsym.get_st_size()); + osym.put_st_info(st_bind, st_type); + osym.put_st_other(gsym.get_st_other()); + osym.put_st_shndx(shndx); + + Symbol* res = symtab->add_from_incrobj(this, name, NULL, &sym); + + if (shndx != elfcpp::SHN_UNDEF) + ++this->defined_count_; + + // If this is a linker-defined symbol that hasn't yet been defined, + // define it now. + if (input_shndx == -1U && !res->is_defined()) + { + shndx = gsym.get_st_shndx(); + v = gsym.get_st_value(); + Elf_size_type symsize = gsym.get_st_size(); + if (shndx == elfcpp::SHN_ABS) + { + symtab->define_as_constant(name, NULL, + Symbol_table::INCREMENTAL_BASE, + v, symsize, st_type, st_bind, + gsym.get_st_visibility(), 0, + false, false); + } + else + { + Output_section* os = this->ibase_->output_section(shndx); + gold_assert(os != NULL && os->has_fixed_layout()); + v -= os->address(); + if (symsize > 0) + os->reserve(v, symsize); + symtab->define_in_output_data(name, NULL, + Symbol_table::INCREMENTAL_BASE, + os, v, symsize, st_type, st_bind, + gsym.get_st_visibility(), 0, + false, false); + } + } + + this->symbols_[i] = res; + this->ibase_->add_global_symbol(output_symndx - first_global, res); + } +} + +// Return TRUE if we should include this object from an archive library. + +template +Archive::Should_include +Sized_relobj_incr::do_should_include_member( + Symbol_table*, + Layout*, + Read_symbols_data*, + std::string*) +{ + gold_unreachable(); +} + +// Iterate over global symbols, calling a visitor class V for each. + +template +void +Sized_relobj_incr::do_for_all_global_symbols( + Read_symbols_data*, + Library_base::Symbol_visitor_base*) +{ + // This routine is not used for incremental objects. +} + +// Get the size of a section. + +template +uint64_t +Sized_relobj_incr::do_section_size(unsigned int) +{ + gold_unreachable(); +} + +// Get the name of a section. This returns the name of the output +// section, because we don't usually track the names of the input +// sections. + +template +std::string +Sized_relobj_incr::do_section_name(unsigned int shndx) const +{ + const Output_sections& out_sections(this->output_sections()); + const Output_section* os = out_sections[shndx]; + if (os == NULL) + return NULL; + return os->name(); +} + +// Return a view of the contents of a section. + +template +const unsigned char* +Sized_relobj_incr::do_section_contents( + unsigned int shndx, + section_size_type* plen, + bool) +{ + Output_sections& out_sections(this->output_sections()); + Output_section* os = out_sections[shndx]; + gold_assert(os != NULL); + off_t section_offset = os->offset(); + typename Input_entry_reader::Input_section_info sect = + this->input_reader_.get_input_section(shndx - 1); + section_offset += sect.sh_offset; + *plen = sect.sh_size; + return this->ibase_->view(section_offset, sect.sh_size).data(); +} + +// Return section flags. + +template +uint64_t +Sized_relobj_incr::do_section_flags(unsigned int) +{ + gold_unreachable(); +} + +// Return section entsize. + +template +uint64_t +Sized_relobj_incr::do_section_entsize(unsigned int) +{ + gold_unreachable(); +} + +// Return section address. + +template +uint64_t +Sized_relobj_incr::do_section_address(unsigned int) +{ + gold_unreachable(); +} + +// Return section type. + +template +unsigned int +Sized_relobj_incr::do_section_type(unsigned int) +{ + gold_unreachable(); +} + +// Return the section link field. + +template +unsigned int +Sized_relobj_incr::do_section_link(unsigned int) +{ + gold_unreachable(); +} + +// Return the section link field. + +template +unsigned int +Sized_relobj_incr::do_section_info(unsigned int) +{ + gold_unreachable(); +} + +// Return the section alignment. + +template +uint64_t +Sized_relobj_incr::do_section_addralign(unsigned int) +{ + gold_unreachable(); +} + +// Return the Xindex structure to use. + +template +Xindex* +Sized_relobj_incr::do_initialize_xindex() +{ + gold_unreachable(); +} + +// Get symbol counts. + +template +void +Sized_relobj_incr::do_get_global_symbol_counts( + const Symbol_table*, + size_t* defined, + size_t* used) const +{ + *defined = this->defined_count_; + size_t count = 0; + for (typename Symbols::const_iterator p = this->symbols_.begin(); + p != this->symbols_.end(); + ++p) + if (*p != NULL + && (*p)->source() == Symbol::FROM_OBJECT + && (*p)->object() == this + && (*p)->is_defined()) + ++count; + *used = count; +} + +// Read the relocs. + +template +void +Sized_relobj_incr::do_read_relocs(Read_relocs_data*) +{ +} + +// Process the relocs to find list of referenced sections. Used only +// during garbage collection. + +template +void +Sized_relobj_incr::do_gc_process_relocs(Symbol_table*, + Layout*, + Read_relocs_data*) +{ + gold_unreachable(); +} + +// Scan the relocs and adjust the symbol table. + +template +void +Sized_relobj_incr::do_scan_relocs(Symbol_table*, + Layout* layout, + Read_relocs_data*) +{ + // Count the incremental relocations for this object. + unsigned int nsyms = this->input_reader_.get_global_symbol_count(); + this->allocate_incremental_reloc_counts(); + for (unsigned int i = 0; i < nsyms; i++) + { + Incremental_global_symbol_reader sym = + this->input_reader_.get_global_symbol_reader(i); + unsigned int reloc_count = sym.reloc_count(); + if (reloc_count > 0 && this->incr_reloc_offset_ == -1U) + this->incr_reloc_offset_ = sym.reloc_offset(); + this->incr_reloc_count_ += reloc_count; + for (unsigned int j = 0; j < reloc_count; j++) + this->count_incremental_reloc(i); + } + this->incr_reloc_output_index_ = + layout->incremental_inputs()->get_reloc_count(); + this->finalize_incremental_relocs(layout, false); + + // The incoming incremental relocations may not end up in the same + // location after the incremental update, because the incremental info + // is regenerated in each link. Because the new location may overlap + // with other data in the updated output file, we need to copy the + // relocations into a buffer so that we can still read them safely + // after we start writing updates to the output file. + if (this->incr_reloc_count_ > 0) + { + const Incremental_relocs_reader& relocs_reader = + this->ibase_->relocs_reader(); + const unsigned int incr_reloc_size = relocs_reader.reloc_size; + unsigned int len = this->incr_reloc_count_ * incr_reloc_size; + this->incr_relocs_ = new unsigned char[len]; + memcpy(this->incr_relocs_, + relocs_reader.data(this->incr_reloc_offset_), + len); + } +} + +// Count the local symbols. + +template +void +Sized_relobj_incr::do_count_local_symbols( + Stringpool_template* pool, + Stringpool_template*) +{ + const int sym_size = elfcpp::Elf_sizes::sym_size; + + // Set the count of local symbols based on the incremental info. + unsigned int nlocals = this->input_reader_.get_local_symbol_count(); + this->local_symbol_count_ = nlocals; + this->local_symbols_.reserve(nlocals); + + // Get views of the base file's symbol table and string table. + Incremental_binary::View symtab_view(NULL); + unsigned int symtab_count; + elfcpp::Elf_strtab strtab(NULL, 0); + this->ibase_->get_symtab_view(&symtab_view, &symtab_count, &strtab); + + // Read the local symbols from the base file's symbol table. + off_t off = this->input_reader_.get_local_symbol_offset(); + const unsigned char* symp = symtab_view.data() + off; + for (unsigned int i = 0; i < nlocals; ++i, symp += sym_size) + { + elfcpp::Sym sym(symp); + const char* name; + if (!strtab.get_c_string(sym.get_st_name(), &name)) + name = ""; + gold_debug(DEBUG_INCREMENTAL, "Local symbol %d: %s", i, name); + name = pool->add(name, true, NULL); + this->local_symbols_.push_back(Local_symbol(name, + sym.get_st_value(), + sym.get_st_size(), + sym.get_st_shndx(), + sym.get_st_type(), + false)); + } +} + +// Finalize the local symbols. + +template +unsigned int +Sized_relobj_incr::do_finalize_local_symbols( + unsigned int index, + off_t off, + Symbol_table*) +{ + this->local_symbol_index_ = index; + this->local_symbol_offset_ = off; + return index + this->local_symbol_count_; +} + +// Set the offset where local dynamic symbol information will be stored. + +template +unsigned int +Sized_relobj_incr::do_set_local_dynsym_indexes( + unsigned int index) +{ + // FIXME: set local dynsym indexes. + return index; +} + +// Set the offset where local dynamic symbol information will be stored. + +template +unsigned int +Sized_relobj_incr::do_set_local_dynsym_offset(off_t) +{ + return 0; +} + +// Relocate the input sections and write out the local symbols. +// We don't actually do any relocation here. For unchanged input files, +// we reapply relocations only for symbols that have changed; that happens +// in Layout_task_runner::run(). We do need to rewrite the incremental +// relocations for this object. + +template +void +Sized_relobj_incr::do_relocate(const Symbol_table*, + const Layout* layout, + Output_file* of) +{ + if (this->incr_reloc_count_ == 0) + return; + + const unsigned int incr_reloc_size = + Incremental_relocs_reader::reloc_size; + + // Get a view for the .gnu_incremental_relocs section. + Incremental_inputs* inputs = 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); + + // Copy the relocations from the buffer. + off_t off = this->incr_reloc_output_index_ * incr_reloc_size; + unsigned int len = this->incr_reloc_count_ * incr_reloc_size; + memcpy(view + off, this->incr_relocs_, len); + + // The output section table may have changed, so we need to map + // the old section index to the new section index for each relocation. + for (unsigned int i = 0; i < this->incr_reloc_count_; ++i) + { + unsigned char* pov = view + off + i * incr_reloc_size; + unsigned int shndx = elfcpp::Swap<32, big_endian>::readval(pov + 4); + Output_section* os = this->ibase_->output_section(shndx); + gold_assert(os != NULL); + shndx = os->out_shndx(); + elfcpp::Swap<32, big_endian>::writeval(pov + 4, shndx); + } + + of->write_output_view(off, len, view); + + // Get views into the output file for the portions of the symbol table + // and the dynamic symbol table that we will be writing. + off_t symtab_off = layout->symtab_section()->offset(); + off_t output_size = this->local_symbol_count_ * This::sym_size; + unsigned char* oview = NULL; + if (output_size > 0) + oview = of->get_output_view(symtab_off + this->local_symbol_offset_, + output_size); + + off_t dyn_output_size = this->output_local_dynsym_count_ * sym_size; + unsigned char* dyn_oview = NULL; + if (dyn_output_size > 0) + dyn_oview = of->get_output_view(this->local_dynsym_offset_, + dyn_output_size); + + // Write the local symbols. + unsigned char* ov = oview; + unsigned char* dyn_ov = dyn_oview; + const Stringpool* sympool = layout->sympool(); + const Stringpool* dynpool = layout->dynpool(); + Output_symtab_xindex* symtab_xindex = layout->symtab_xindex(); + Output_symtab_xindex* dynsym_xindex = layout->dynsym_xindex(); + for (unsigned int i = 0; i < this->local_symbol_count_; ++i) + { + Local_symbol& lsym(this->local_symbols_[i]); + + bool is_ordinary; + unsigned int st_shndx = this->adjust_sym_shndx(i, lsym.st_shndx, + &is_ordinary); + if (is_ordinary) + { + Output_section* os = this->ibase_->output_section(st_shndx); + st_shndx = os->out_shndx(); + if (st_shndx >= elfcpp::SHN_LORESERVE) + { + symtab_xindex->add(this->local_symbol_index_ + i, st_shndx); + if (lsym.needs_dynsym_entry) + dynsym_xindex->add(lsym.output_dynsym_index, st_shndx); + st_shndx = elfcpp::SHN_XINDEX; + } + } + + // Write the symbol to the output symbol table. + { + elfcpp::Sym_write osym(ov); + osym.put_st_name(sympool->get_offset(lsym.name)); + osym.put_st_value(lsym.st_value); + osym.put_st_size(lsym.st_size); + osym.put_st_info(elfcpp::STB_LOCAL, + static_cast(lsym.st_type)); + osym.put_st_other(0); + osym.put_st_shndx(st_shndx); + ov += sym_size; + } + + // Write the symbol to the output dynamic symbol table. + if (lsym.needs_dynsym_entry) + { + gold_assert(dyn_ov < dyn_oview + dyn_output_size); + elfcpp::Sym_write osym(dyn_ov); + osym.put_st_name(dynpool->get_offset(lsym.name)); + osym.put_st_value(lsym.st_value); + osym.put_st_size(lsym.st_size); + osym.put_st_info(elfcpp::STB_LOCAL, + static_cast(lsym.st_type)); + osym.put_st_other(0); + osym.put_st_shndx(st_shndx); + dyn_ov += sym_size; + } + } + + if (output_size > 0) + { + gold_assert(ov - oview == output_size); + of->write_output_view(symtab_off + this->local_symbol_offset_, + output_size, oview); + } + + if (dyn_output_size > 0) + { + gold_assert(dyn_ov - dyn_oview == dyn_output_size); + of->write_output_view(this->local_dynsym_offset_, dyn_output_size, + dyn_oview); + } +} + +// Set the offset of a section. + +template +void +Sized_relobj_incr::do_set_section_offset(unsigned int, + uint64_t) +{ +} + +// Class Sized_incr_dynobj. Most of these methods are not used for +// Incremental objects, but are required to be implemented by the +// base class Object. + +template +Sized_incr_dynobj::Sized_incr_dynobj( + const std::string& name, + Sized_incremental_binary* ibase, + unsigned int input_file_index) + : Dynobj(name, NULL), ibase_(ibase), + input_file_index_(input_file_index), + input_reader_(ibase->inputs_reader().input_file(input_file_index)), + symbols_(), defined_count_(0) +{ + if (this->input_reader_.is_in_system_directory()) + this->set_is_in_system_directory(); + if (this->input_reader_.as_needed()) + this->set_as_needed(); + this->set_soname_string(this->input_reader_.get_soname()); + this->set_shnum(0); +} + +// Read the symbols. + +template +void +Sized_incr_dynobj::do_read_symbols(Read_symbols_data*) +{ + gold_unreachable(); +} + +// Lay out the input sections. + +template +void +Sized_incr_dynobj::do_layout( + Symbol_table*, + Layout*, + Read_symbols_data*) +{ +} + +// Add the symbols to the symbol table. + +template +void +Sized_incr_dynobj::do_add_symbols( + Symbol_table* symtab, + Read_symbols_data*, + Layout*) +{ + const int sym_size = elfcpp::Elf_sizes::sym_size; + unsigned char symbuf[sym_size]; + elfcpp::Sym sym(symbuf); + elfcpp::Sym_write osym(symbuf); + + unsigned int nsyms = this->input_reader_.get_global_symbol_count(); + this->symbols_.resize(nsyms); + + Incremental_binary::View symtab_view(NULL); + unsigned int symtab_count; + elfcpp::Elf_strtab strtab(NULL, 0); + this->ibase_->get_symtab_view(&symtab_view, &symtab_count, &strtab); + + Incremental_symtab_reader isymtab(this->ibase_->symtab_reader()); + unsigned int isym_count = isymtab.symbol_count(); + unsigned int first_global = symtab_count - isym_count; + + // We keep a set of symbols that we have generated COPY relocations + // for, indexed by the symbol value. We do not need more than one + // COPY relocation per address. + typedef typename std::set
Copied_symbols; + Copied_symbols copied_symbols; + + const unsigned char* sym_p; + for (unsigned int i = 0; i < nsyms; ++i) + { + bool is_def; + bool is_copy; + unsigned int output_symndx = + this->input_reader_.get_output_symbol_index(i, &is_def, &is_copy); + sym_p = symtab_view.data() + output_symndx * sym_size; + elfcpp::Sym gsym(sym_p); + const char* name; + if (!strtab.get_c_string(gsym.get_st_name(), &name)) + name = ""; + + Address v; + unsigned int shndx; + elfcpp::STB st_bind = gsym.get_st_bind(); + elfcpp::STT st_type = gsym.get_st_type(); + + // Local hidden symbols start out as globals, but get converted to + // to local during output. + if (st_bind == elfcpp::STB_LOCAL) + st_bind = elfcpp::STB_GLOBAL; + + if (!is_def) + { + shndx = elfcpp::SHN_UNDEF; + v = 0; + } + else + { + // For a symbol defined in a shared object, the section index + // is meaningless, as long as it's not SHN_UNDEF. + shndx = 1; + v = gsym.get_st_value(); + ++this->defined_count_; + } + + osym.put_st_name(0); + osym.put_st_value(v); + osym.put_st_size(gsym.get_st_size()); + osym.put_st_info(st_bind, st_type); + osym.put_st_other(gsym.get_st_other()); + osym.put_st_shndx(shndx); + + Sized_symbol* res = + symtab->add_from_incrobj(this, name, NULL, &sym); + this->symbols_[i] = res; + this->ibase_->add_global_symbol(output_symndx - first_global, + this->symbols_[i]); + + if (is_copy) + { + std::pair ins = + copied_symbols.insert(v); + if (ins.second) + { + unsigned int shndx = gsym.get_st_shndx(); + Output_section* os = this->ibase_->output_section(shndx); + off_t offset = v - os->address(); + this->ibase_->add_copy_reloc(this->symbols_[i], os, offset); + } + } + } +} + +// Return TRUE if we should include this object from an archive library. + +template +Archive::Should_include +Sized_incr_dynobj::do_should_include_member( + Symbol_table*, + Layout*, + Read_symbols_data*, + std::string*) +{ + gold_unreachable(); +} + +// Iterate over global symbols, calling a visitor class V for each. + +template +void +Sized_incr_dynobj::do_for_all_global_symbols( + Read_symbols_data*, + Library_base::Symbol_visitor_base*) +{ + // This routine is not used for dynamic libraries. +} + +// Iterate over local symbols, calling a visitor class V for each GOT offset +// associated with a local symbol. + +template +void +Sized_incr_dynobj::do_for_all_local_got_entries( + Got_offset_list::Visitor*) const +{ +} + +// Get the size of a section. + +template +uint64_t +Sized_incr_dynobj::do_section_size(unsigned int) +{ + gold_unreachable(); +} + +// Get the name of a section. + +template +std::string +Sized_incr_dynobj::do_section_name(unsigned int) const +{ + gold_unreachable(); +} + +// Return a view of the contents of a section. + +template +const unsigned char* +Sized_incr_dynobj::do_section_contents( + unsigned int, + section_size_type*, + bool) +{ + gold_unreachable(); +} + +// Return section flags. + +template +uint64_t +Sized_incr_dynobj::do_section_flags(unsigned int) +{ + gold_unreachable(); +} + +// Return section entsize. + +template +uint64_t +Sized_incr_dynobj::do_section_entsize(unsigned int) +{ + gold_unreachable(); +} + +// Return section address. + +template +uint64_t +Sized_incr_dynobj::do_section_address(unsigned int) +{ + gold_unreachable(); +} + +// Return section type. + +template +unsigned int +Sized_incr_dynobj::do_section_type(unsigned int) +{ + gold_unreachable(); +} + +// Return the section link field. + +template +unsigned int +Sized_incr_dynobj::do_section_link(unsigned int) +{ + gold_unreachable(); +} + +// Return the section link field. + +template +unsigned int +Sized_incr_dynobj::do_section_info(unsigned int) +{ + gold_unreachable(); +} + +// Return the section alignment. + +template +uint64_t +Sized_incr_dynobj::do_section_addralign(unsigned int) +{ + gold_unreachable(); +} + +// Return the Xindex structure to use. + +template +Xindex* +Sized_incr_dynobj::do_initialize_xindex() +{ + gold_unreachable(); +} + +// Get symbol counts. + +template +void +Sized_incr_dynobj::do_get_global_symbol_counts( + const Symbol_table*, + size_t* defined, + size_t* used) const +{ + *defined = this->defined_count_; + size_t count = 0; + for (typename Symbols::const_iterator p = this->symbols_.begin(); + p != this->symbols_.end(); + ++p) + if (*p != NULL + && (*p)->source() == Symbol::FROM_OBJECT + && (*p)->object() == this + && (*p)->is_defined() + && (*p)->dynsym_index() != -1U) + ++count; + *used = count; +} + +// Allocate an incremental object of the appropriate size and endianness. + +Object* +make_sized_incremental_object( + Incremental_binary* ibase, + unsigned int input_file_index, + Incremental_input_type input_type, + const Incremental_binary::Input_reader* input_reader) +{ + Object* obj = NULL; + std::string name(input_reader->filename()); + + switch (parameters->size_and_endianness()) + { +#ifdef HAVE_TARGET_32_LITTLE + case Parameters::TARGET_32_LITTLE: + { + Sized_incremental_binary<32, false>* sized_ibase = + static_cast*>(ibase); + if (input_type == INCREMENTAL_INPUT_SHARED_LIBRARY) + obj = new Sized_incr_dynobj<32, false>(name, sized_ibase, + input_file_index); + else + obj = new Sized_relobj_incr<32, false>(name, sized_ibase, + input_file_index); + } + break; +#endif +#ifdef HAVE_TARGET_32_BIG + case Parameters::TARGET_32_BIG: + { + Sized_incremental_binary<32, true>* sized_ibase = + static_cast*>(ibase); + if (input_type == INCREMENTAL_INPUT_SHARED_LIBRARY) + obj = new Sized_incr_dynobj<32, true>(name, sized_ibase, + input_file_index); + else + obj = new Sized_relobj_incr<32, true>(name, sized_ibase, + input_file_index); + } + break; +#endif +#ifdef HAVE_TARGET_64_LITTLE + case Parameters::TARGET_64_LITTLE: + { + Sized_incremental_binary<64, false>* sized_ibase = + static_cast*>(ibase); + if (input_type == INCREMENTAL_INPUT_SHARED_LIBRARY) + obj = new Sized_incr_dynobj<64, false>(name, sized_ibase, + input_file_index); + else + obj = new Sized_relobj_incr<64, false>(name, sized_ibase, + input_file_index); + } + break; +#endif +#ifdef HAVE_TARGET_64_BIG + case Parameters::TARGET_64_BIG: + { + Sized_incremental_binary<64, true>* sized_ibase = + static_cast*>(ibase); + if (input_type == INCREMENTAL_INPUT_SHARED_LIBRARY) + obj = new Sized_incr_dynobj<64, true>(name, sized_ibase, + input_file_index); + else + obj = new Sized_relobj_incr<64, true>(name, sized_ibase, + input_file_index); + } + break; +#endif + default: + gold_unreachable(); + } + + gold_assert(obj != NULL); + return obj; +} + +// Copy the unused symbols from the incremental input info. +// We need to do this because we may be overwriting the incremental +// input info in the base file before we write the new incremental +// info. +void +Incremental_library::copy_unused_symbols() +{ + unsigned int symcount = this->input_reader_->get_unused_symbol_count(); + this->unused_symbols_.reserve(symcount); + for (unsigned int i = 0; i < symcount; ++i) + { + std::string name(this->input_reader_->get_unused_symbol(i)); + this->unused_symbols_.push_back(name); + } +} + +// Iterator for unused global symbols in the library. +void +Incremental_library::do_for_all_unused_symbols(Symbol_visitor_base* v) const +{ + for (Symbol_list::const_iterator p = this->unused_symbols_.begin(); + p != this->unused_symbols_.end(); + ++p) + v->visit(p->c_str()); } // Instantiate the templates we need. @@ -700,21 +3084,45 @@ Incremental_inputs::sized_create_inputs_section_data() #ifdef HAVE_TARGET_32_LITTLE template class Sized_incremental_binary<32, false>; + +template +class Sized_relobj_incr<32, false>; + +template +class Sized_incr_dynobj<32, false>; #endif #ifdef HAVE_TARGET_32_BIG template class Sized_incremental_binary<32, true>; + +template +class Sized_relobj_incr<32, true>; + +template +class Sized_incr_dynobj<32, true>; #endif #ifdef HAVE_TARGET_64_LITTLE template class Sized_incremental_binary<64, false>; + +template +class Sized_relobj_incr<64, false>; + +template +class Sized_incr_dynobj<64, false>; #endif #ifdef HAVE_TARGET_64_BIG template class Sized_incremental_binary<64, true>; + +template +class Sized_relobj_incr<64, true>; + +template +class Sized_incr_dynobj<64, true>; #endif } // End namespace gold.