diff options
Diffstat (limited to 'binutils-2.20.1/gold/incremental.cc')
-rw-r--r-- | binutils-2.20.1/gold/incremental.cc | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/binutils-2.20.1/gold/incremental.cc b/binutils-2.20.1/gold/incremental.cc new file mode 100644 index 00000000..bf028349 --- /dev/null +++ b/binutils-2.20.1/gold/incremental.cc @@ -0,0 +1,544 @@ +// inremental.cc -- incremental linking support for gold + +// Copyright 2009 Free Software Foundation, Inc. +// Written by Mikolaj Zalewski <mikolajz@google.com>. + +// This file is part of gold. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +// MA 02110-1301, USA. + +#include "gold.h" + +#include <cstdarg> + +#include "elfcpp.h" +#include "output.h" +#include "incremental.h" +#include "archive.h" +#include "output.h" +#include "target-select.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; + +// Inform the user why we don't do an incremental link. Not called in +// the obvious case of missing output file. TODO: Is this helpful? + +void +vexplain_no_incremental(const char* format, va_list args) +{ + char* buf = NULL; + if (vasprintf(&buf, format, args) < 0) + gold_nomem(); + gold_info(_("the link might take longer: " + "cannot perform incremental link: %s"), buf); + free(buf); +} + +void +explain_no_incremental(const char* format, ...) +{ + va_list args; + va_start(args, format); + vexplain_no_incremental(format, args); + va_end(args); +} + +// Report an error. + +void +Incremental_binary::error(const char* format, ...) const +{ + va_list args; + va_start(args, format); + // Current code only checks if the file can be used for incremental linking, + // so errors shouldn't fail the build, but only result in a fallback to a + // full build. + // TODO: when we implement incremental editing of the file, we may need a + // flag that will cause errors to be treated seriously. + vexplain_no_incremental(format, args); + va_end(args); +} + +template<int size, bool big_endian> +bool +Sized_incremental_binary<size, big_endian>::do_find_incremental_inputs_section( + Location* location, + unsigned int* strtab_shndx) +{ + unsigned int shndx = this->elf_file_.find_section_by_type( + elfcpp::SHT_GNU_INCREMENTAL_INPUTS); + if (shndx == elfcpp::SHN_UNDEF) // Not found. + return false; + *strtab_shndx = this->elf_file_.section_link(shndx); + *location = this->elf_file_.section_contents(shndx); + return true; +} + +template<int size, bool big_endian> +bool +Sized_incremental_binary<size, big_endian>::do_check_inputs( + Incremental_inputs* incremental_inputs) +{ + const int entry_size = + Incremental_inputs_entry_write<size, big_endian>::data_size; + const int header_size = + Incremental_inputs_header_write<size, big_endian>::data_size; + + unsigned int strtab_shndx; + Location location; + + if (!do_find_incremental_inputs_section(&location, &strtab_shndx)) + { + explain_no_incremental(_("no incremental data from previous build")); + return false; + } + if (location.data_size < header_size + || strtab_shndx >= this->elf_file_.shnum() + || this->elf_file_.section_type(strtab_shndx) != elfcpp::SHT_STRTAB) + { + explain_no_incremental(_("invalid incremental build data")); + return false; + } + + Location 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<size, big_endian> header(data_view.data()); + + if (header.get_version() != INCREMENTAL_LINK_VERSION) + { + explain_no_incremental(_("different version of incremental build data")); + 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)) + { + explain_no_incremental(_("invalid incremental build data")); + return false; + } + + if (incremental_inputs->command_line() != command_line) + { + explain_no_incremental(_("command line changed")); + return false; + } + + // TODO: compare incremental_inputs->inputs() with entries in data_view. + return true; +} + +namespace +{ + +// Create a Sized_incremental_binary object of the specified size and +// endianness. Fails if the target architecture is not supported. + +template<int size, bool big_endian> +Incremental_binary* +make_sized_incremental_binary(Output_file* file, + const elfcpp::Ehdr<size, big_endian>& 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]); + if (target == NULL) + { + explain_no_incremental(_("unsupported ELF machine number %d"), + 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<size, big_endian>(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 +// should be opened. + +Incremental_binary* +open_incremental_binary(Output_file* file) +{ + off_t filesize = file->filesize(); + int want = elfcpp::Elf_recognizer::max_header_size; + if (filesize < want) + want = filesize; + + const unsigned char* p = file->get_input_view(0, want); + if (!elfcpp::Elf_recognizer::is_elf_file(p, want)) + { + explain_no_incremental(_("output is not an ELF file.")); + return NULL; + } + + int size = 0; + bool big_endian = false; + std::string error; + if (!elfcpp::Elf_recognizer::is_valid_header(p, want, &size, &big_endian, + &error)) + { + explain_no_incremental(error.c_str()); + return NULL; + } + + Incremental_binary* result = NULL; + if (size == 32) + { + if (big_endian) + { +#ifdef HAVE_TARGET_32_BIG + result = make_sized_incremental_binary<32, true>( + file, elfcpp::Ehdr<32, true>(p)); +#else + 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)); +#else + 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)); +#else + 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)); +#else + explain_no_incremental(_("unsupported file: 64-bit, little-endian")); +#endif + } + } + else + gold_unreachable(); + + 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_); +} + +// Add the command line to the string table, setting +// command_line_key_. In incremental builds, the command line is +// stored in .gnu_incremental_inputs so that the next linker run can +// check if the command line options didn't change. + +void +Incremental_inputs::report_command_line(int argc, const char* const* argv) +{ + // Always store 'gold' as argv[0] to avoid a full relink if the user used a + // different path to the linker. + std::string args("gold"); + // 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 + || strcmp(argv[i], "--incremental-unchanged") == 0 + || strcmp(argv[i], "--incremental-unknown") == 0) + 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; + } + args.append("'"); + } + + this->command_line_ = args; + this->strtab_->add(this->command_line_.c_str(), false, + &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. + +void +Incremental_inputs::report_archive(const Input_argument* input, + Archive* archive) +{ + Hold_lock hl(*this->lock_); + + 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)); +} + +// Record that the input argument INPUT is an object OBJ. This is +// called by Read_symbols after finding out the type of the file. + +void +Incremental_inputs::report_object(const Input_argument* input, + Object* obj) +{ + Hold_lock hl(*this->lock_); + + Input_info info; + info.type = (obj->is_dynamic() + ? INCREMENTAL_INPUT_SHARED_LIBRARY + : INCREMENTAL_INPUT_OBJECT); + info.object = obj; + info.mtime = obj->input_file()->file().get_mtime(); + this->inputs_map_.insert(std::make_pair(input, info)); +} + +// 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. + +void +Incremental_inputs::report_script(const Input_argument* input, + Timespec mtime, + Script_info* script) +{ + Hold_lock hl(*this->lock_); + + Input_info info; + info.type = INCREMENTAL_INPUT_SCRIPT; + info.script = script; + info.mtime = mtime; + this->inputs_map_.insert(std::make_pair(input, info)); +} + +// 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). + +void +Incremental_inputs::finalize_inputs( + Input_argument_list::const_iterator begin, + Input_argument_list::const_iterator end, + unsigned int* index) +{ + 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); + } + } +} + +// Finalize the incremental link information. Called from +// Layout::finalize. + +void +Incremental_inputs::finalize() +{ + unsigned int index = 0; + finalize_inputs(this->inputs_->begin(), this->inputs_->end(), &index); + + // Sanity check. + for (Inputs_info_map::const_iterator p = this->inputs_map_.begin(); + p != this->inputs_map_.end(); + ++p) + { + gold_assert(p->second.filename_key != 0); + } + + this->strtab_->set_string_offsets(); +} + +// Create the content of the .gnu_incremental_inputs section. + +Output_section_data* +Incremental_inputs::create_incremental_inputs_section_data() +{ + switch (parameters->size_and_endianness()) + { +#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(); + } +} + +// Sized creation of .gnu_incremental_inputs section. + +template<int size, bool big_endian> +Output_section_data* +Incremental_inputs::sized_create_inputs_section_data() +{ + const int entry_size = + Incremental_inputs_entry_write<size, big_endian>::data_size; + const int header_size = + Incremental_inputs_header_write<size, big_endian>::data_size; + + unsigned int sz = header_size + entry_size * this->inputs_map_.size(); + unsigned char* buffer = new unsigned char[sz]; + unsigned char* inputs_base = buffer + header_size; + + Incremental_inputs_header_write<size, big_endian> header_writer(buffer); + gold_assert(this->command_line_key_ > 0); + int cmd_offset = this->strtab_->get_offset_from_key(this->command_line_key_); + + header_writer.put_version(INCREMENTAL_LINK_VERSION); + header_writer.put_input_file_count(this->inputs_map_.size()); + header_writer.put_command_line_offset(cmd_offset); + header_writer.put_reserved(0); + + for (Inputs_info_map::const_iterator it = this->inputs_map_.begin(); + it != this->inputs_map_.end(); + ++it) + { + gold_assert(it->second.index < this->inputs_map_.size()); + + unsigned char* entry_buffer = + inputs_base + it->second.index * entry_size; + Incremental_inputs_entry_write<size, big_endian> entry(entry_buffer); + int filename_offset = + this->strtab_->get_offset_from_key(it->second.filename_key); + entry.put_filename_offset(filename_offset); + switch (it->second.type) + { + case INCREMENTAL_INPUT_SCRIPT: + entry.put_data_offset(0); + break; + case INCREMENTAL_INPUT_ARCHIVE: + case INCREMENTAL_INPUT_OBJECT: + case INCREMENTAL_INPUT_SHARED_LIBRARY: + // TODO: add per input data. Currently we store + // an out-of-bounds offset for future version of gold to reject + // such an incremental_inputs section. + entry.put_data_offset(0xffffffff); + break; + default: + gold_unreachable(); + } + entry.put_timestamp_sec(it->second.mtime.seconds); + entry.put_timestamp_nsec(it->second.mtime.nanoseconds); + entry.put_input_type(it->second.type); + entry.put_reserved(0); + } + + return new Output_data_const_buffer(buffer, sz, 8, + "** incremental link inputs list"); +} + +// Instantiate the templates we need. + +#ifdef HAVE_TARGET_32_LITTLE +template +class Sized_incremental_binary<32, false>; +#endif + +#ifdef HAVE_TARGET_32_BIG +template +class Sized_incremental_binary<32, true>; +#endif + +#ifdef HAVE_TARGET_64_LITTLE +template +class Sized_incremental_binary<64, false>; +#endif + +#ifdef HAVE_TARGET_64_BIG +template +class Sized_incremental_binary<64, true>; +#endif + +} // End namespace gold. |