| 1 | // inremental.cc -- incremental linking support for gold |
| 2 | |
| 3 | // Copyright 2009 Free Software Foundation, Inc. |
| 4 | // Written by Mikolaj Zalewski <mikolajz@google.com>. |
| 5 | |
| 6 | // This file is part of gold. |
| 7 | |
| 8 | // This program is free software; you can redistribute it and/or modify |
| 9 | // it under the terms of the GNU General Public License as published by |
| 10 | // the Free Software Foundation; either version 3 of the License, or |
| 11 | // (at your option) any later version. |
| 12 | |
| 13 | // This program is distributed in the hope that it will be useful, |
| 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | // GNU General Public License for more details. |
| 17 | |
| 18 | // You should have received a copy of the GNU General Public License |
| 19 | // along with this program; if not, write to the Free Software |
| 20 | // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| 21 | // MA 02110-1301, USA. |
| 22 | |
| 23 | #include "gold.h" |
| 24 | |
| 25 | #include <cstdarg> |
| 26 | |
| 27 | #include "elfcpp.h" |
| 28 | #include "output.h" |
| 29 | #include "incremental.h" |
| 30 | #include "archive.h" |
| 31 | #include "output.h" |
| 32 | #include "target-select.h" |
| 33 | |
| 34 | using elfcpp::Convert; |
| 35 | |
| 36 | namespace gold { |
| 37 | |
| 38 | // Version information. Will change frequently during the development, later |
| 39 | // we could think about backward (and forward?) compatibility. |
| 40 | const unsigned int INCREMENTAL_LINK_VERSION = 1; |
| 41 | |
| 42 | namespace internal { |
| 43 | |
| 44 | // Header of the .gnu_incremental_input section. |
| 45 | struct Incremental_inputs_header_data |
| 46 | { |
| 47 | // Incremental linker version. |
| 48 | elfcpp::Elf_Word version; |
| 49 | |
| 50 | // Numer of input files in the link. |
| 51 | elfcpp::Elf_Word input_file_count; |
| 52 | |
| 53 | // Offset of command line options in .gnu_incremental_strtab. |
| 54 | elfcpp::Elf_Word command_line_offset; |
| 55 | |
| 56 | // Padding. |
| 57 | elfcpp::Elf_Word reserved; |
| 58 | }; |
| 59 | |
| 60 | // Data stored in .gnu_incremental_input after the header for each of the |
| 61 | // Incremental_input_header_data::input_file_count input entries. |
| 62 | struct Incremental_inputs_entry_data |
| 63 | { |
| 64 | // Offset of file name in .gnu_incremental_strtab section. |
| 65 | elfcpp::Elf_Word filename_offset; |
| 66 | |
| 67 | // Offset of data in .gnu_incremental_input. |
| 68 | elfcpp::Elf_Word data_offset; |
| 69 | |
| 70 | // Timestamp (in seconds). |
| 71 | elfcpp::Elf_Xword timestamp_sec; |
| 72 | |
| 73 | // Nano-second part of timestamp (if supported). |
| 74 | elfcpp::Elf_Word timestamp_nsec; |
| 75 | |
| 76 | // Type of the input entry. |
| 77 | elfcpp::Elf_Half input_type; |
| 78 | |
| 79 | // Padding. |
| 80 | elfcpp::Elf_Half reserved; |
| 81 | }; |
| 82 | |
| 83 | } |
| 84 | |
| 85 | // Accessors. |
| 86 | |
| 87 | // Reader class for .gnu_incremental_inputs header. See |
| 88 | // internal::Incremental_input_header for fields descriptions. |
| 89 | |
| 90 | template<int size, bool big_endian> |
| 91 | class Incremental_inputs_header |
| 92 | { |
| 93 | public: |
| 94 | Incremental_inputs_header(const unsigned char *p) |
| 95 | : p_(reinterpret_cast<const internal::Incremental_inputs_header_data*>(p)) |
| 96 | { } |
| 97 | |
| 98 | static const int data_size = sizeof(internal::Incremental_inputs_header_data); |
| 99 | |
| 100 | elfcpp::Elf_Word |
| 101 | get_version() const |
| 102 | { return Convert<32, big_endian>::convert_host(this->p_->version); } |
| 103 | |
| 104 | elfcpp::Elf_Word |
| 105 | get_input_file_count() const |
| 106 | { return Convert<32, big_endian>::convert_host(this->p_->input_file_count); } |
| 107 | |
| 108 | elfcpp::Elf_Word |
| 109 | get_command_line_offset() const |
| 110 | { return Convert<32, big_endian>::convert_host(this->p_->command_line_offset); } |
| 111 | |
| 112 | elfcpp::Elf_Word |
| 113 | get_reserved() const |
| 114 | { return Convert<32, big_endian>::convert_host(this->p_->reserved); } |
| 115 | |
| 116 | private: |
| 117 | const internal::Incremental_inputs_header_data* p_; |
| 118 | }; |
| 119 | |
| 120 | // Writer class for .gnu_incremental_inputs header. See |
| 121 | // internal::Incremental_input_header for fields descriptions. |
| 122 | |
| 123 | template<int size, bool big_endian> |
| 124 | class Incremental_inputs_header_write |
| 125 | { |
| 126 | public: |
| 127 | Incremental_inputs_header_write(unsigned char *p) |
| 128 | : p_(reinterpret_cast<internal::Incremental_inputs_header_data*>(p)) |
| 129 | { } |
| 130 | |
| 131 | static const int data_size = sizeof(internal::Incremental_inputs_header_data); |
| 132 | |
| 133 | void |
| 134 | put_version(elfcpp::Elf_Word v) |
| 135 | { this->p_->version = Convert<32, big_endian>::convert_host(v); } |
| 136 | |
| 137 | void |
| 138 | put_input_file_count(elfcpp::Elf_Word v) |
| 139 | { this->p_->input_file_count = Convert<32, big_endian>::convert_host(v); } |
| 140 | |
| 141 | void |
| 142 | put_command_line_offset(elfcpp::Elf_Word v) |
| 143 | { this->p_->command_line_offset = Convert<32, big_endian>::convert_host(v); } |
| 144 | |
| 145 | void |
| 146 | put_reserved(elfcpp::Elf_Word v) |
| 147 | { this->p_->reserved = Convert<32, big_endian>::convert_host(v); } |
| 148 | |
| 149 | private: |
| 150 | internal::Incremental_inputs_header_data* p_; |
| 151 | }; |
| 152 | |
| 153 | // Reader class for an .gnu_incremental_inputs entry. See |
| 154 | // internal::Incremental_input_entry for fields descriptions. |
| 155 | template<int size, bool big_endian> |
| 156 | class Incremental_inputs_entry |
| 157 | { |
| 158 | public: |
| 159 | Incremental_inputs_entry(const unsigned char *p) |
| 160 | : p_(reinterpret_cast<const internal::Incremental_inputs_entry_data*>(p)) |
| 161 | { } |
| 162 | |
| 163 | static const int data_size = sizeof(internal::Incremental_inputs_entry_data); |
| 164 | |
| 165 | elfcpp::Elf_Word |
| 166 | get_filename_offset(elfcpp::Elf_Word v) |
| 167 | { return Convert<32, big_endian>::convert_host(this->p_->filename_offset); } |
| 168 | |
| 169 | elfcpp::Elf_Word |
| 170 | get_data_offset(elfcpp::Elf_Word v) |
| 171 | { return Convert<32, big_endian>::convert_host(this->p_->data_offset); } |
| 172 | |
| 173 | elfcpp::Elf_Xword |
| 174 | get_timestamp_sec(elfcpp::Elf_Xword v) |
| 175 | { return Convert<64, big_endian>::convert_host(this->p_->timestamp_sec); } |
| 176 | |
| 177 | elfcpp::Elf_Word |
| 178 | get_timestamp_nsec(elfcpp::Elf_Word v) |
| 179 | { return Convert<32, big_endian>::convert_host(this->p_->timestamp_nsec); } |
| 180 | |
| 181 | elfcpp::Elf_Word |
| 182 | get_input_type(elfcpp::Elf_Word v) |
| 183 | { return Convert<32, big_endian>::convert_host(this->p_->input_type); } |
| 184 | |
| 185 | elfcpp::Elf_Word |
| 186 | get_reserved(elfcpp::Elf_Word v) |
| 187 | { return Convert<32, big_endian>::convert_host(this->p_->reserved); } |
| 188 | |
| 189 | private: |
| 190 | const internal::Incremental_inputs_entry_data* p_; |
| 191 | }; |
| 192 | |
| 193 | // Writer class for an .gnu_incremental_inputs entry. See |
| 194 | // internal::Incremental_input_entry for fields descriptions. |
| 195 | template<int size, bool big_endian> |
| 196 | class Incremental_inputs_entry_write |
| 197 | { |
| 198 | public: |
| 199 | Incremental_inputs_entry_write(unsigned char *p) |
| 200 | : p_(reinterpret_cast<internal::Incremental_inputs_entry_data*>(p)) |
| 201 | { } |
| 202 | |
| 203 | static const int data_size = sizeof(internal::Incremental_inputs_entry_data); |
| 204 | |
| 205 | void |
| 206 | put_filename_offset(elfcpp::Elf_Word v) |
| 207 | { this->p_->filename_offset = Convert<32, big_endian>::convert_host(v); } |
| 208 | |
| 209 | void |
| 210 | put_data_offset(elfcpp::Elf_Word v) |
| 211 | { this->p_->data_offset = Convert<32, big_endian>::convert_host(v); } |
| 212 | |
| 213 | void |
| 214 | put_timestamp_sec(elfcpp::Elf_Xword v) |
| 215 | { this->p_->timestamp_sec = Convert<64, big_endian>::convert_host(v); } |
| 216 | |
| 217 | void |
| 218 | put_timestamp_nsec(elfcpp::Elf_Word v) |
| 219 | { this->p_->timestamp_nsec = Convert<32, big_endian>::convert_host(v); } |
| 220 | |
| 221 | void |
| 222 | put_input_type(elfcpp::Elf_Word v) |
| 223 | { this->p_->input_type = Convert<32, big_endian>::convert_host(v); } |
| 224 | |
| 225 | void |
| 226 | put_reserved(elfcpp::Elf_Word v) |
| 227 | { this->p_->reserved = Convert<32, big_endian>::convert_host(v); } |
| 228 | |
| 229 | private: |
| 230 | internal::Incremental_inputs_entry_data* p_; |
| 231 | }; |
| 232 | |
| 233 | // Inform the user why we don't do an incremental link. Not called in |
| 234 | // the obvious case of missing output file. TODO: Is this helpful? |
| 235 | |
| 236 | void |
| 237 | vexplain_no_incremental(const char* format, va_list args) |
| 238 | { |
| 239 | char* buf = NULL; |
| 240 | if (vasprintf(&buf, format, args) < 0) |
| 241 | gold_nomem(); |
| 242 | gold_info(_("the link might take longer: " |
| 243 | "cannot perform incremental link: %s"), buf); |
| 244 | free(buf); |
| 245 | } |
| 246 | |
| 247 | void |
| 248 | explain_no_incremental(const char* format, ...) |
| 249 | { |
| 250 | va_list args; |
| 251 | va_start(args, format); |
| 252 | vexplain_no_incremental(format, args); |
| 253 | va_end(args); |
| 254 | } |
| 255 | |
| 256 | // Report an error. |
| 257 | |
| 258 | void |
| 259 | Incremental_binary::error(const char* format, ...) const |
| 260 | { |
| 261 | va_list args; |
| 262 | va_start(args, format); |
| 263 | // Current code only checks if the file can be used for incremental linking, |
| 264 | // so errors shouldn't fail the build, but only result in a fallback to a |
| 265 | // full build. |
| 266 | // TODO: when we implement incremental editing of the file, we may need a |
| 267 | // flag that will cause errors to be treated seriously. |
| 268 | vexplain_no_incremental(format, args); |
| 269 | va_end(args); |
| 270 | } |
| 271 | |
| 272 | template<int size, bool big_endian> |
| 273 | bool |
| 274 | Sized_incremental_binary<size, big_endian>::do_find_incremental_inputs_section( |
| 275 | Location* location, |
| 276 | unsigned int* strtab_shndx) |
| 277 | { |
| 278 | unsigned int shndx = this->elf_file_.find_section_by_type( |
| 279 | elfcpp::SHT_GNU_INCREMENTAL_INPUTS); |
| 280 | if (shndx == elfcpp::SHN_UNDEF) // Not found. |
| 281 | return false; |
| 282 | *strtab_shndx = this->elf_file_.section_link(shndx); |
| 283 | *location = this->elf_file_.section_contents(shndx); |
| 284 | return true; |
| 285 | } |
| 286 | |
| 287 | template<int size, bool big_endian> |
| 288 | bool |
| 289 | Sized_incremental_binary<size, big_endian>::do_check_inputs( |
| 290 | Incremental_inputs* incremental_inputs) |
| 291 | { |
| 292 | const int entry_size = |
| 293 | Incremental_inputs_entry_write<size, big_endian>::data_size; |
| 294 | const int header_size = |
| 295 | Incremental_inputs_header_write<size, big_endian>::data_size; |
| 296 | |
| 297 | unsigned int strtab_shndx; |
| 298 | Location location; |
| 299 | |
| 300 | if (!do_find_incremental_inputs_section(&location, &strtab_shndx)) |
| 301 | { |
| 302 | explain_no_incremental(_("no incremental data from previous build")); |
| 303 | return false; |
| 304 | } |
| 305 | if (location.data_size < header_size |
| 306 | || strtab_shndx >= this->elf_file_.shnum() |
| 307 | || this->elf_file_.section_type(strtab_shndx) != elfcpp::SHT_STRTAB) |
| 308 | { |
| 309 | explain_no_incremental(_("invalid incremental build data")); |
| 310 | return false; |
| 311 | } |
| 312 | |
| 313 | Location strtab_location(this->elf_file_.section_contents(strtab_shndx)); |
| 314 | View data_view(view(location)); |
| 315 | View strtab_view(view(strtab_location)); |
| 316 | elfcpp::Elf_strtab strtab(strtab_view.data(), strtab_location.data_size); |
| 317 | Incremental_inputs_header<size, big_endian> header(data_view.data()); |
| 318 | |
| 319 | if (header.get_version() != INCREMENTAL_LINK_VERSION) |
| 320 | { |
| 321 | explain_no_incremental(_("different version of incremental build data")); |
| 322 | return false; |
| 323 | } |
| 324 | |
| 325 | const char* command_line; |
| 326 | // We divide instead of multiplying to make sure there is no integer |
| 327 | // overflow. |
| 328 | size_t max_input_entries = (location.data_size - header_size) / entry_size; |
| 329 | if (header.get_input_file_count() > max_input_entries |
| 330 | || !strtab.get_c_string(header.get_command_line_offset(), &command_line)) |
| 331 | { |
| 332 | explain_no_incremental(_("invalid incremental build data")); |
| 333 | return false; |
| 334 | } |
| 335 | |
| 336 | if (incremental_inputs->command_line() != command_line) |
| 337 | { |
| 338 | explain_no_incremental(_("command line changed")); |
| 339 | return false; |
| 340 | } |
| 341 | |
| 342 | // TODO: compare incremental_inputs->inputs() with entries in data_view. |
| 343 | return true; |
| 344 | } |
| 345 | |
| 346 | namespace |
| 347 | { |
| 348 | |
| 349 | // Create a Sized_incremental_binary object of the specified size and |
| 350 | // endianness. Fails if the target architecture is not supported. |
| 351 | |
| 352 | template<int size, bool big_endian> |
| 353 | Incremental_binary* |
| 354 | make_sized_incremental_binary(Output_file* file, |
| 355 | const elfcpp::Ehdr<size, big_endian>& ehdr) |
| 356 | { |
| 357 | Target* target = select_target(ehdr.get_e_machine(), size, big_endian, |
| 358 | ehdr.get_e_ident()[elfcpp::EI_OSABI], |
| 359 | ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]); |
| 360 | if (target == NULL) |
| 361 | { |
| 362 | explain_no_incremental(_("unsupported ELF machine number %d"), |
| 363 | ehdr.get_e_machine()); |
| 364 | return NULL; |
| 365 | } |
| 366 | |
| 367 | return new Sized_incremental_binary<size, big_endian>(file, ehdr, target); |
| 368 | } |
| 369 | |
| 370 | } // End of anonymous namespace. |
| 371 | |
| 372 | // Create an Incremental_binary object for FILE. Returns NULL is this is not |
| 373 | // possible, e.g. FILE is not an ELF file or has an unsupported target. FILE |
| 374 | // should be opened. |
| 375 | |
| 376 | Incremental_binary* |
| 377 | open_incremental_binary(Output_file* file) |
| 378 | { |
| 379 | off_t filesize = file->filesize(); |
| 380 | int want = elfcpp::Elf_recognizer::max_header_size; |
| 381 | if (filesize < want) |
| 382 | want = filesize; |
| 383 | |
| 384 | const unsigned char* p = file->get_input_view(0, want); |
| 385 | if (!elfcpp::Elf_recognizer::is_elf_file(p, want)) |
| 386 | { |
| 387 | explain_no_incremental(_("output is not an ELF file.")); |
| 388 | return NULL; |
| 389 | } |
| 390 | |
| 391 | int size; |
| 392 | bool big_endian; |
| 393 | std::string error; |
| 394 | if (!elfcpp::Elf_recognizer::is_valid_header(p, want, &size, &big_endian, |
| 395 | &error)) |
| 396 | { |
| 397 | explain_no_incremental(error.c_str()); |
| 398 | return NULL; |
| 399 | } |
| 400 | |
| 401 | Incremental_binary* result = NULL; |
| 402 | if (size == 32) |
| 403 | { |
| 404 | if (big_endian) |
| 405 | { |
| 406 | #ifdef HAVE_TARGET_32_BIG |
| 407 | result = make_sized_incremental_binary<32, true>( |
| 408 | file, elfcpp::Ehdr<32, true>(p)); |
| 409 | #else |
| 410 | explain_no_incremental(_("unsupported file: 32-bit, big-endian")); |
| 411 | #endif |
| 412 | } |
| 413 | else |
| 414 | { |
| 415 | #ifdef HAVE_TARGET_32_LITTLE |
| 416 | result = make_sized_incremental_binary<32, false>( |
| 417 | file, elfcpp::Ehdr<32, false>(p)); |
| 418 | #else |
| 419 | explain_no_incremental(_("unsupported file: 32-bit, little-endian")); |
| 420 | #endif |
| 421 | } |
| 422 | } |
| 423 | else if (size == 64) |
| 424 | { |
| 425 | if (big_endian) |
| 426 | { |
| 427 | #ifdef HAVE_TARGET_64_BIG |
| 428 | result = make_sized_incremental_binary<64, true>( |
| 429 | file, elfcpp::Ehdr<64, true>(p)); |
| 430 | #else |
| 431 | explain_no_incremental(_("unsupported file: 64-bit, big-endian")); |
| 432 | #endif |
| 433 | } |
| 434 | else |
| 435 | { |
| 436 | #ifdef HAVE_TARGET_64_LITTLE |
| 437 | result = make_sized_incremental_binary<64, false>( |
| 438 | file, elfcpp::Ehdr<64, false>(p)); |
| 439 | #else |
| 440 | explain_no_incremental(_("unsupported file: 64-bit, little-endian")); |
| 441 | #endif |
| 442 | } |
| 443 | } |
| 444 | else |
| 445 | gold_unreachable(); |
| 446 | |
| 447 | return result; |
| 448 | } |
| 449 | |
| 450 | // Analyzes the output file to check if incremental linking is possible and |
| 451 | // (to be done) what files need to be relinked. |
| 452 | |
| 453 | bool |
| 454 | Incremental_checker::can_incrementally_link_output_file() |
| 455 | { |
| 456 | Output_file output(this->output_name_); |
| 457 | if (!output.open_for_modification()) |
| 458 | return false; |
| 459 | Incremental_binary* binary = open_incremental_binary(&output); |
| 460 | if (binary == NULL) |
| 461 | return false; |
| 462 | return binary->check_inputs(this->incremental_inputs_); |
| 463 | } |
| 464 | |
| 465 | // Add the command line to the string table, setting |
| 466 | // command_line_key_. In incremental builds, the command line is |
| 467 | // stored in .gnu_incremental_inputs so that the next linker run can |
| 468 | // check if the command line options didn't change. |
| 469 | |
| 470 | void |
| 471 | Incremental_inputs::report_command_line(int argc, const char* const* argv) |
| 472 | { |
| 473 | // Always store 'gold' as argv[0] to avoid a full relink if the user used a |
| 474 | // different path to the linker. |
| 475 | std::string args("gold"); |
| 476 | // Copied from collect_argv in main.cc. |
| 477 | for (int i = 1; i < argc; ++i) |
| 478 | { |
| 479 | // Adding/removing these options should result in a full relink. |
| 480 | if (strcmp(argv[i], "--incremental-changed") == 0 |
| 481 | || strcmp(argv[i], "--incremental-unchanged") == 0 |
| 482 | || strcmp(argv[i], "--incremental-unknown") == 0) |
| 483 | continue; |
| 484 | |
| 485 | args.append(" '"); |
| 486 | // Now append argv[i], but with all single-quotes escaped |
| 487 | const char* argpos = argv[i]; |
| 488 | while (1) |
| 489 | { |
| 490 | const int len = strcspn(argpos, "'"); |
| 491 | args.append(argpos, len); |
| 492 | if (argpos[len] == '\0') |
| 493 | break; |
| 494 | args.append("'\"'\"'"); |
| 495 | argpos += len + 1; |
| 496 | } |
| 497 | args.append("'"); |
| 498 | } |
| 499 | |
| 500 | this->command_line_ = args; |
| 501 | this->strtab_->add(this->command_line_.c_str(), false, |
| 502 | &this->command_line_key_); |
| 503 | } |
| 504 | |
| 505 | // Record that the input argument INPUT is an achive ARCHIVE. This is |
| 506 | // called by Read_symbols after finding out the type of the file. |
| 507 | |
| 508 | void |
| 509 | Incremental_inputs::report_archive(const Input_argument* input, |
| 510 | Archive* archive) |
| 511 | { |
| 512 | Hold_lock hl(*this->lock_); |
| 513 | |
| 514 | Input_info info; |
| 515 | info.type = INCREMENTAL_INPUT_ARCHIVE; |
| 516 | info.archive = archive; |
| 517 | info.mtime = archive->file().get_mtime(); |
| 518 | this->inputs_map_.insert(std::make_pair(input, info)); |
| 519 | } |
| 520 | |
| 521 | // Record that the input argument INPUT is an object OBJ. This is |
| 522 | // called by Read_symbols after finding out the type of the file. |
| 523 | |
| 524 | void |
| 525 | Incremental_inputs::report_object(const Input_argument* input, |
| 526 | Object* obj) |
| 527 | { |
| 528 | Hold_lock hl(*this->lock_); |
| 529 | |
| 530 | Input_info info; |
| 531 | info.type = (obj->is_dynamic() |
| 532 | ? INCREMENTAL_INPUT_SHARED_LIBRARY |
| 533 | : INCREMENTAL_INPUT_OBJECT); |
| 534 | info.object = obj; |
| 535 | info.mtime = obj->input_file()->file().get_mtime(); |
| 536 | this->inputs_map_.insert(std::make_pair(input, info)); |
| 537 | } |
| 538 | |
| 539 | // Record that the input argument INPUT is an script SCRIPT. This is |
| 540 | // called by read_script after parsing the script and reading the list |
| 541 | // of inputs added by this script. |
| 542 | |
| 543 | void |
| 544 | Incremental_inputs::report_script(const Input_argument* input, |
| 545 | Timespec mtime, |
| 546 | Script_info* script) |
| 547 | { |
| 548 | Hold_lock hl(*this->lock_); |
| 549 | |
| 550 | Input_info info; |
| 551 | info.type = INCREMENTAL_INPUT_SCRIPT; |
| 552 | info.script = script; |
| 553 | info.mtime = mtime; |
| 554 | this->inputs_map_.insert(std::make_pair(input, info)); |
| 555 | } |
| 556 | |
| 557 | // Compute indexes in the order in which the inputs should appear in |
| 558 | // .gnu_incremental_inputs. This needs to be done after all the |
| 559 | // scripts are parsed. The function is first called for the command |
| 560 | // line inputs arguments and may call itself recursively for e.g. a |
| 561 | // list of elements of a group or a list of inputs added by a script. |
| 562 | // The [BEGIN; END) interval to analyze and *INDEX is the current |
| 563 | // value of the index (that will be updated). |
| 564 | |
| 565 | void |
| 566 | Incremental_inputs::finalize_inputs( |
| 567 | Input_argument_list::const_iterator begin, |
| 568 | Input_argument_list::const_iterator end, |
| 569 | unsigned int* index) |
| 570 | { |
| 571 | for (Input_argument_list::const_iterator p = begin; p != end; ++p) |
| 572 | { |
| 573 | if (p->is_group()) |
| 574 | { |
| 575 | finalize_inputs(p->group()->begin(), p->group()->end(), index); |
| 576 | continue; |
| 577 | } |
| 578 | |
| 579 | Inputs_info_map::iterator it = this->inputs_map_.find(&(*p)); |
| 580 | // TODO: turn it into an assert when the code will be more stable. |
| 581 | if (it == this->inputs_map_.end()) |
| 582 | { |
| 583 | gold_error("internal error: %s: incremental build info not provided", |
| 584 | (p->is_file() ? p->file().name() : "[group]")); |
| 585 | continue; |
| 586 | } |
| 587 | Input_info* info = &it->second; |
| 588 | info->index = *index; |
| 589 | (*index)++; |
| 590 | this->strtab_->add(p->file().name(), false, &info->filename_key); |
| 591 | if (info->type == INCREMENTAL_INPUT_SCRIPT) |
| 592 | { |
| 593 | finalize_inputs(info->script->inputs()->begin(), |
| 594 | info->script->inputs()->end(), |
| 595 | index); |
| 596 | } |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | // Finalize the incremental link information. Called from |
| 601 | // Layout::finalize. |
| 602 | |
| 603 | void |
| 604 | Incremental_inputs::finalize() |
| 605 | { |
| 606 | unsigned int index = 0; |
| 607 | finalize_inputs(this->inputs_->begin(), this->inputs_->end(), &index); |
| 608 | |
| 609 | // Sanity check. |
| 610 | for (Inputs_info_map::const_iterator p = this->inputs_map_.begin(); |
| 611 | p != this->inputs_map_.end(); |
| 612 | ++p) |
| 613 | { |
| 614 | gold_assert(p->second.filename_key != 0); |
| 615 | } |
| 616 | |
| 617 | this->strtab_->set_string_offsets(); |
| 618 | } |
| 619 | |
| 620 | // Create the content of the .gnu_incremental_inputs section. |
| 621 | |
| 622 | Output_section_data* |
| 623 | Incremental_inputs::create_incremental_inputs_section_data() |
| 624 | { |
| 625 | switch (parameters->size_and_endianness()) |
| 626 | { |
| 627 | #ifdef HAVE_TARGET_32_LITTLE |
| 628 | case Parameters::TARGET_32_LITTLE: |
| 629 | return this->sized_create_inputs_section_data<32, false>(); |
| 630 | #endif |
| 631 | #ifdef HAVE_TARGET_32_BIG |
| 632 | case Parameters::TARGET_32_BIG: |
| 633 | return this->sized_create_inputs_section_data<32, true>(); |
| 634 | #endif |
| 635 | #ifdef HAVE_TARGET_64_LITTLE |
| 636 | case Parameters::TARGET_64_LITTLE: |
| 637 | return this->sized_create_inputs_section_data<64, false>(); |
| 638 | #endif |
| 639 | #ifdef HAVE_TARGET_64_BIG |
| 640 | case Parameters::TARGET_64_BIG: |
| 641 | return this->sized_create_inputs_section_data<64, true>(); |
| 642 | #endif |
| 643 | default: |
| 644 | gold_unreachable(); |
| 645 | } |
| 646 | } |
| 647 | |
| 648 | // Sized creation of .gnu_incremental_inputs section. |
| 649 | |
| 650 | template<int size, bool big_endian> |
| 651 | Output_section_data* |
| 652 | Incremental_inputs::sized_create_inputs_section_data() |
| 653 | { |
| 654 | const int entry_size = |
| 655 | Incremental_inputs_entry_write<size, big_endian>::data_size; |
| 656 | const int header_size = |
| 657 | Incremental_inputs_header_write<size, big_endian>::data_size; |
| 658 | |
| 659 | unsigned int sz = header_size + entry_size * this->inputs_map_.size(); |
| 660 | unsigned char* buffer = new unsigned char[sz]; |
| 661 | unsigned char* inputs_base = buffer + header_size; |
| 662 | |
| 663 | Incremental_inputs_header_write<size, big_endian> header_writer(buffer); |
| 664 | gold_assert(this->command_line_key_ > 0); |
| 665 | int cmd_offset = this->strtab_->get_offset_from_key(this->command_line_key_); |
| 666 | |
| 667 | header_writer.put_version(INCREMENTAL_LINK_VERSION); |
| 668 | header_writer.put_input_file_count(this->inputs_map_.size()); |
| 669 | header_writer.put_command_line_offset(cmd_offset); |
| 670 | header_writer.put_reserved(0); |
| 671 | |
| 672 | for (Inputs_info_map::const_iterator it = this->inputs_map_.begin(); |
| 673 | it != this->inputs_map_.end(); |
| 674 | ++it) |
| 675 | { |
| 676 | gold_assert(it->second.index < this->inputs_map_.size()); |
| 677 | |
| 678 | unsigned char* entry_buffer = |
| 679 | inputs_base + it->second.index * entry_size; |
| 680 | Incremental_inputs_entry_write<size, big_endian> entry(entry_buffer); |
| 681 | int filename_offset = |
| 682 | this->strtab_->get_offset_from_key(it->second.filename_key); |
| 683 | entry.put_filename_offset(filename_offset); |
| 684 | // TODO: add per input data and timestamp. Currently we store |
| 685 | // an out-of-bounds offset for future version of gold to reject |
| 686 | // such an incremental_inputs section. |
| 687 | entry.put_data_offset(0xffffffff); |
| 688 | entry.put_timestamp_sec(it->second.mtime.seconds); |
| 689 | entry.put_timestamp_nsec(it->second.mtime.nanoseconds); |
| 690 | entry.put_input_type(it->second.type); |
| 691 | entry.put_reserved(0); |
| 692 | } |
| 693 | |
| 694 | return new Output_data_const_buffer(buffer, sz, 8, |
| 695 | "** incremental link inputs list"); |
| 696 | } |
| 697 | |
| 698 | // Instantiate the templates we need. |
| 699 | |
| 700 | #ifdef HAVE_TARGET_32_LITTLE |
| 701 | template |
| 702 | class Sized_incremental_binary<32, false>; |
| 703 | #endif |
| 704 | |
| 705 | #ifdef HAVE_TARGET_32_BIG |
| 706 | template |
| 707 | class Sized_incremental_binary<32, true>; |
| 708 | #endif |
| 709 | |
| 710 | #ifdef HAVE_TARGET_64_LITTLE |
| 711 | template |
| 712 | class Sized_incremental_binary<64, false>; |
| 713 | #endif |
| 714 | |
| 715 | #ifdef HAVE_TARGET_64_BIG |
| 716 | template |
| 717 | class Sized_incremental_binary<64, true>; |
| 718 | #endif |
| 719 | |
| 720 | } // End namespace gold. |