|  | // import-archive.cc -- Go frontend read import data from an archive file. | 
|  |  | 
|  | // Copyright 2009 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "go-system.h" | 
|  |  | 
|  | #include "go-diagnostics.h" | 
|  | #include "import.h" | 
|  |  | 
|  | #ifndef O_BINARY | 
|  | #define O_BINARY 0 | 
|  | #endif | 
|  |  | 
|  | // Archive magic numbers. | 
|  |  | 
|  | static const char armag[] = | 
|  | { | 
|  | '!', '<', 'a', 'r', 'c', 'h', '>', '\n' | 
|  | }; | 
|  |  | 
|  | static const char armagt[] = | 
|  | { | 
|  | '!', '<', 't', 'h', 'i', 'n', '>', '\n' | 
|  | }; | 
|  |  | 
|  | static const char armagb[] = | 
|  | { | 
|  | '<', 'b', 'i', 'g', 'a', 'f', '>', '\n' | 
|  | }; | 
|  |  | 
|  | static const char arfmag[2] = { '`', '\n' }; | 
|  |  | 
|  | // Archive fixed length header for AIX big format. | 
|  |  | 
|  | struct Archive_fl_header | 
|  | { | 
|  | // Archive magic string. | 
|  | char fl_magic[8]; | 
|  | // Offset to member table. | 
|  | char fl_memoff[20]; | 
|  | // Offset to global symbol table. | 
|  | char fl_gstoff[20]; | 
|  | // Offset to global symbol table for 64-bit objects. | 
|  | char fl_gst64off[20]; | 
|  | // Offset to first archive member. | 
|  | char fl_fstmoff[20]; | 
|  | // Offset to last archive member. | 
|  | char fl_lstmoff[20]; | 
|  | // Offset to first member on free list. | 
|  | char fl_freeoff[20]; | 
|  | }; | 
|  |  | 
|  | // The header of an entry in an archive.  This is all readable text, | 
|  | // padded with spaces where necesary. | 
|  |  | 
|  | struct Archive_header | 
|  | { | 
|  | // The entry name. | 
|  | char ar_name[16]; | 
|  | // The file modification time. | 
|  | char ar_date[12]; | 
|  | // The user's UID in decimal. | 
|  | char ar_uid[6]; | 
|  | // The user's GID in decimal. | 
|  | char ar_gid[6]; | 
|  | // The file mode in octal. | 
|  | char ar_mode[8]; | 
|  | // The file size in decimal. | 
|  | char ar_size[10]; | 
|  | // The final magic code. | 
|  | char ar_fmag[2]; | 
|  | }; | 
|  |  | 
|  | // The header of an entry in an AIX big archive. | 
|  | // This is followed by ar_namlen bytes + 2 bytes for arfmag. | 
|  |  | 
|  | struct Archive_big_header | 
|  | { | 
|  | // The file size in decimal. | 
|  | char ar_size[20]; | 
|  | // The next member offset in decimal. | 
|  | char ar_nxtmem[20]; | 
|  | // The previous member offset in decimal. | 
|  | char ar_prvmem[20]; | 
|  | // The file modification time in decimal. | 
|  | char ar_date[12]; | 
|  | // The user's UID in decimal. | 
|  | char ar_uid[12]; | 
|  | // The user's GID in decimal. | 
|  | char ar_gid[12]; | 
|  | // The file mode in octal. | 
|  | char ar_mode[12]; | 
|  | // The file name length in decimal. | 
|  | char ar_namlen[4]; | 
|  | }; | 
|  |  | 
|  | // The functions in this file extract Go export data from an archive. | 
|  |  | 
|  | const int Import::archive_magic_len; | 
|  |  | 
|  | // Return true if BYTES, which are from the start of the file, are an | 
|  | // archive magic number. | 
|  |  | 
|  | bool | 
|  | Import::is_archive_magic(const char* bytes) | 
|  | { | 
|  | return (memcmp(bytes, armag, Import::archive_magic_len) == 0 | 
|  | || memcmp(bytes, armagt, Import::archive_magic_len) == 0 | 
|  | || memcmp(bytes, armagb, Import::archive_magic_len) == 0); | 
|  | } | 
|  |  | 
|  | // An object used to read an archive file. | 
|  |  | 
|  | class Archive_file | 
|  | { | 
|  | public: | 
|  | Archive_file(const std::string& filename, int fd, Location location) | 
|  | : filename_(filename), fd_(fd), filesize_(-1), first_member_offset_(0), | 
|  | extended_names_(), is_thin_archive_(false), is_big_archive_(false), | 
|  | location_(location), nested_archives_() | 
|  | { } | 
|  |  | 
|  | // Initialize. | 
|  | bool | 
|  | initialize(); | 
|  |  | 
|  | // Return the file name. | 
|  | const std::string& | 
|  | filename() const | 
|  | { return this->filename_; } | 
|  |  | 
|  | // Get the file size. | 
|  | off_t | 
|  | filesize() const | 
|  | { return this->filesize_; } | 
|  |  | 
|  | // Return the offset of the first member. | 
|  | off_t | 
|  | first_member_offset() const | 
|  | { return this->first_member_offset_; } | 
|  |  | 
|  | // Return whether this is a thin archive. | 
|  | bool | 
|  | is_thin_archive() const | 
|  | { return this->is_thin_archive_; } | 
|  |  | 
|  | // Return whether this is a big archive. | 
|  | bool | 
|  | is_big_archive() const | 
|  | { return this->is_big_archive_; } | 
|  |  | 
|  | // Return the location of the import statement. | 
|  | Location | 
|  | location() const | 
|  | { return this->location_; } | 
|  |  | 
|  | // Read bytes. | 
|  | bool | 
|  | read(off_t offset, off_t size, char*); | 
|  |  | 
|  | // Parse a decimal in readable text. | 
|  | bool | 
|  | parse_decimal(const char* str, off_t size, long* res) const; | 
|  |  | 
|  | // Read the archive header at OFF, setting *PNAME, *SIZE, | 
|  | // *NESTED_OFF and *NEXT_OFF. | 
|  | bool | 
|  | read_header(off_t off, std::string* pname, off_t* size, off_t* nested_off, | 
|  | off_t* next_off); | 
|  |  | 
|  | // Interpret the header of HDR, the header of the archive member at | 
|  | // file offset OFF.  Return whether it succeeded.  Set *SIZE to the | 
|  | // size of the member.  Set *PNAME to the name of the member.  Set | 
|  | // *NESTED_OFF to the offset in a nested archive. | 
|  | bool | 
|  | interpret_header(const Archive_header* hdr, off_t off, | 
|  | std::string* pname, off_t* size, off_t* nested_off) const; | 
|  |  | 
|  | // Get the file and offset for an archive member. | 
|  | bool | 
|  | get_file_and_offset(off_t off, const std::string& hdrname, | 
|  | off_t nested_off, int* memfd, off_t* memoff, | 
|  | std::string* memname); | 
|  |  | 
|  | private: | 
|  | // Initialize a big archive (AIX) | 
|  | bool | 
|  | initialize_big_archive(); | 
|  |  | 
|  | // Initialize a normal archive | 
|  | bool | 
|  | initialize_archive(); | 
|  |  | 
|  | // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF. | 
|  | bool | 
|  | read_big_archive_header(off_t off, std::string* pname, | 
|  | off_t* size, off_t* next_off); | 
|  |  | 
|  | // Read the normal archive header at OFF, setting *PNAME, *SIZE, | 
|  | // *NESTED_OFF and *NEXT_OFF. | 
|  | bool | 
|  | read_archive_header(off_t off, std::string* pname, off_t* size, | 
|  | off_t* nested_off, off_t* next_off); | 
|  |  | 
|  | // For keeping track of open nested archives in a thin archive file. | 
|  | typedef std::map<std::string, Archive_file*> Nested_archive_table; | 
|  |  | 
|  | // The name of the file. | 
|  | std::string filename_; | 
|  | // The file descriptor. | 
|  | int fd_; | 
|  | // The file size; | 
|  | off_t filesize_; | 
|  | // The first member offset; | 
|  | off_t first_member_offset_; | 
|  | // The extended name table. | 
|  | std::string extended_names_; | 
|  | // Whether this is a thin archive. | 
|  | bool is_thin_archive_; | 
|  | // Whether this is a big archive. | 
|  | bool is_big_archive_; | 
|  | // The location of the import statements. | 
|  | Location location_; | 
|  | // Table of nested archives. | 
|  | Nested_archive_table nested_archives_; | 
|  | }; | 
|  |  | 
|  | bool | 
|  | Archive_file::initialize() | 
|  | { | 
|  | struct stat st; | 
|  | if (fstat(this->fd_, &st) < 0) | 
|  | { | 
|  | go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | 
|  | return false; | 
|  | } | 
|  | this->filesize_ = st.st_size; | 
|  |  | 
|  | char buf[sizeof(armagt)]; | 
|  | if (::lseek(this->fd_, 0, SEEK_SET) < 0 | 
|  | || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt)) | 
|  | { | 
|  | go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | 
|  | return false; | 
|  | } | 
|  | if (memcmp(buf, armagt, sizeof(armagt)) == 0) | 
|  | this->is_thin_archive_ = true; | 
|  | else if (memcmp(buf, armagb, sizeof(armagb)) == 0) | 
|  | this->is_big_archive_ = true; | 
|  |  | 
|  | if (this->is_big_archive_) | 
|  | return this->initialize_big_archive(); | 
|  | else | 
|  | return this->initialize_archive(); | 
|  | } | 
|  |  | 
|  | // Initialize a big archive (AIX). | 
|  |  | 
|  | bool | 
|  | Archive_file::initialize_big_archive() | 
|  | { | 
|  | Archive_fl_header flhdr; | 
|  |  | 
|  | // Read the fixed length header. | 
|  | if (::lseek(this->fd_, 0, SEEK_SET) < 0 | 
|  | || ::read(this->fd_, &flhdr, sizeof(flhdr)) != sizeof(flhdr)) | 
|  | { | 
|  | go_error_at(this->location_, "%s: could not read archive header", | 
|  | this->filename_.c_str()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Parse offset of the first member. | 
|  | long off; | 
|  | if (!this->parse_decimal(flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff), &off)) | 
|  | { | 
|  | char* buf = new char[sizeof(flhdr.fl_fstmoff) + 1]; | 
|  | memcpy(buf, flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff)); | 
|  | go_error_at(this->location_, | 
|  | ("%s: malformed first member offset in archive header" | 
|  | " (expected decimal, got %s)"), | 
|  | this->filename_.c_str(), buf); | 
|  | delete[] buf; | 
|  | return false; | 
|  | } | 
|  | if (off == 0) // Empty archive. | 
|  | this->first_member_offset_ = this->filesize_; | 
|  | else | 
|  | this->first_member_offset_ = off; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Initialize a normal archive. | 
|  |  | 
|  | bool | 
|  | Archive_file::initialize_archive() | 
|  | { | 
|  | this->first_member_offset_ = sizeof(armag); | 
|  | if (this->first_member_offset_ == this->filesize_) | 
|  | { | 
|  | // Empty archive. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Look for the extended name table. | 
|  | std::string filename; | 
|  | off_t size; | 
|  | off_t next_off; | 
|  | if (!this->read_header(this->first_member_offset_, &filename, | 
|  | &size, NULL, &next_off)) | 
|  | return false; | 
|  | if (filename.empty()) | 
|  | { | 
|  | // We found the symbol table. | 
|  | if (!this->read_header(next_off, &filename, &size, NULL, NULL)) | 
|  | filename.clear(); | 
|  | } | 
|  | if (filename == "/") | 
|  | { | 
|  | char* rdbuf = new char[size]; | 
|  | if (::read(this->fd_, rdbuf, size) != size) | 
|  | { | 
|  | go_error_at(this->location_, "%s: could not read extended names", | 
|  | filename.c_str()); | 
|  | delete[] rdbuf; | 
|  | return false; | 
|  | } | 
|  | this->extended_names_.assign(rdbuf, size); | 
|  | delete[] rdbuf; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Read bytes from the file. | 
|  |  | 
|  | bool | 
|  | Archive_file::read(off_t offset, off_t size, char* buf) | 
|  | { | 
|  | if (::lseek(this->fd_, offset, SEEK_SET) < 0 | 
|  | || ::read(this->fd_, buf, size) != size) | 
|  | { | 
|  | go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Parse a decimal in readable text. | 
|  |  | 
|  | bool | 
|  | Archive_file::parse_decimal(const char* str, off_t size, long* res) const | 
|  | { | 
|  | char* buf = new char[size + 1]; | 
|  | memcpy(buf, str, size); | 
|  | char* ps = buf + size; | 
|  | while (ps > buf && ps[-1] == ' ') | 
|  | --ps; | 
|  | *ps = '\0'; | 
|  |  | 
|  | errno = 0; | 
|  | char* end; | 
|  | *res = strtol(buf, &end, 10); | 
|  | if (*end != '\0' | 
|  | || *res < 0 | 
|  | || (*res == LONG_MAX && errno == ERANGE)) | 
|  | { | 
|  | delete[] buf; | 
|  | return false; | 
|  | } | 
|  | delete[] buf; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Read the header at OFF.  Set *PNAME to the name, *SIZE to the size, | 
|  | // *NESTED_OFF to the nested offset, and *NEXT_OFF to the next member offset. | 
|  |  | 
|  | bool | 
|  | Archive_file::read_header(off_t off, std::string* pname, off_t* size, | 
|  | off_t* nested_off, off_t* next_off) | 
|  | { | 
|  | if (::lseek(this->fd_, off, SEEK_SET) < 0) | 
|  | { | 
|  | go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | 
|  | return false; | 
|  | } | 
|  | if (this->is_big_archive_) | 
|  | return this->read_big_archive_header(off, pname, size, next_off); | 
|  | else | 
|  | return this->read_archive_header(off, pname, size, nested_off, next_off); | 
|  | } | 
|  |  | 
|  | // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF. | 
|  |  | 
|  | bool | 
|  | Archive_file::read_big_archive_header(off_t off, std::string* pname, | 
|  | off_t* size, off_t* next_off) | 
|  | { | 
|  | Archive_big_header hdr; | 
|  | ssize_t got; | 
|  |  | 
|  | got = ::read(this->fd_, &hdr, sizeof hdr); | 
|  | if (got != sizeof hdr) | 
|  | { | 
|  | if (got < 0) | 
|  | go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | 
|  | else if (got > 0) | 
|  | go_error_at(this->location_, "%s: short entry header at %ld", | 
|  | this->filename_.c_str(), static_cast<long>(off)); | 
|  | else | 
|  | go_error_at(this->location_, "%s: unexpected EOF at %ld", | 
|  | this->filename_.c_str(), static_cast<long>(off)); | 
|  | } | 
|  |  | 
|  | long local_size; | 
|  | if (!this->parse_decimal(hdr.ar_size, sizeof(hdr.ar_size), &local_size)) | 
|  | { | 
|  | char* buf = new char[sizeof(hdr.ar_size) + 1]; | 
|  | memcpy(buf, hdr.ar_size, sizeof(hdr.ar_size)); | 
|  | go_error_at(this->location_, | 
|  | ("%s: malformed ar_size in entry header at %ld" | 
|  | " (expected decimal, got %s)"), | 
|  | this->filename_.c_str(), static_cast<long>(off), buf); | 
|  | delete[] buf; | 
|  | return false; | 
|  | } | 
|  | *size = local_size; | 
|  |  | 
|  | long namlen; | 
|  | if (!this->parse_decimal(hdr.ar_namlen, sizeof(hdr.ar_namlen), &namlen)) | 
|  | { | 
|  | char* buf = new char[sizeof(hdr.ar_namlen) + 1]; | 
|  | memcpy(buf, hdr.ar_namlen, sizeof(hdr.ar_namlen)); | 
|  | go_error_at(this->location_, | 
|  | ("%s: malformed ar_namlen in entry header at %ld" | 
|  | " (expected decimal, got %s)"), | 
|  | this->filename_.c_str(), static_cast<long>(off), buf); | 
|  | delete[] buf; | 
|  | return false; | 
|  | } | 
|  | // Read member name following member header. | 
|  | char* rdbuf = new char[namlen]; | 
|  | got = ::read(this->fd_, rdbuf, namlen); | 
|  | if (got != namlen) | 
|  | { | 
|  | go_error_at(this->location_, | 
|  | "%s: malformed member name in entry header at %ld", | 
|  | this->filename_.c_str(), static_cast<long>(off)); | 
|  | delete[] rdbuf; | 
|  | return false; | 
|  | } | 
|  | pname->assign(rdbuf, namlen); | 
|  | delete[] rdbuf; | 
|  |  | 
|  | long local_next_off; | 
|  | if (!this->parse_decimal(hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem), &local_next_off)) | 
|  | { | 
|  | char* buf = new char[sizeof(hdr.ar_nxtmem) + 1]; | 
|  | memcpy(buf, hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem)); | 
|  | go_error_at(this->location_, | 
|  | ("%s: malformed ar_nxtmem in entry header at %ld" | 
|  | " (expected decimal, got %s)"), | 
|  | this->filename_.c_str(), static_cast<long>(off), buf); | 
|  | delete[] buf; | 
|  | return false; | 
|  | } | 
|  | if (next_off != NULL) | 
|  | { | 
|  | if (local_next_off == 0) // Last member. | 
|  | *next_off = this->filesize_; | 
|  | else | 
|  | *next_off = local_next_off; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Read the normal archive header at OFF, setting *PNAME, *SIZE, | 
|  | // *NESTED_OFF and *NEXT_OFF. | 
|  |  | 
|  | bool | 
|  | Archive_file::read_archive_header(off_t off, std::string* pname, off_t* size, | 
|  | off_t* nested_off, off_t* next_off) | 
|  | { | 
|  | Archive_header hdr; | 
|  | ssize_t got = ::read(this->fd_, &hdr, sizeof hdr); | 
|  | if (got != sizeof hdr) | 
|  | { | 
|  | if (got < 0) | 
|  | go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | 
|  | else if (got > 0) | 
|  | go_error_at(this->location_, "%s: short archive header at %ld", | 
|  | this->filename_.c_str(), static_cast<long>(off)); | 
|  | else | 
|  | go_error_at(this->location_, "%s: unexpected EOF at %ld", | 
|  | this->filename_.c_str(), static_cast<long>(off)); | 
|  | } | 
|  | off_t local_nested_off; | 
|  | if (!this->interpret_header(&hdr, off, pname, size, &local_nested_off)) | 
|  | return false; | 
|  | if (nested_off != NULL) | 
|  | *nested_off = local_nested_off; | 
|  |  | 
|  | off_t local_next_off; | 
|  | local_next_off = off + sizeof(Archive_header); | 
|  | if (!this->is_thin_archive_ || pname->empty() || *pname == "/") | 
|  | local_next_off += *size; | 
|  | if ((local_next_off & 1) != 0) | 
|  | ++local_next_off; | 
|  | if (local_next_off > this->filesize_) // Last member. | 
|  | local_next_off = this->filesize_; | 
|  | if (next_off != NULL) | 
|  | *next_off = local_next_off; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Interpret the header of HDR, the header of the archive member at | 
|  | // file offset OFF. | 
|  |  | 
|  | bool | 
|  | Archive_file::interpret_header(const Archive_header* hdr, off_t off, | 
|  | std::string* pname, off_t* size, | 
|  | off_t* nested_off) const | 
|  | { | 
|  | if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0) | 
|  | { | 
|  | go_error_at(this->location_, "%s: malformed archive header at %lu", | 
|  | this->filename_.c_str(), static_cast<unsigned long>(off)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | long local_size; | 
|  | if (!this->parse_decimal(hdr->ar_size, sizeof hdr->ar_size, &local_size)) | 
|  | { | 
|  | go_error_at(this->location_, "%s: malformed archive header size at %lu", | 
|  | this->filename_.c_str(), static_cast<unsigned long>(off)); | 
|  | return false; | 
|  | } | 
|  | *size = local_size; | 
|  |  | 
|  | *nested_off = 0; | 
|  | if (hdr->ar_name[0] != '/') | 
|  | { | 
|  | const char* name_end = strchr(hdr->ar_name, '/'); | 
|  | if (name_end == NULL | 
|  | || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name)) | 
|  | { | 
|  | go_error_at(this->location_, | 
|  | "%s: malformed archive header name at %lu", | 
|  | this->filename_.c_str(), static_cast<unsigned long>(off)); | 
|  | return false; | 
|  | } | 
|  | pname->assign(hdr->ar_name, name_end - hdr->ar_name); | 
|  | } | 
|  | else if (hdr->ar_name[1] == ' ') | 
|  | { | 
|  | // This is the symbol table. | 
|  | pname->clear(); | 
|  | } | 
|  | else if (hdr->ar_name[1] == 'S' && hdr->ar_name[2] == 'Y' | 
|  | && hdr->ar_name[3] == 'M' && hdr->ar_name[4] == '6' | 
|  | && hdr->ar_name[5] == '4' && hdr->ar_name[6] == '/' | 
|  | && hdr->ar_name[7] == ' ' | 
|  | ) | 
|  | { | 
|  | // 64-bit symbol table. | 
|  | pname->clear(); | 
|  | } | 
|  | else if (hdr->ar_name[1] == '/') | 
|  | { | 
|  | // This is the extended name table. | 
|  | pname->assign(1, '/'); | 
|  | } | 
|  | else | 
|  | { | 
|  | char* end; | 
|  | errno = 0; | 
|  | long x = strtol(hdr->ar_name + 1, &end, 10); | 
|  | long y = 0; | 
|  | if (*end == ':') | 
|  | y = strtol(end + 1, &end, 10); | 
|  | if (*end != ' ' | 
|  | || x < 0 | 
|  | || (x == LONG_MAX && errno == ERANGE) | 
|  | || static_cast<size_t>(x) >= this->extended_names_.size()) | 
|  | { | 
|  | go_error_at(this->location_, "%s: bad extended name index at %lu", | 
|  | this->filename_.c_str(), static_cast<unsigned long>(off)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const char* name = this->extended_names_.data() + x; | 
|  | const char* name_end = strchr(name, '\n'); | 
|  | if (static_cast<size_t>(name_end - name) > this->extended_names_.size() | 
|  | || name_end[-1] != '/') | 
|  | { | 
|  | go_error_at(this->location_, | 
|  | "%s: bad extended name entry at header %lu", | 
|  | this->filename_.c_str(), static_cast<unsigned long>(off)); | 
|  | return false; | 
|  | } | 
|  | pname->assign(name, name_end - 1 - name); | 
|  | *nested_off = y; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Get the file and offset for an archive member. | 
|  |  | 
|  | bool | 
|  | Archive_file::get_file_and_offset(off_t off, const std::string& hdrname, | 
|  | off_t nested_off, int* memfd, off_t* memoff, | 
|  | std::string* memname) | 
|  | { | 
|  | if (this->is_big_archive_) | 
|  | { | 
|  | *memfd = this->fd_; | 
|  | *memoff = (off + sizeof(Archive_big_header) + hdrname.length() | 
|  | + sizeof(arfmag)); | 
|  | if ((*memoff & 1) != 0) | 
|  | ++*memoff; | 
|  | *memname = this->filename_ + '(' + hdrname + ')'; | 
|  | return true; | 
|  | } | 
|  | else if (!this->is_thin_archive_) | 
|  | { | 
|  | *memfd = this->fd_; | 
|  | *memoff = off + sizeof(Archive_header); | 
|  | *memname = this->filename_ + '(' + hdrname + ')'; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | std::string filename = hdrname; | 
|  | if (!IS_ABSOLUTE_PATH(filename.c_str())) | 
|  | { | 
|  | const char* archive_path = this->filename_.c_str(); | 
|  | const char* basename = lbasename(archive_path); | 
|  | if (basename > archive_path) | 
|  | filename.replace(0, 0, | 
|  | this->filename_.substr(0, basename - archive_path)); | 
|  | } | 
|  |  | 
|  | if (nested_off > 0) | 
|  | { | 
|  | // This is a member of a nested archive. | 
|  | Archive_file* nfile; | 
|  | Nested_archive_table::const_iterator p = | 
|  | this->nested_archives_.find(filename); | 
|  | if (p != this->nested_archives_.end()) | 
|  | nfile = p->second; | 
|  | else | 
|  | { | 
|  | int nfd = open(filename.c_str(), O_RDONLY | O_BINARY); | 
|  | if (nfd < 0) | 
|  | { | 
|  | go_error_at(this->location_, "%s: can't open nested archive %s", | 
|  | this->filename_.c_str(), filename.c_str()); | 
|  | return false; | 
|  | } | 
|  | nfile = new Archive_file(filename, nfd, this->location_); | 
|  | if (!nfile->initialize()) | 
|  | { | 
|  | delete nfile; | 
|  | return false; | 
|  | } | 
|  | this->nested_archives_[filename] = nfile; | 
|  | } | 
|  |  | 
|  | std::string nname; | 
|  | off_t nsize; | 
|  | off_t nnested_off; | 
|  | if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off, NULL)) | 
|  | return false; | 
|  | return nfile->get_file_and_offset(nested_off, nname, nnested_off, | 
|  | memfd, memoff, memname); | 
|  | } | 
|  |  | 
|  | // An external member of a thin archive. | 
|  | *memfd = open(filename.c_str(), O_RDONLY | O_BINARY); | 
|  | if (*memfd < 0) | 
|  | { | 
|  | go_error_at(this->location_, "%s: %m", filename.c_str()); | 
|  | return false; | 
|  | } | 
|  | *memoff = 0; | 
|  | *memname = filename; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // An archive member iterator.  This is more-or-less copied from gold. | 
|  |  | 
|  | class Archive_iterator | 
|  | { | 
|  | public: | 
|  | // The header of an archive member.  This is what this iterator | 
|  | // points to. | 
|  | struct Header | 
|  | { | 
|  | // The name of the member. | 
|  | std::string name; | 
|  | // The file offset of the member. | 
|  | off_t off; | 
|  | // The file offset of a nested archive member. | 
|  | off_t nested_off; | 
|  | // The size of the member. | 
|  | off_t size; | 
|  | }; | 
|  |  | 
|  | Archive_iterator(Archive_file* afile, off_t off) | 
|  | : afile_(afile), off_(off) | 
|  | { this->read_next_header(); } | 
|  |  | 
|  | const Header& | 
|  | operator*() const | 
|  | { return this->header_; } | 
|  |  | 
|  | const Header* | 
|  | operator->() const | 
|  | { return &this->header_; } | 
|  |  | 
|  | Archive_iterator& | 
|  | operator++() | 
|  | { | 
|  | if (this->off_ == this->afile_->filesize()) | 
|  | return *this; | 
|  | this->off_ = this->next_off_; | 
|  | this->read_next_header(); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | Archive_iterator | 
|  | operator++(int) | 
|  | { | 
|  | Archive_iterator ret = *this; | 
|  | ++*this; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | bool | 
|  | operator==(const Archive_iterator& p) const | 
|  | { return this->off_ == p->off; } | 
|  |  | 
|  | bool | 
|  | operator!=(const Archive_iterator& p) const | 
|  | { return this->off_ != p->off; } | 
|  |  | 
|  | private: | 
|  | void | 
|  | read_next_header(); | 
|  |  | 
|  | // The underlying archive file. | 
|  | Archive_file* afile_; | 
|  | // The current offset in the file. | 
|  | off_t off_; | 
|  | // The offset of the next member. | 
|  | off_t next_off_; | 
|  | // The current archive header. | 
|  | Header header_; | 
|  | }; | 
|  |  | 
|  | // Read the next archive header. | 
|  |  | 
|  | void | 
|  | Archive_iterator::read_next_header() | 
|  | { | 
|  | off_t filesize = this->afile_->filesize(); | 
|  | while (true) | 
|  | { | 
|  | if (this->off_ == filesize) | 
|  | { | 
|  | this->header_.off = filesize; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!this->afile_->read_header(this->off_, &this->header_.name, | 
|  | &this->header_.size, | 
|  | &this->header_.nested_off, | 
|  | &this->next_off_)) | 
|  | { | 
|  | this->header_.off = filesize; | 
|  | this->off_ = filesize; | 
|  | return; | 
|  | } | 
|  | this->header_.off = this->off_; | 
|  |  | 
|  | // Skip special members. | 
|  | if (!this->header_.name.empty() && this->header_.name != "/") | 
|  | return; | 
|  |  | 
|  | this->off_ = this->next_off_; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Initial iterator. | 
|  |  | 
|  | Archive_iterator | 
|  | archive_begin(Archive_file* afile) | 
|  | { | 
|  | return Archive_iterator(afile, afile->first_member_offset()); | 
|  | } | 
|  |  | 
|  | // Final iterator. | 
|  |  | 
|  | Archive_iterator | 
|  | archive_end(Archive_file* afile) | 
|  | { | 
|  | return Archive_iterator(afile, afile->filesize()); | 
|  | } | 
|  |  | 
|  | // A type of Import_stream which concatenates other Import_streams | 
|  | // together. | 
|  |  | 
|  | class Stream_concatenate : public Import::Stream | 
|  | { | 
|  | public: | 
|  | Stream_concatenate() | 
|  | : inputs_() | 
|  | { } | 
|  |  | 
|  | // Add a new stream. | 
|  | void | 
|  | add(Import::Stream* is) | 
|  | { this->inputs_.push_back(is); } | 
|  |  | 
|  | protected: | 
|  | bool | 
|  | do_peek(size_t, const char**); | 
|  |  | 
|  | void | 
|  | do_advance(size_t); | 
|  |  | 
|  | private: | 
|  | std::list<Import::Stream*> inputs_; | 
|  | }; | 
|  |  | 
|  | // Peek ahead. | 
|  |  | 
|  | bool | 
|  | Stream_concatenate::do_peek(size_t length, const char** bytes) | 
|  | { | 
|  | while (true) | 
|  | { | 
|  | if (this->inputs_.empty()) | 
|  | return false; | 
|  | if (this->inputs_.front()->peek(length, bytes)) | 
|  | return true; | 
|  | delete this->inputs_.front(); | 
|  | this->inputs_.pop_front(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Advance. | 
|  |  | 
|  | void | 
|  | Stream_concatenate::do_advance(size_t skip) | 
|  | { | 
|  | while (true) | 
|  | { | 
|  | if (this->inputs_.empty()) | 
|  | return; | 
|  | if (!this->inputs_.front()->at_eof()) | 
|  | { | 
|  | // We just assume that this will do the right thing.  It | 
|  | // should be OK since we should never want to skip past | 
|  | // multiple streams. | 
|  | this->inputs_.front()->advance(skip); | 
|  | return; | 
|  | } | 
|  | delete this->inputs_.front(); | 
|  | this->inputs_.pop_front(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Import data from an archive.  We walk through the archive and | 
|  | // import data from each member. | 
|  |  | 
|  | Import::Stream* | 
|  | Import::find_archive_export_data(const std::string& filename, int fd, | 
|  | Location location) | 
|  | { | 
|  | Archive_file afile(filename, fd, location); | 
|  | if (!afile.initialize()) | 
|  | return NULL; | 
|  |  | 
|  | Stream_concatenate* ret = new Stream_concatenate; | 
|  |  | 
|  | bool any_data = false; | 
|  | bool any_members = false; | 
|  | Archive_iterator pend = archive_end(&afile); | 
|  | for (Archive_iterator p = archive_begin(&afile); p != pend; p++) | 
|  | { | 
|  | any_members = true; | 
|  | int member_fd; | 
|  | off_t member_off; | 
|  | std::string member_name; | 
|  | if (!afile.get_file_and_offset(p->off, p->name, p->nested_off, | 
|  | &member_fd, &member_off, &member_name)) | 
|  | return NULL; | 
|  |  | 
|  | Import::Stream* is = Import::find_object_export_data(member_name, | 
|  | member_fd, | 
|  | member_off, | 
|  | location); | 
|  | if (is != NULL) | 
|  | { | 
|  | ret->add(is); | 
|  | any_data = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!any_members) | 
|  | { | 
|  | // It's normal to have an empty archive file when using gobuild. | 
|  | return new Stream_from_string(""); | 
|  | } | 
|  |  | 
|  | if (!any_data) | 
|  | { | 
|  | delete ret; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } |