github.com/kidsbmilk/gofronted_all@v0.0.0-20220701224323-6479d5976c5d/go/import-archive.cc (about) 1 // import-archive.cc -- Go frontend read import data from an archive file. 2 3 // Copyright 2009 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 #include "go-system.h" 8 9 #include "go-diagnostics.h" 10 #include "import.h" 11 12 #ifndef O_BINARY 13 #define O_BINARY 0 14 #endif 15 16 // Archive magic numbers. 17 18 static const char armag[] = 19 { 20 '!', '<', 'a', 'r', 'c', 'h', '>', '\n' 21 }; 22 23 static const char armagt[] = 24 { 25 '!', '<', 't', 'h', 'i', 'n', '>', '\n' 26 }; 27 28 static const char armagb[] = 29 { 30 '<', 'b', 'i', 'g', 'a', 'f', '>', '\n' 31 }; 32 33 static const char arfmag[2] = { '`', '\n' }; 34 35 // Archive fixed length header for AIX big format. 36 37 struct Archive_fl_header 38 { 39 // Archive magic string. 40 char fl_magic[8]; 41 // Offset to member table. 42 char fl_memoff[20]; 43 // Offset to global symbol table. 44 char fl_gstoff[20]; 45 // Offset to global symbol table for 64-bit objects. 46 char fl_gst64off[20]; 47 // Offset to first archive member. 48 char fl_fstmoff[20]; 49 // Offset to last archive member. 50 char fl_lstmoff[20]; 51 // Offset to first member on free list. 52 char fl_freeoff[20]; 53 }; 54 55 // The header of an entry in an archive. This is all readable text, 56 // padded with spaces where necesary. 57 58 struct Archive_header 59 { 60 // The entry name. 61 char ar_name[16]; 62 // The file modification time. 63 char ar_date[12]; 64 // The user's UID in decimal. 65 char ar_uid[6]; 66 // The user's GID in decimal. 67 char ar_gid[6]; 68 // The file mode in octal. 69 char ar_mode[8]; 70 // The file size in decimal. 71 char ar_size[10]; 72 // The final magic code. 73 char ar_fmag[2]; 74 }; 75 76 // The header of an entry in an AIX big archive. 77 // This is followed by ar_namlen bytes + 2 bytes for arfmag. 78 79 struct Archive_big_header 80 { 81 // The file size in decimal. 82 char ar_size[20]; 83 // The next member offset in decimal. 84 char ar_nxtmem[20]; 85 // The previous member offset in decimal. 86 char ar_prvmem[20]; 87 // The file modification time in decimal. 88 char ar_date[12]; 89 // The user's UID in decimal. 90 char ar_uid[12]; 91 // The user's GID in decimal. 92 char ar_gid[12]; 93 // The file mode in octal. 94 char ar_mode[12]; 95 // The file name length in decimal. 96 char ar_namlen[4]; 97 }; 98 99 // The functions in this file extract Go export data from an archive. 100 101 const int Import::archive_magic_len; 102 103 // Return true if BYTES, which are from the start of the file, are an 104 // archive magic number. 105 106 bool 107 Import::is_archive_magic(const char* bytes) 108 { 109 return (memcmp(bytes, armag, Import::archive_magic_len) == 0 110 || memcmp(bytes, armagt, Import::archive_magic_len) == 0 111 || memcmp(bytes, armagb, Import::archive_magic_len) == 0); 112 } 113 114 // An object used to read an archive file. 115 116 class Archive_file 117 { 118 public: 119 Archive_file(const std::string& filename, int fd, Location location) 120 : filename_(filename), fd_(fd), filesize_(-1), first_member_offset_(0), 121 extended_names_(), is_thin_archive_(false), is_big_archive_(false), 122 location_(location), nested_archives_() 123 { } 124 125 // Initialize. 126 bool 127 initialize(); 128 129 // Return the file name. 130 const std::string& 131 filename() const 132 { return this->filename_; } 133 134 // Get the file size. 135 off_t 136 filesize() const 137 { return this->filesize_; } 138 139 // Return the offset of the first member. 140 off_t 141 first_member_offset() const 142 { return this->first_member_offset_; } 143 144 // Return whether this is a thin archive. 145 bool 146 is_thin_archive() const 147 { return this->is_thin_archive_; } 148 149 // Return whether this is a big archive. 150 bool 151 is_big_archive() const 152 { return this->is_big_archive_; } 153 154 // Return the location of the import statement. 155 Location 156 location() const 157 { return this->location_; } 158 159 // Read bytes. 160 bool 161 read(off_t offset, off_t size, char*); 162 163 // Parse a decimal in readable text. 164 bool 165 parse_decimal(const char* str, off_t size, long* res) const; 166 167 // Read the archive header at OFF, setting *PNAME, *SIZE, 168 // *NESTED_OFF and *NEXT_OFF. 169 bool 170 read_header(off_t off, std::string* pname, off_t* size, off_t* nested_off, 171 off_t* next_off); 172 173 // Interpret the header of HDR, the header of the archive member at 174 // file offset OFF. Return whether it succeeded. Set *SIZE to the 175 // size of the member. Set *PNAME to the name of the member. Set 176 // *NESTED_OFF to the offset in a nested archive. 177 bool 178 interpret_header(const Archive_header* hdr, off_t off, 179 std::string* pname, off_t* size, off_t* nested_off) const; 180 181 // Get the file and offset for an archive member. 182 bool 183 get_file_and_offset(off_t off, const std::string& hdrname, 184 off_t nested_off, int* memfd, off_t* memoff, 185 std::string* memname); 186 187 private: 188 // Initialize a big archive (AIX) 189 bool 190 initialize_big_archive(); 191 192 // Initialize a normal archive 193 bool 194 initialize_archive(); 195 196 // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF. 197 bool 198 read_big_archive_header(off_t off, std::string* pname, 199 off_t* size, off_t* next_off); 200 201 // Read the normal archive header at OFF, setting *PNAME, *SIZE, 202 // *NESTED_OFF and *NEXT_OFF. 203 bool 204 read_archive_header(off_t off, std::string* pname, off_t* size, 205 off_t* nested_off, off_t* next_off); 206 207 // For keeping track of open nested archives in a thin archive file. 208 typedef std::map<std::string, Archive_file*> Nested_archive_table; 209 210 // The name of the file. 211 std::string filename_; 212 // The file descriptor. 213 int fd_; 214 // The file size; 215 off_t filesize_; 216 // The first member offset; 217 off_t first_member_offset_; 218 // The extended name table. 219 std::string extended_names_; 220 // Whether this is a thin archive. 221 bool is_thin_archive_; 222 // Whether this is a big archive. 223 bool is_big_archive_; 224 // The location of the import statements. 225 Location location_; 226 // Table of nested archives. 227 Nested_archive_table nested_archives_; 228 }; 229 230 bool 231 Archive_file::initialize() 232 { 233 struct stat st; 234 if (fstat(this->fd_, &st) < 0) 235 { 236 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); 237 return false; 238 } 239 this->filesize_ = st.st_size; 240 241 char buf[sizeof(armagt)]; 242 if (::lseek(this->fd_, 0, SEEK_SET) < 0 243 || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt)) 244 { 245 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); 246 return false; 247 } 248 if (memcmp(buf, armagt, sizeof(armagt)) == 0) 249 this->is_thin_archive_ = true; 250 else if (memcmp(buf, armagb, sizeof(armagb)) == 0) 251 this->is_big_archive_ = true; 252 253 if (this->is_big_archive_) 254 return this->initialize_big_archive(); 255 else 256 return this->initialize_archive(); 257 } 258 259 // Initialize a big archive (AIX). 260 261 bool 262 Archive_file::initialize_big_archive() 263 { 264 Archive_fl_header flhdr; 265 266 // Read the fixed length header. 267 if (::lseek(this->fd_, 0, SEEK_SET) < 0 268 || ::read(this->fd_, &flhdr, sizeof(flhdr)) != sizeof(flhdr)) 269 { 270 go_error_at(this->location_, "%s: could not read archive header", 271 this->filename_.c_str()); 272 return false; 273 } 274 275 // Parse offset of the first member. 276 long off; 277 if (!this->parse_decimal(flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff), &off)) 278 { 279 char* buf = new char[sizeof(flhdr.fl_fstmoff) + 1]; 280 memcpy(buf, flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff)); 281 go_error_at(this->location_, 282 ("%s: malformed first member offset in archive header" 283 " (expected decimal, got %s)"), 284 this->filename_.c_str(), buf); 285 delete[] buf; 286 return false; 287 } 288 if (off == 0) // Empty archive. 289 this->first_member_offset_ = this->filesize_; 290 else 291 this->first_member_offset_ = off; 292 return true; 293 } 294 295 // Initialize a normal archive. 296 297 bool 298 Archive_file::initialize_archive() 299 { 300 this->first_member_offset_ = sizeof(armag); 301 if (this->first_member_offset_ == this->filesize_) 302 { 303 // Empty archive. 304 return true; 305 } 306 307 // Look for the extended name table. 308 std::string filename; 309 off_t size; 310 off_t next_off; 311 if (!this->read_header(this->first_member_offset_, &filename, 312 &size, NULL, &next_off)) 313 return false; 314 if (filename.empty()) 315 { 316 // We found the symbol table. 317 if (!this->read_header(next_off, &filename, &size, NULL, NULL)) 318 filename.clear(); 319 } 320 if (filename == "/") 321 { 322 char* rdbuf = new char[size]; 323 if (::read(this->fd_, rdbuf, size) != size) 324 { 325 go_error_at(this->location_, "%s: could not read extended names", 326 filename.c_str()); 327 delete[] rdbuf; 328 return false; 329 } 330 this->extended_names_.assign(rdbuf, size); 331 delete[] rdbuf; 332 } 333 334 return true; 335 } 336 337 // Read bytes from the file. 338 339 bool 340 Archive_file::read(off_t offset, off_t size, char* buf) 341 { 342 if (::lseek(this->fd_, offset, SEEK_SET) < 0 343 || ::read(this->fd_, buf, size) != size) 344 { 345 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); 346 return false; 347 } 348 return true; 349 } 350 351 // Parse a decimal in readable text. 352 353 bool 354 Archive_file::parse_decimal(const char* str, off_t size, long* res) const 355 { 356 char* buf = new char[size + 1]; 357 memcpy(buf, str, size); 358 char* ps = buf + size; 359 while (ps > buf && ps[-1] == ' ') 360 --ps; 361 *ps = '\0'; 362 363 errno = 0; 364 char* end; 365 *res = strtol(buf, &end, 10); 366 if (*end != '\0' 367 || *res < 0 368 || (*res == LONG_MAX && errno == ERANGE)) 369 { 370 delete[] buf; 371 return false; 372 } 373 delete[] buf; 374 return true; 375 } 376 377 // Read the header at OFF. Set *PNAME to the name, *SIZE to the size, 378 // *NESTED_OFF to the nested offset, and *NEXT_OFF to the next member offset. 379 380 bool 381 Archive_file::read_header(off_t off, std::string* pname, off_t* size, 382 off_t* nested_off, off_t* next_off) 383 { 384 if (::lseek(this->fd_, off, SEEK_SET) < 0) 385 { 386 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); 387 return false; 388 } 389 if (this->is_big_archive_) 390 return this->read_big_archive_header(off, pname, size, next_off); 391 else 392 return this->read_archive_header(off, pname, size, nested_off, next_off); 393 } 394 395 // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF. 396 397 bool 398 Archive_file::read_big_archive_header(off_t off, std::string* pname, 399 off_t* size, off_t* next_off) 400 { 401 Archive_big_header hdr; 402 ssize_t got; 403 404 got = ::read(this->fd_, &hdr, sizeof hdr); 405 if (got != sizeof hdr) 406 { 407 if (got < 0) 408 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); 409 else if (got > 0) 410 go_error_at(this->location_, "%s: short entry header at %ld", 411 this->filename_.c_str(), static_cast<long>(off)); 412 else 413 go_error_at(this->location_, "%s: unexpected EOF at %ld", 414 this->filename_.c_str(), static_cast<long>(off)); 415 } 416 417 long local_size; 418 if (!this->parse_decimal(hdr.ar_size, sizeof(hdr.ar_size), &local_size)) 419 { 420 char* buf = new char[sizeof(hdr.ar_size) + 1]; 421 memcpy(buf, hdr.ar_size, sizeof(hdr.ar_size)); 422 go_error_at(this->location_, 423 ("%s: malformed size in entry header at %ld" 424 " (expected decimal, got %s)"), 425 this->filename_.c_str(), static_cast<long>(off), buf); 426 delete[] buf; 427 return false; 428 } 429 *size = local_size; 430 431 long namlen; 432 if (!this->parse_decimal(hdr.ar_namlen, sizeof(hdr.ar_namlen), &namlen)) 433 { 434 char* buf = new char[sizeof(hdr.ar_namlen) + 1]; 435 memcpy(buf, hdr.ar_namlen, sizeof(hdr.ar_namlen)); 436 go_error_at(this->location_, 437 ("%s: malformed name length in entry header at %ld" 438 " (expected decimal, got %s)"), 439 this->filename_.c_str(), static_cast<long>(off), buf); 440 delete[] buf; 441 return false; 442 } 443 // Read member name following member header. 444 char* rdbuf = new char[namlen]; 445 got = ::read(this->fd_, rdbuf, namlen); 446 if (got != namlen) 447 { 448 go_error_at(this->location_, 449 "%s: malformed member name in entry header at %ld", 450 this->filename_.c_str(), static_cast<long>(off)); 451 delete[] rdbuf; 452 return false; 453 } 454 pname->assign(rdbuf, namlen); 455 delete[] rdbuf; 456 457 long local_next_off; 458 if (!this->parse_decimal(hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem), &local_next_off)) 459 { 460 char* buf = new char[sizeof(hdr.ar_nxtmem) + 1]; 461 memcpy(buf, hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem)); 462 go_error_at(this->location_, 463 ("%s: malformed next member offset in entry header at %ld" 464 " (expected decimal, got %s)"), 465 this->filename_.c_str(), static_cast<long>(off), buf); 466 delete[] buf; 467 return false; 468 } 469 if (next_off != NULL) 470 { 471 if (local_next_off == 0) // Last member. 472 *next_off = this->filesize_; 473 else 474 *next_off = local_next_off; 475 } 476 return true; 477 } 478 479 // Read the normal archive header at OFF, setting *PNAME, *SIZE, 480 // *NESTED_OFF and *NEXT_OFF. 481 482 bool 483 Archive_file::read_archive_header(off_t off, std::string* pname, off_t* size, 484 off_t* nested_off, off_t* next_off) 485 { 486 Archive_header hdr; 487 ssize_t got = ::read(this->fd_, &hdr, sizeof hdr); 488 if (got != sizeof hdr) 489 { 490 if (got < 0) 491 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); 492 else if (got > 0) 493 go_error_at(this->location_, "%s: short archive header at %ld", 494 this->filename_.c_str(), static_cast<long>(off)); 495 else 496 go_error_at(this->location_, "%s: unexpected EOF at %ld", 497 this->filename_.c_str(), static_cast<long>(off)); 498 } 499 off_t local_nested_off; 500 if (!this->interpret_header(&hdr, off, pname, size, &local_nested_off)) 501 return false; 502 if (nested_off != NULL) 503 *nested_off = local_nested_off; 504 505 off_t local_next_off; 506 local_next_off = off + sizeof(Archive_header); 507 if (!this->is_thin_archive_ || pname->empty() || *pname == "/") 508 local_next_off += *size; 509 if ((local_next_off & 1) != 0) 510 ++local_next_off; 511 if (local_next_off > this->filesize_) // Last member. 512 local_next_off = this->filesize_; 513 if (next_off != NULL) 514 *next_off = local_next_off; 515 return true; 516 } 517 518 // Interpret the header of HDR, the header of the archive member at 519 // file offset OFF. 520 521 bool 522 Archive_file::interpret_header(const Archive_header* hdr, off_t off, 523 std::string* pname, off_t* size, 524 off_t* nested_off) const 525 { 526 if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0) 527 { 528 go_error_at(this->location_, "%s: malformed archive header at %lu", 529 this->filename_.c_str(), static_cast<unsigned long>(off)); 530 return false; 531 } 532 533 long local_size; 534 if (!this->parse_decimal(hdr->ar_size, sizeof hdr->ar_size, &local_size)) 535 { 536 go_error_at(this->location_, "%s: malformed archive header size at %lu", 537 this->filename_.c_str(), static_cast<unsigned long>(off)); 538 return false; 539 } 540 *size = local_size; 541 542 *nested_off = 0; 543 if (hdr->ar_name[0] != '/') 544 { 545 const char* name_end = strchr(hdr->ar_name, '/'); 546 if (name_end == NULL 547 || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name)) 548 { 549 go_error_at(this->location_, 550 "%s: malformed archive header name at %lu", 551 this->filename_.c_str(), static_cast<unsigned long>(off)); 552 return false; 553 } 554 pname->assign(hdr->ar_name, name_end - hdr->ar_name); 555 } 556 else if (hdr->ar_name[1] == ' ') 557 { 558 // This is the symbol table. 559 pname->clear(); 560 } 561 else if (hdr->ar_name[1] == 'S' && hdr->ar_name[2] == 'Y' 562 && hdr->ar_name[3] == 'M' && hdr->ar_name[4] == '6' 563 && hdr->ar_name[5] == '4' && hdr->ar_name[6] == '/' 564 && hdr->ar_name[7] == ' ' 565 ) 566 { 567 // 64-bit symbol table. 568 pname->clear(); 569 } 570 else if (hdr->ar_name[1] == '/') 571 { 572 // This is the extended name table. 573 pname->assign(1, '/'); 574 } 575 else 576 { 577 char* end; 578 errno = 0; 579 long x = strtol(hdr->ar_name + 1, &end, 10); 580 long y = 0; 581 if (*end == ':') 582 y = strtol(end + 1, &end, 10); 583 if (*end != ' ' 584 || x < 0 585 || (x == LONG_MAX && errno == ERANGE) 586 || static_cast<size_t>(x) >= this->extended_names_.size()) 587 { 588 go_error_at(this->location_, "%s: bad extended name index at %lu", 589 this->filename_.c_str(), static_cast<unsigned long>(off)); 590 return false; 591 } 592 593 const char* name = this->extended_names_.data() + x; 594 const char* name_end = strchr(name, '\n'); 595 if (static_cast<size_t>(name_end - name) > this->extended_names_.size() 596 || name_end[-1] != '/') 597 { 598 go_error_at(this->location_, 599 "%s: bad extended name entry at header %lu", 600 this->filename_.c_str(), static_cast<unsigned long>(off)); 601 return false; 602 } 603 pname->assign(name, name_end - 1 - name); 604 *nested_off = y; 605 } 606 607 return true; 608 } 609 610 // Get the file and offset for an archive member. 611 612 bool 613 Archive_file::get_file_and_offset(off_t off, const std::string& hdrname, 614 off_t nested_off, int* memfd, off_t* memoff, 615 std::string* memname) 616 { 617 if (this->is_big_archive_) 618 { 619 *memfd = this->fd_; 620 *memoff = (off + sizeof(Archive_big_header) + hdrname.length() 621 + sizeof(arfmag)); 622 if ((*memoff & 1) != 0) 623 ++*memoff; 624 *memname = this->filename_ + '(' + hdrname + ')'; 625 return true; 626 } 627 else if (!this->is_thin_archive_) 628 { 629 *memfd = this->fd_; 630 *memoff = off + sizeof(Archive_header); 631 *memname = this->filename_ + '(' + hdrname + ')'; 632 return true; 633 } 634 635 std::string filename = hdrname; 636 if (!IS_ABSOLUTE_PATH(filename.c_str())) 637 { 638 const char* archive_path = this->filename_.c_str(); 639 const char* basename = lbasename(archive_path); 640 if (basename > archive_path) 641 filename.replace(0, 0, 642 this->filename_.substr(0, basename - archive_path)); 643 } 644 645 if (nested_off > 0) 646 { 647 // This is a member of a nested archive. 648 Archive_file* nfile; 649 Nested_archive_table::const_iterator p = 650 this->nested_archives_.find(filename); 651 if (p != this->nested_archives_.end()) 652 nfile = p->second; 653 else 654 { 655 int nfd = open(filename.c_str(), O_RDONLY | O_BINARY); 656 if (nfd < 0) 657 { 658 go_error_at(this->location_, "%s: cannot open nested archive %s", 659 this->filename_.c_str(), filename.c_str()); 660 return false; 661 } 662 nfile = new Archive_file(filename, nfd, this->location_); 663 if (!nfile->initialize()) 664 { 665 delete nfile; 666 return false; 667 } 668 this->nested_archives_[filename] = nfile; 669 } 670 671 std::string nname; 672 off_t nsize; 673 off_t nnested_off; 674 if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off, NULL)) 675 return false; 676 return nfile->get_file_and_offset(nested_off, nname, nnested_off, 677 memfd, memoff, memname); 678 } 679 680 // An external member of a thin archive. 681 *memfd = open(filename.c_str(), O_RDONLY | O_BINARY); 682 if (*memfd < 0) 683 { 684 go_error_at(this->location_, "%s: %m", filename.c_str()); 685 return false; 686 } 687 *memoff = 0; 688 *memname = filename; 689 return true; 690 } 691 692 // An archive member iterator. This is more-or-less copied from gold. 693 694 class Archive_iterator 695 { 696 public: 697 // The header of an archive member. This is what this iterator 698 // points to. 699 struct Header 700 { 701 // The name of the member. 702 std::string name; 703 // The file offset of the member. 704 off_t off; 705 // The file offset of a nested archive member. 706 off_t nested_off; 707 // The size of the member. 708 off_t size; 709 }; 710 711 Archive_iterator(Archive_file* afile, off_t off) 712 : afile_(afile), off_(off) 713 { this->read_next_header(); } 714 715 const Header& 716 operator*() const 717 { return this->header_; } 718 719 const Header* 720 operator->() const 721 { return &this->header_; } 722 723 Archive_iterator& 724 operator++() 725 { 726 if (this->off_ == this->afile_->filesize()) 727 return *this; 728 this->off_ = this->next_off_; 729 this->read_next_header(); 730 return *this; 731 } 732 733 Archive_iterator 734 operator++(int) 735 { 736 Archive_iterator ret = *this; 737 ++*this; 738 return ret; 739 } 740 741 bool 742 operator==(const Archive_iterator& p) const 743 { return this->off_ == p->off; } 744 745 bool 746 operator!=(const Archive_iterator& p) const 747 { return this->off_ != p->off; } 748 749 private: 750 void 751 read_next_header(); 752 753 // The underlying archive file. 754 Archive_file* afile_; 755 // The current offset in the file. 756 off_t off_; 757 // The offset of the next member. 758 off_t next_off_; 759 // The current archive header. 760 Header header_; 761 }; 762 763 // Read the next archive header. 764 765 void 766 Archive_iterator::read_next_header() 767 { 768 off_t filesize = this->afile_->filesize(); 769 while (true) 770 { 771 if (this->off_ == filesize) 772 { 773 this->header_.off = filesize; 774 return; 775 } 776 777 if (!this->afile_->read_header(this->off_, &this->header_.name, 778 &this->header_.size, 779 &this->header_.nested_off, 780 &this->next_off_)) 781 { 782 this->header_.off = filesize; 783 this->off_ = filesize; 784 return; 785 } 786 this->header_.off = this->off_; 787 788 // Skip special members. 789 if (!this->header_.name.empty() && this->header_.name != "/") 790 return; 791 792 this->off_ = this->next_off_; 793 } 794 } 795 796 // Initial iterator. 797 798 Archive_iterator 799 archive_begin(Archive_file* afile) 800 { 801 return Archive_iterator(afile, afile->first_member_offset()); 802 } 803 804 // Final iterator. 805 806 Archive_iterator 807 archive_end(Archive_file* afile) 808 { 809 return Archive_iterator(afile, afile->filesize()); 810 } 811 812 // A type of Import_stream which concatenates other Import_streams 813 // together. 814 815 class Stream_concatenate : public Import::Stream 816 { 817 public: 818 Stream_concatenate() 819 : inputs_() 820 { } 821 822 // Add a new stream. 823 void 824 add(Import::Stream* is) 825 { this->inputs_.push_back(is); } 826 827 protected: 828 bool 829 do_peek(size_t, const char**); 830 831 void 832 do_advance(size_t); 833 834 private: 835 std::list<Import::Stream*> inputs_; 836 }; 837 838 // Peek ahead. 839 840 bool 841 Stream_concatenate::do_peek(size_t length, const char** bytes) 842 { 843 while (true) 844 { 845 if (this->inputs_.empty()) 846 return false; 847 if (this->inputs_.front()->peek(length, bytes)) 848 return true; 849 delete this->inputs_.front(); 850 this->inputs_.pop_front(); 851 } 852 } 853 854 // Advance. 855 856 void 857 Stream_concatenate::do_advance(size_t skip) 858 { 859 while (true) 860 { 861 if (this->inputs_.empty()) 862 return; 863 if (!this->inputs_.front()->at_eof()) 864 { 865 // We just assume that this will do the right thing. It 866 // should be OK since we should never want to skip past 867 // multiple streams. 868 this->inputs_.front()->advance(skip); 869 return; 870 } 871 delete this->inputs_.front(); 872 this->inputs_.pop_front(); 873 } 874 } 875 876 // Import data from an archive. We walk through the archive and 877 // import data from each member. 878 879 Import::Stream* 880 Import::find_archive_export_data(const std::string& filename, int fd, 881 Location location) 882 { 883 Archive_file afile(filename, fd, location); 884 if (!afile.initialize()) 885 return NULL; 886 887 Stream_concatenate* ret = new Stream_concatenate; 888 889 bool any_data = false; 890 bool any_members = false; 891 Archive_iterator pend = archive_end(&afile); 892 for (Archive_iterator p = archive_begin(&afile); p != pend; p++) 893 { 894 any_members = true; 895 int member_fd; 896 off_t member_off; 897 std::string member_name; 898 if (!afile.get_file_and_offset(p->off, p->name, p->nested_off, 899 &member_fd, &member_off, &member_name)) 900 return NULL; 901 902 Import::Stream* is = Import::find_object_export_data(member_name, 903 member_fd, 904 member_off, 905 location); 906 if (is != NULL) 907 { 908 ret->add(is); 909 any_data = true; 910 } 911 } 912 913 if (!any_members) 914 { 915 // It's normal to have an empty archive file when using gobuild. 916 return new Stream_from_string(""); 917 } 918 919 if (!any_data) 920 { 921 delete ret; 922 return NULL; 923 } 924 925 return ret; 926 }