diff options
-rw-r--r-- | elfcpp/ChangeLog | 6 | ||||
-rw-r--r-- | elfcpp/elfcpp_file.h | 165 | ||||
-rw-r--r-- | gold/ChangeLog | 21 | ||||
-rw-r--r-- | gold/incremental.cc | 192 | ||||
-rw-r--r-- | gold/incremental.h | 113 | ||||
-rw-r--r-- | gold/object.cc | 74 | ||||
-rw-r--r-- | gold/output.h | 5 |
7 files changed, 513 insertions, 63 deletions
diff --git a/elfcpp/ChangeLog b/elfcpp/ChangeLog index 2a668a6..7569cbd 100644 --- a/elfcpp/ChangeLog +++ b/elfcpp/ChangeLog @@ -1,3 +1,9 @@ +2009-10-09 Mikolaj Zalewski <mikolajz@google.com> + + * elfcpp_file.h: Fix header guard. Include <cstdio>. + (class Elf_recognizer): New class, code from gold/object.cc. + (Elf_file::find_section_by_type): New method. + 2009-07-23 Ulrich Drepper <drepper@redhat.com> * elfcpp.h (enum STB): Add STB_GNU_UNIQUE. diff --git a/elfcpp/elfcpp_file.h b/elfcpp/elfcpp_file.h index f1f4423..00ab28b 100644 --- a/elfcpp/elfcpp_file.h +++ b/elfcpp/elfcpp_file.h @@ -53,15 +53,45 @@ // This permits writing // elfcpp::Shdr shdr(file, ef.section_header(n)); -#ifndef ELFPCP_FILE_H +#ifndef ELFCPP_FILE_H #define ELFCPP_FILE_H #include <string> +#include <cstdio> #include <cstring> namespace elfcpp { +// A simple helper class to recognize if a file has an ELF header. + +class Elf_recognizer +{ + public: + // Maximum header size. The user should try to read this much of + // the file when using this class. + + static const int max_header_size = Elf_sizes<64>::ehdr_size; + + // Checks if the file contains the ELF magic. Other header fields + // are not checked. + + static bool + is_elf_file(const unsigned char* ehdr_buf, int size); + + // Check if EHDR_BUF/BUFSIZE is a valid header of a 32-bit or + // 64-bit, little-endian or big-endian ELF file. Assumes + // is_elf_file() has been checked to be true. If the header is not + // valid, *ERROR contains a human-readable error message. If is is, + // *SIZE is set to either 32 or 64, *BIG_ENDIAN is set to indicate + // whether the file is big-endian. + + static bool + is_valid_header(const unsigned char* ehdr_buf, off_t bufsize, + int* size, bool* big_endian, + std::string* error); +}; + // This object is used to read an ELF file. // SIZE: The size of file, 32 or 64. // BIG_ENDIAN: Whether the file is in big-endian format. @@ -99,6 +129,11 @@ class Elf_file shoff() const { return this->shoff_; } + // Find the first section with an sh_type field equal to TYPE and + // return its index. Returns SHN_UNDEF if there is no such section. + unsigned int + find_section_by_type(unsigned int type); + // Return the number of sections. unsigned int shnum() @@ -193,6 +228,115 @@ class Elf_file int large_shndx_offset_; }; +// Inline function definitions. + +// Check for presence of the ELF magic number. + +inline bool +Elf_recognizer::is_elf_file(const unsigned char* ehdr_buf, int size) +{ + if (size < 4) + return false; + + static unsigned char elfmagic[4] = + { + elfcpp::ELFMAG0, elfcpp::ELFMAG1, + elfcpp::ELFMAG2, elfcpp::ELFMAG3 + }; + return memcmp(ehdr_buf, elfmagic, 4) == 0; +} + +namespace +{ + +// Print a number to a string. + +inline std::string +internal_printf_int(const char* format, int arg) +{ + char buf[256]; + snprintf(buf, sizeof(buf), format, arg); + return std::string(buf); +} + +} // End anonymous namespace. + +// Check the validity of the ELF header. + +inline bool +Elf_recognizer::is_valid_header( + const unsigned char* ehdr_buf, + off_t bufsize, + int* size, + bool* big_endian, + std::string* error) +{ + if (bufsize < elfcpp::EI_NIDENT) + { + *error = _("ELF file too short"); + return false; + } + + int v = ehdr_buf[elfcpp::EI_VERSION]; + if (v != elfcpp::EV_CURRENT) + { + if (v == elfcpp::EV_NONE) + *error = _("invalid ELF version 0"); + else + *error = internal_printf_int(_("unsupported ELF version %d"), v); + return false; + } + + int c = ehdr_buf[elfcpp::EI_CLASS]; + if (c == elfcpp::ELFCLASSNONE) + { + *error = _("invalid ELF class 0"); + return false; + } + else if (c != elfcpp::ELFCLASS32 + && c != elfcpp::ELFCLASS64) + { + *error = internal_printf_int(_("unsupported ELF class %d"), c); + return false; + } + + int d = ehdr_buf[elfcpp::EI_DATA]; + if (d == elfcpp::ELFDATANONE) + { + *error = _("invalid ELF data encoding"); + return false; + } + else if (d != elfcpp::ELFDATA2LSB + && d != elfcpp::ELFDATA2MSB) + { + *error = internal_printf_int(_("unsupported ELF data encoding %d"), d); + return false; + } + + *big_endian = (d == elfcpp::ELFDATA2MSB); + + if (c == elfcpp::ELFCLASS32) + { + if (bufsize < elfcpp::Elf_sizes<32>::ehdr_size) + { + *error = _("ELF file too short"); + return false; + } + *size = 32; + } + else + { + if (bufsize < elfcpp::Elf_sizes<64>::ehdr_size) + { + *error = _("ELF file too short"); + return false; + } + *size = 64; + } + + return true; +} + // Template function definitions. // Construct an Elf_file given an ELF file header. @@ -269,6 +413,25 @@ Elf_file<size, big_endian, File>::initialize_shnum() } } +// Find section with sh_type equal to TYPE and return its index. +// Returns SHN_UNDEF if not found. + +template<int size, bool big_endian, typename File> +unsigned int +Elf_file<size, big_endian, File>::find_section_by_type(unsigned int type) +{ + unsigned int shnum = this->shnum(); + typename File::View v(this->file_->view(this->shoff_, + This::shdr_size * shnum)); + for (unsigned int i = 0; i < shnum; i++) + { + Ef_shdr shdr(v.data() + This::shdr_size * i); + if (shdr.get_sh_type() == type) + return i; + } + return SHN_UNDEF; +} + // Return the file offset of the section header of section SHNDX. template<int size, bool big_endian, typename File> diff --git a/gold/ChangeLog b/gold/ChangeLog index 4a2e831..227ded7 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,24 @@ +2009-10-09 Mikolaj Zalewski <mikolajz@google.com> + + * incremental.cc: Include <cstdarg> and "target-select.h". + (vexplain_no_incremental): New function. + (explain_no_incremental): New function. + (Incremental_binary::error): New method. + (Sized_incremental_binary::do_find_incremental_inputs_section): New + method. + (make_sized_incremental_binary): New function. + (open_incremental_binary): New function. + (can_incrementally_link_file): Add checks if output is ELF and has + inputs section. + * incremental.h: Include "elfcpp_file.h" and "output.h". + (Incremental_binary): New class. + (Sized_incremental_binary): New class. + (open_incremental_binary): Declare. + * object.cc (is_elf_object): Use + elfcpp::Elf_recognizer::is_elf_file. + (make_elf_object): Use elfcpp::Elf_recognizer::is_valid_header. + * output.h (Output_file::filesize): New method. + 2009-10-07 Viktor Kutuzov <vkutuzov@accesssoftek.com> * arm.cc (Arm_relocate_functions::extract_arm_movw_movt_addend): diff --git a/gold/incremental.cc b/gold/incremental.cc index 4a3ecb1..9f7c4c2 100644 --- a/gold/incremental.cc +++ b/gold/incremental.cc @@ -21,11 +21,15 @@ // 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" using elfcpp::Convert; @@ -150,6 +154,162 @@ class Incremental_inputs_entry_write internal::Incremental_inputs_entry_data* p_; }; +// 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 shndx = this->elf_file_.find_section_by_type( + elfcpp::SHT_GNU_INCREMENTAL_INPUTS); + if (shndx == elfcpp::SHN_UNDEF) // Not found. + return false; + *location = this->elf_file_.section_contents(shndx); + 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; + } + + 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; + bool big_endian; + 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. @@ -159,6 +319,16 @@ 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; + Incremental_binary::Location inputs_location; + if (!binary->find_incremental_inputs_section(&inputs_location)) + { + explain_no_incremental("no incremental data from previous build"); + delete binary; + return false; + } return true; } @@ -392,4 +562,26 @@ Incremental_inputs::sized_create_inputs_section_data() "** 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. diff --git a/gold/incremental.h b/gold/incremental.h index 9650993..8909324 100644 --- a/gold/incremental.h +++ b/gold/incremental.h @@ -26,9 +26,11 @@ #include <map> #include <vector> +#include "elfcpp_file.h" #include "stringpool.h" #include "workqueue.h" #include "fileread.h" +#include "output.h" namespace gold { @@ -50,6 +52,117 @@ enum Incremental_input_type INCREMENTAL_INPUT_SCRIPT = 4 }; +// 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) + { return do_find_incremental_inputs_section(location); } + + protected: + // Find incremental inputs section. + virtual bool + do_find_incremental_inputs_section(Location* location) = 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); + + 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 diff --git a/gold/object.cc b/gold/object.cc index 5ac7878..b8d9eb8 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -2281,7 +2281,7 @@ is_elf_object(Input_file* input_file, off_t offset, const unsigned char** start, int *read_size) { off_t filesize = input_file->file().filesize(); - int want = elfcpp::Elf_sizes<64>::ehdr_size; + int want = elfcpp::Elf_recognizer::max_header_size; if (filesize - offset < want) want = filesize - offset; @@ -2290,15 +2290,7 @@ is_elf_object(Input_file* input_file, off_t offset, *start = p; *read_size = want; - if (want < 4) - return false; - - static unsigned char elfmagic[4] = - { - elfcpp::ELFMAG0, elfcpp::ELFMAG1, - elfcpp::ELFMAG2, elfcpp::ELFMAG3 - }; - return memcmp(p, elfmagic, 4) == 0; + return elfcpp::Elf_recognizer::is_elf_file(p, want); } // Read an ELF file and return the appropriate instance of Object. @@ -2311,57 +2303,18 @@ make_elf_object(const std::string& name, Input_file* input_file, off_t offset, if (punconfigured != NULL) *punconfigured = false; - if (bytes < elfcpp::EI_NIDENT) - { - gold_error(_("%s: ELF file too short"), name.c_str()); - return NULL; - } - - int v = p[elfcpp::EI_VERSION]; - if (v != elfcpp::EV_CURRENT) - { - if (v == elfcpp::EV_NONE) - gold_error(_("%s: invalid ELF version 0"), name.c_str()); - else - gold_error(_("%s: unsupported ELF version %d"), name.c_str(), v); - return NULL; - } - - int c = p[elfcpp::EI_CLASS]; - if (c == elfcpp::ELFCLASSNONE) + std::string error; + bool big_endian; + int size; + if (!elfcpp::Elf_recognizer::is_valid_header(p, bytes, &size, + &big_endian, &error)) { - gold_error(_("%s: invalid ELF class 0"), name.c_str()); - return NULL; - } - else if (c != elfcpp::ELFCLASS32 - && c != elfcpp::ELFCLASS64) - { - gold_error(_("%s: unsupported ELF class %d"), name.c_str(), c); + gold_error(_("%s: %s"), name.c_str(), error.c_str()); return NULL; } - int d = p[elfcpp::EI_DATA]; - if (d == elfcpp::ELFDATANONE) + if (size == 32) { - gold_error(_("%s: invalid ELF data encoding"), name.c_str()); - return NULL; - } - else if (d != elfcpp::ELFDATA2LSB - && d != elfcpp::ELFDATA2MSB) - { - gold_error(_("%s: unsupported ELF data encoding %d"), name.c_str(), d); - return NULL; - } - - bool big_endian = d == elfcpp::ELFDATA2MSB; - - if (c == elfcpp::ELFCLASS32) - { - if (bytes < elfcpp::Elf_sizes<32>::ehdr_size) - { - gold_error(_("%s: ELF file too short"), name.c_str()); - return NULL; - } if (big_endian) { #ifdef HAVE_TARGET_32_BIG @@ -2395,13 +2348,8 @@ make_elf_object(const std::string& name, Input_file* input_file, off_t offset, #endif } } - else + else if (size == 64) { - if (bytes < elfcpp::Elf_sizes<64>::ehdr_size) - { - gold_error(_("%s: ELF file too short"), name.c_str()); - return NULL; - } if (big_endian) { #ifdef HAVE_TARGET_64_BIG @@ -2435,6 +2383,8 @@ make_elf_object(const std::string& name, Input_file* input_file, off_t offset, #endif } } + else + gold_unreachable(); } // Instantiate the templates we need. diff --git a/gold/output.h b/gold/output.h index 6de6e69..377864c 100644 --- a/gold/output.h +++ b/gold/output.h @@ -3433,6 +3433,11 @@ class Output_file void close(); + // Return the size of this file. + off_t + filesize() + { return this->file_size_; } + // We currently always use mmap which makes the view handling quite // simple. In the future we may support other approaches. |