| // inremental.h -- incremental linking support for gold -*- C++ -*- |
| |
| // 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. |
| |
| #ifndef GOLD_INCREMENTAL_H |
| #define GOLD_INCREMENTAL_H |
| |
| #include <map> |
| #include <vector> |
| |
| #include "elfcpp_file.h" |
| #include "stringpool.h" |
| #include "workqueue.h" |
| #include "fileread.h" |
| #include "output.h" |
| |
| namespace gold |
| { |
| |
| class Archive; |
| class Input_argument; |
| class Incremental_inputs_checker; |
| class Object; |
| class Output_section_data; |
| |
| // Incremental input type as stored in .gnu_incremental_inputs. |
| |
| enum Incremental_input_type |
| { |
| INCREMENTAL_INPUT_INVALID = 0, |
| INCREMENTAL_INPUT_OBJECT = 1, |
| INCREMENTAL_INPUT_ARCHIVE = 2, |
| INCREMENTAL_INPUT_SHARED_LIBRARY = 3, |
| INCREMENTAL_INPUT_SCRIPT = 4 |
| }; |
| |
| // Header of the .gnu_incremental_input section. |
| struct Incremental_inputs_header_data |
| { |
| // Incremental linker version. |
| elfcpp::Elf_Word version; |
| |
| // Numer of input files in the link. |
| elfcpp::Elf_Word input_file_count; |
| |
| // Offset of command line options in .gnu_incremental_strtab. |
| elfcpp::Elf_Word command_line_offset; |
| |
| // Padding. |
| elfcpp::Elf_Word reserved; |
| }; |
| |
| // Reader class for .gnu_incremental_inputs header. See |
| // Incremental_inputs_header_data for fields descriptions. |
| |
| template<int size, bool big_endian> |
| class Incremental_inputs_header |
| { |
| private: |
| typedef Incremental_inputs_header_data Data_type; |
| typedef elfcpp::Convert<32, big_endian> Convert32; |
| |
| public: |
| Incremental_inputs_header(const unsigned char *p) |
| : p_(reinterpret_cast<const Data_type*>(p)) |
| { } |
| |
| static const int data_size = sizeof(Data_type); |
| |
| elfcpp::Elf_Word |
| get_version() const |
| { return Convert32::convert_host(this->p_->version); } |
| |
| elfcpp::Elf_Word |
| get_input_file_count() const |
| { return Convert32::convert_host(this->p_->input_file_count); } |
| |
| elfcpp::Elf_Word |
| get_command_line_offset() const |
| { return Convert32::convert_host(this->p_->command_line_offset); } |
| |
| elfcpp::Elf_Word |
| get_reserved() const |
| { return Convert32::convert_host(this->p_->reserved); } |
| |
| private: |
| const Data_type* p_; |
| }; |
| |
| // Writer class for .gnu_incremental_inputs header. See |
| // Incremental_inputs_header_data for fields descriptions. |
| |
| template<int size, bool big_endian> |
| class Incremental_inputs_header_write |
| { |
| private: |
| typedef Incremental_inputs_header_data Data_type; |
| typedef elfcpp::Convert<32, big_endian> Convert32; |
| |
| public: |
| Incremental_inputs_header_write(unsigned char *p) |
| : p_(reinterpret_cast<Data_type*>(p)) |
| { } |
| |
| static const int data_size = sizeof(Data_type); |
| |
| void |
| put_version(elfcpp::Elf_Word v) |
| { this->p_->version = Convert32::convert_host(v); } |
| |
| void |
| put_input_file_count(elfcpp::Elf_Word v) |
| { this->p_->input_file_count = Convert32::convert_host(v); } |
| |
| void |
| put_command_line_offset(elfcpp::Elf_Word v) |
| { this->p_->command_line_offset = Convert32::convert_host(v); } |
| |
| void |
| put_reserved(elfcpp::Elf_Word v) |
| { this->p_->reserved = Convert32::convert_host(v); } |
| |
| private: |
| Data_type* p_; |
| }; |
| |
| // Data stored in .gnu_incremental_input after the header for each of the |
| // Incremental_input_header_data::input_file_count input entries. |
| struct Incremental_inputs_entry_data |
| { |
| // Offset of file name in .gnu_incremental_strtab section. |
| elfcpp::Elf_Word filename_offset; |
| |
| // Offset of data in .gnu_incremental_input. |
| elfcpp::Elf_Word data_offset; |
| |
| // Timestamp (in seconds). |
| elfcpp::Elf_Xword timestamp_sec; |
| |
| // Nano-second part of timestamp (if supported). |
| elfcpp::Elf_Word timestamp_nsec; |
| |
| // Type of the input entry. |
| elfcpp::Elf_Half input_type; |
| |
| // Padding. |
| elfcpp::Elf_Half reserved; |
| }; |
| |
| // Reader class for an .gnu_incremental_inputs entry. See |
| // Incremental_inputs_entry_data for fields descriptions. |
| template<int size, bool big_endian> |
| class Incremental_inputs_entry |
| { |
| private: |
| typedef Incremental_inputs_entry_data Data_type; |
| typedef elfcpp::Convert<32, big_endian> Convert32; |
| typedef elfcpp::Convert<64, big_endian> Convert64; |
| |
| public: |
| Incremental_inputs_entry(const unsigned char *p) |
| : p_(reinterpret_cast<const Data_type*>(p)) |
| { } |
| |
| static const int data_size = sizeof(Data_type); |
| |
| elfcpp::Elf_Word |
| get_filename_offset() |
| { return Convert32::convert_host(this->p_->filename_offset); } |
| |
| elfcpp::Elf_Word |
| get_data_offset() |
| { return Convert32::convert_host(this->p_->data_offset); } |
| |
| elfcpp::Elf_Xword |
| get_timestamp_sec() |
| { return Convert64::convert_host(this->p_->timestamp_sec); } |
| |
| elfcpp::Elf_Word |
| get_timestamp_nsec() |
| { return Convert32::convert_host(this->p_->timestamp_nsec); } |
| |
| elfcpp::Elf_Word |
| get_input_type() |
| { return Convert32::convert_host(this->p_->input_type); } |
| |
| elfcpp::Elf_Word |
| get_reserved() |
| { return Convert32::convert_host(this->p_->reserved); } |
| |
| private: |
| const Data_type* p_; |
| }; |
| |
| // Writer class for an .gnu_incremental_inputs entry. See |
| // Incremental_inputs_entry_data for fields descriptions. |
| template<int size, bool big_endian> |
| class Incremental_inputs_entry_write |
| { |
| private: |
| typedef Incremental_inputs_entry_data Data_type; |
| typedef elfcpp::Convert<32, big_endian> Convert32; |
| typedef elfcpp::Convert<64, big_endian> Convert64; |
| |
| public: |
| Incremental_inputs_entry_write(unsigned char *p) |
| : p_(reinterpret_cast<Data_type*>(p)) |
| { } |
| |
| static const int data_size = sizeof(Data_type); |
| |
| void |
| put_filename_offset(elfcpp::Elf_Word v) |
| { this->p_->filename_offset = Convert32::convert_host(v); } |
| |
| void |
| put_data_offset(elfcpp::Elf_Word v) |
| { this->p_->data_offset = Convert32::convert_host(v); } |
| |
| void |
| put_timestamp_sec(elfcpp::Elf_Xword v) |
| { this->p_->timestamp_sec = Convert64::convert_host(v); } |
| |
| void |
| put_timestamp_nsec(elfcpp::Elf_Word v) |
| { this->p_->timestamp_nsec = Convert32::convert_host(v); } |
| |
| void |
| put_input_type(elfcpp::Elf_Word v) |
| { this->p_->input_type = Convert32::convert_host(v); } |
| |
| void |
| put_reserved(elfcpp::Elf_Word v) |
| { this->p_->reserved = Convert32::convert_host(v); } |
| |
| private: |
| Data_type* p_; |
| }; |
| |
| // An object representing the ELF file we edit during an incremental build. |
| // Similar to Object or Dynobj, but operates on Output_file and contains |
| // method specific to file edition (TBD). This is the abstract parent class |
| // implemented in Sized_incremental_binary<size, big_endian> for a specific |
| // endianness and size. |
| |
| class Incremental_binary |
| { |
| public: |
| Incremental_binary(Output_file* output, Target* target) |
| : output_(output), target_(target) |
| { } |
| |
| virtual |
| ~Incremental_binary() |
| { } |
| |
| // Functions and types for the elfcpp::Elf_file interface. This |
| // permit us to use Incremental_binary as the File template parameter for |
| // elfcpp::Elf_file. |
| |
| // The View class is returned by view. It must support a single |
| // method, data(). This is trivial, because Output_file::get_output_view |
| // does what we need. |
| class View |
| { |
| public: |
| View(const unsigned char* p) |
| : p_(p) |
| { } |
| |
| const unsigned char* |
| data() const |
| { return this->p_; } |
| |
| private: |
| const unsigned char* p_; |
| }; |
| |
| // Return a View. |
| View |
| view(off_t file_offset, section_size_type data_size) |
| { return View(this->output_->get_input_view(file_offset, data_size)); } |
| |
| // A location in the file. |
| struct Location |
| { |
| off_t file_offset; |
| off_t data_size; |
| |
| Location(off_t fo, section_size_type ds) |
| : file_offset(fo), data_size(ds) |
| { } |
| |
| Location() |
| : file_offset(0), data_size(0) |
| { } |
| }; |
| |
| // Get a View given a Location. |
| View view(Location loc) |
| { return View(this->view(loc.file_offset, loc.data_size)); } |
| |
| // Report an error. |
| void |
| error(const char* format, ...) const ATTRIBUTE_PRINTF_2; |
| |
| // Find the .gnu_incremental_inputs section. It selects the first section |
| // of type SHT_GNU_INCREMENTAL_INPUTS. Returns false if such a section |
| // is not found. |
| bool |
| find_incremental_inputs_section(Location* location, |
| unsigned int* strtab_shndx) |
| { return do_find_incremental_inputs_section(location, strtab_shndx); } |
| |
| // Check the .gnu_incremental_inputs section to see whether an incremental |
| // build is possible. |
| // TODO: on success, should report what files needs to be rebuilt. |
| // INCREMENTAL_INPUTS is used to read the canonical form of the command line |
| // and read the input arguments. TODO: for items that don't need to be |
| // rebuilt, we should also copy the incremental input information. |
| virtual bool |
| check_inputs(Incremental_inputs* incremental_inputs) |
| { return do_check_inputs(incremental_inputs); } |
| |
| protected: |
| // Find incremental inputs section. |
| virtual bool |
| do_find_incremental_inputs_section(Location* location, |
| unsigned int* strtab_shndx) = 0; |
| |
| // Check the .gnu_incremental_inputs section to see whether an incremental |
| // build is possible. |
| virtual bool |
| do_check_inputs(Incremental_inputs* incremental_inputs) = 0; |
| |
| private: |
| // Edited output file object. |
| Output_file* output_; |
| // Target of the output file. |
| Target* target_; |
| }; |
| |
| template<int size, bool big_endian> |
| class Sized_incremental_binary : public Incremental_binary |
| { |
| public: |
| Sized_incremental_binary(Output_file* output, |
| const elfcpp::Ehdr<size, big_endian>& ehdr, |
| Target* target) |
| : Incremental_binary(output, target), elf_file_(this, ehdr) |
| { } |
| |
| protected: |
| virtual bool |
| do_find_incremental_inputs_section(Location* location, |
| unsigned int* strtab_shndx); |
| |
| virtual bool |
| do_check_inputs(Incremental_inputs* incremental_inputs); |
| |
| private: |
| // Output as an ELF file. |
| elfcpp::Elf_file<size, big_endian, Incremental_binary> elf_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. |
| Incremental_binary* |
| open_incremental_binary(Output_file* file); |
| |
| // Code invoked early during an incremental link that checks what files need |
| // to be relinked. |
| class Incremental_checker |
| { |
| public: |
| // Check if the file named OUTPUT_NAME can be linked incrementally. |
| // INCREMENTAL_INPUTS must have the canonical form of the command line |
| // and input arguments filled - at this point of linking other fields are |
| // probably not filled yet. TODO: for inputs that don't need to be |
| // rebuilt, this function should fill the incremental input information. |
| Incremental_checker(const char* output_name, |
| Incremental_inputs* incremental_inputs) |
| : output_name_(output_name), incremental_inputs_(incremental_inputs) |
| { } |
| |
| // Analyzes the output file to check if incremental linking is possible and |
| // what files needs to be relinked. |
| bool |
| can_incrementally_link_output_file(); |
| |
| private: |
| // Name of the output file to analyze. |
| const char* output_name_; |
| |
| // The Incremental_inputs object. At this stage of link, only the command |
| // line and inputs are filled. |
| Incremental_inputs* incremental_inputs_; |
| }; |
| |
| // This class contains the information needed during an incremental |
| // build about the inputs necessary to build the .gnu_incremental_inputs. |
| class Incremental_inputs |
| { |
| public: |
| Incremental_inputs() |
| : lock_(new Lock()), inputs_(NULL), command_line_key_(0), |
| strtab_(new Stringpool()) |
| { } |
| ~Incremental_inputs() { delete this->strtab_; } |
| |
| // Record the command line. |
| void |
| report_command_line(int argc, const char* const* argv); |
| |
| // Record the input arguments obtained from parsing the command line. |
| void |
| report_inputs(const Input_arguments& inputs) |
| { this->inputs_ = &inputs; } |
| |
| // Record that the input argument INPUT is an archive ARCHIVE. |
| void |
| report_archive(const Input_argument* input, Archive* archive); |
| |
| // Record that the input argument INPUT is to an object OBJ. |
| void |
| report_object(const Input_argument* input, Object* obj); |
| |
| // Record that the input argument INPUT is to an script SCRIPT. |
| void |
| report_script(const Input_argument* input, Timespec mtime, |
| Script_info* script); |
| |
| // Prepare for layout. Called from Layout::finalize. |
| void |
| finalize(); |
| |
| // Create the content of the .gnu_incremental_inputs section. |
| Output_section_data* |
| create_incremental_inputs_section_data(); |
| |
| // Return the .gnu_incremental_strtab stringpool. |
| Stringpool* |
| get_stringpool() |
| { return this->strtab_; } |
| |
| // Return the canonical form of the command line, as will be stored in |
| // .gnu_incremental_strtab. |
| const std::string& |
| command_line() |
| { return this->command_line_; } |
| |
| // Return the input files found in the command line. |
| const Input_arguments* |
| inputs() |
| { return this->inputs_; } |
| |
| private: |
| // Code for each of the four possible variants of create_inputs_section_data. |
| template<int size, bool big_endian> |
| Output_section_data* |
| sized_create_inputs_section_data(); |
| |
| // Compute indexes in the order in which the inputs should appear in |
| // .gnu_incremental_inputs and put file names to the stringtable. |
| // This needs to be done after all the scripts are parsed. |
| |
| void |
| finalize_inputs(Input_argument_list::const_iterator begin, |
| Input_argument_list::const_iterator end, |
| unsigned int* index); |
| |
| // Additional data about an input needed for an incremental link. |
| // None of these pointers is owned by the structure. |
| struct Input_info |
| { |
| Input_info() |
| : type(INCREMENTAL_INPUT_INVALID), archive(NULL), filename_key(0), |
| index(0) |
| { } |
| |
| // Type of the file pointed by this argument. |
| Incremental_input_type type; |
| |
| union |
| { |
| // Present if type == INCREMENTAL_INPUT_ARCHIVE. |
| Archive* archive; |
| |
| // Present if type == INCREMENTAL_INPUT_OBJECT or |
| // INCREMENTAL_INPUT_SHARED_LIBRARY. |
| Object* object; |
| |
| // Present if type == INCREMENTAL_INPUT_SCRIPT. |
| Script_info* script; |
| }; |
| |
| // Key of the filename string in the section stringtable. |
| Stringpool::Key filename_key; |
| |
| // Position of the entry information in the output section. |
| unsigned int index; |
| |
| // Last modification time of the file. |
| Timespec mtime; |
| }; |
| |
| typedef std::map<const Input_argument*, Input_info> Inputs_info_map; |
| |
| // A lock guarding access to inputs_ during the first phase of linking, when |
| // report_ function may be called from multiple threads. |
| Lock* lock_; |
| |
| // The list of input arguments obtained from parsing the command line. |
| const Input_arguments* inputs_; |
| |
| // A map containing additional information about the input elements. |
| Inputs_info_map inputs_map_; |
| |
| // Canonical form of the command line, as will be stored in |
| // .gnu_incremental_strtab. |
| std::string command_line_; |
| |
| // The key of the command line string in the string pool. |
| Stringpool::Key command_line_key_; |
| |
| // The .gnu_incremental_strtab string pool associated with the |
| // .gnu_incremental_inputs. |
| Stringpool* strtab_; |
| }; |
| |
| } // End namespace gold. |
| |
| #endif // !defined(GOLD_INCREMENTAL_H) |