// 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;
// Version information. Will change frequently during the development, later
// we could think about backward (and forward?) compatibility.
-const int INCREMENTAL_LINK_VERSION = 1;
+const unsigned int INCREMENTAL_LINK_VERSION = 1;
namespace internal {
// Accessors.
-// See internal::Incremental_input_header for fields descriptions.
+// Reader class for .gnu_incremental_inputs header. See
+// internal::Incremental_input_header for fields descriptions.
+
+template<int size, bool big_endian>
+class Incremental_inputs_header
+{
+ public:
+ Incremental_inputs_header(const unsigned char *p)
+ : p_(reinterpret_cast<const internal::Incremental_inputs_header_data*>(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_;
+};
+
+// Writer class for .gnu_incremental_inputs header. See
+// internal::Incremental_input_header for fields descriptions.
+
template<int size, bool big_endian>
class Incremental_inputs_header_write
{
internal::Incremental_inputs_header_data* p_;
};
-// See internal::Incremental_input_entry for fields descriptions.
+// Reader class for an .gnu_incremental_inputs entry. See
+// internal::Incremental_input_entry for fields descriptions.
+template<int size, bool big_endian>
+class Incremental_inputs_entry
+{
+ public:
+ Incremental_inputs_entry(const unsigned char *p)
+ : p_(reinterpret_cast<const internal::Incremental_inputs_entry_data*>(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); }
+
+ private:
+ const internal::Incremental_inputs_entry_data* p_;
+};
+
+// Writer class for an .gnu_incremental_inputs entry. See
+// internal::Incremental_input_entry for fields descriptions.
template<int size, bool big_endian>
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* 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;
+ }
+
+ 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.
Output_file output(this->output_name_);
if (!output.open_for_modification())
return false;
- return true;
+ 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
}
args.append("'");
}
- this->strtab_->add(args.c_str(), true, &this->command_line_key_);
+
+ 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
"** 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.