LCOV - code coverage report
Current view: top level - gcc/go/gofrontend - import-archive.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 46.7 % 334 156
Test Date: 2026-02-28 14:20:25 Functions: 84.2 % 19 16
Legend: Lines:     hit not hit

            Line data    Source code
       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          702 : Import::is_archive_magic(const char* bytes)
     108              : {
     109          702 :   return (memcmp(bytes, armag, Import::archive_magic_len) == 0
     110            0 :           || memcmp(bytes, armagt, Import::archive_magic_len) == 0
     111          702 :           || 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          702 :   Archive_file(const std::string& filename, int fd, Location location)
     120          702 :     : filename_(filename), fd_(fd), filesize_(-1), first_member_offset_(0),
     121          702 :       extended_names_(), is_thin_archive_(false), is_big_archive_(false),
     122          702 :       location_(location), nested_archives_()
     123          702 :   { }
     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         5154 :   filesize() const
     137         5154 :   { return this->filesize_; }
     138              : 
     139              :   // Return the offset of the first member.
     140              :   off_t
     141          702 :   first_member_offset() const
     142          702 :   { 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          702 : Archive_file::initialize()
     232              : {
     233          702 :   struct stat st;
     234          702 :   if (fstat(this->fd_, &st) < 0)
     235              :     {
     236            0 :       go_error_at(this->location_, "%s: %m", this->filename_.c_str());
     237            0 :       return false;
     238              :     }
     239          702 :   this->filesize_ = st.st_size;
     240              : 
     241          702 :   char buf[sizeof(armagt)];
     242          702 :   if (::lseek(this->fd_, 0, SEEK_SET) < 0
     243          702 :       || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt))
     244              :     {
     245            0 :       go_error_at(this->location_, "%s: %m", this->filename_.c_str());
     246            0 :       return false;
     247              :     }
     248          702 :   if (memcmp(buf, armagt, sizeof(armagt)) == 0)
     249            0 :     this->is_thin_archive_ = true;
     250          702 :   else if (memcmp(buf, armagb, sizeof(armagb)) == 0)
     251            0 :     this->is_big_archive_ = true;
     252              : 
     253          702 :   if (this->is_big_archive_)
     254            0 :     return this->initialize_big_archive();
     255              :   else
     256          702 :     return this->initialize_archive();
     257              : }
     258              : 
     259              : // Initialize a big archive (AIX).
     260              : 
     261              : bool
     262            0 : Archive_file::initialize_big_archive()
     263              : {
     264            0 :   Archive_fl_header flhdr;
     265              : 
     266              :   // Read the fixed length header.
     267            0 :   if (::lseek(this->fd_, 0, SEEK_SET) < 0
     268            0 :       || ::read(this->fd_, &flhdr, sizeof(flhdr)) != sizeof(flhdr))
     269              :     {
     270            0 :       go_error_at(this->location_, "%s: could not read archive header",
     271              :                   this->filename_.c_str());
     272            0 :       return false;
     273              :     }
     274              : 
     275              :   // Parse offset of the first member.
     276            0 :   long off;
     277            0 :   if (!this->parse_decimal(flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff), &off))
     278              :     {
     279            0 :       char* buf = new char[sizeof(flhdr.fl_fstmoff) + 1];
     280            0 :       memcpy(buf, flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff));
     281            0 :       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            0 :       delete[] buf;
     286            0 :       return false;
     287              :     }
     288            0 :   if (off == 0) // Empty archive.
     289            0 :     this->first_member_offset_ = this->filesize_;
     290              :   else
     291            0 :     this->first_member_offset_ = off;
     292              :   return true;
     293              : }
     294              : 
     295              : // Initialize a normal archive.
     296              : 
     297              : bool
     298          702 : Archive_file::initialize_archive()
     299              : {
     300          702 :   this->first_member_offset_ = sizeof(armag);
     301          702 :   if (this->first_member_offset_ == this->filesize_)
     302              :     {
     303              :       // Empty archive.
     304              :       return true;
     305              :     }
     306              : 
     307              :   // Look for the extended name table.
     308          702 :   std::string filename;
     309          702 :   off_t size;
     310          702 :   off_t next_off;
     311          702 :   if (!this->read_header(this->first_member_offset_, &filename,
     312              :                          &size, NULL, &next_off))
     313              :     return false;
     314          702 :   if (filename.empty())
     315              :     {
     316              :       // We found the symbol table.
     317          702 :       if (!this->read_header(next_off, &filename, &size, NULL, NULL))
     318            0 :         filename.clear();
     319              :     }
     320          702 :   if (filename == "/")
     321              :     {
     322            0 :       char* rdbuf = new char[size];
     323            0 :       if (::read(this->fd_, rdbuf, size) != size)
     324              :         {
     325            0 :           go_error_at(this->location_, "%s: could not read extended names",
     326              :                    filename.c_str());
     327            0 :           delete[] rdbuf;
     328            0 :           return false;
     329              :         }
     330            0 :       this->extended_names_.assign(rdbuf, size);
     331            0 :       delete[] rdbuf;
     332              :     }
     333              : 
     334              :   return true;
     335          702 : }
     336              : 
     337              : // Read bytes from the file.
     338              : 
     339              : bool
     340            0 : Archive_file::read(off_t offset, off_t size, char* buf)
     341              : {
     342            0 :   if (::lseek(this->fd_, offset, SEEK_SET) < 0
     343            0 :       || ::read(this->fd_, buf, size) != size)
     344              :     {
     345            0 :       go_error_at(this->location_, "%s: %m", this->filename_.c_str());
     346            0 :       return false;
     347              :     }
     348              :   return true;
     349              : }
     350              : 
     351              : // Parse a decimal in readable text.
     352              : 
     353              : bool
     354         3630 : Archive_file::parse_decimal(const char* str, off_t size, long* res) const
     355              : {
     356         3630 :   char* buf = new char[size + 1];
     357         3630 :   memcpy(buf, str, size);
     358         3630 :   char* ps = buf + size;
     359        23819 :   while (ps > buf && ps[-1] == ' ')
     360        20189 :     --ps;
     361         3630 :   *ps = '\0';
     362              : 
     363         3630 :   errno = 0;
     364         3630 :   char* end;
     365         3630 :   *res = strtol(buf, &end, 10);
     366         3630 :   if (*end != '\0'
     367         3630 :       || *res < 0
     368         3630 :       || (*res == LONG_MAX && errno == ERANGE))
     369              :     {
     370            0 :       delete[] buf;
     371            0 :       return false;
     372              :     }
     373         3630 :   delete[] buf;
     374         3630 :   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         3630 : Archive_file::read_header(off_t off, std::string* pname, off_t* size,
     382              :                           off_t* nested_off, off_t* next_off)
     383              : {
     384         3630 :   if (::lseek(this->fd_, off, SEEK_SET) < 0)
     385              :     {
     386            0 :       go_error_at(this->location_, "%s: %m", this->filename_.c_str());
     387            0 :       return false;
     388              :     }
     389         3630 :   if (this->is_big_archive_)
     390            0 :     return this->read_big_archive_header(off, pname, size, next_off);
     391              :   else
     392         3630 :     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            0 : Archive_file::read_big_archive_header(off_t off, std::string* pname,
     399              :                                       off_t* size, off_t* next_off)
     400              : {
     401            0 :   Archive_big_header hdr;
     402            0 :   ssize_t got;
     403              : 
     404            0 :   got = ::read(this->fd_, &hdr, sizeof hdr);
     405            0 :   if (got != sizeof hdr)
     406              :     {
     407            0 :       if (got < 0)
     408            0 :         go_error_at(this->location_, "%s: %m", this->filename_.c_str());
     409            0 :       else if (got > 0)
     410            0 :         go_error_at(this->location_, "%s: short entry header at %ld",
     411              :                     this->filename_.c_str(), static_cast<long>(off));
     412              :       else
     413            0 :         go_error_at(this->location_, "%s: unexpected EOF at %ld",
     414              :                     this->filename_.c_str(), static_cast<long>(off));
     415              :     }
     416              : 
     417            0 :   long local_size;
     418            0 :   if (!this->parse_decimal(hdr.ar_size, sizeof(hdr.ar_size), &local_size))
     419              :     {
     420            0 :       char* buf = new char[sizeof(hdr.ar_size) + 1];
     421            0 :       memcpy(buf, hdr.ar_size, sizeof(hdr.ar_size));
     422            0 :       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            0 :       delete[] buf;
     427            0 :       return false;
     428              :     }
     429            0 :   *size = local_size;
     430              : 
     431            0 :   long namlen;
     432            0 :   if (!this->parse_decimal(hdr.ar_namlen, sizeof(hdr.ar_namlen), &namlen))
     433              :     {
     434            0 :       char* buf = new char[sizeof(hdr.ar_namlen) + 1];
     435            0 :       memcpy(buf, hdr.ar_namlen, sizeof(hdr.ar_namlen));
     436            0 :       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            0 :       delete[] buf;
     441            0 :       return false;
     442              :     }
     443              :   // Read member name following member header.
     444            0 :   char* rdbuf = new char[namlen];
     445            0 :   got = ::read(this->fd_, rdbuf, namlen);
     446            0 :   if (got != namlen)
     447              :     {
     448            0 :       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            0 :       delete[] rdbuf;
     452            0 :       return false;
     453              :     }
     454            0 :   pname->assign(rdbuf, namlen);
     455            0 :   delete[] rdbuf;
     456              : 
     457            0 :   long local_next_off;
     458            0 :   if (!this->parse_decimal(hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem), &local_next_off))
     459              :     {
     460            0 :       char* buf = new char[sizeof(hdr.ar_nxtmem) + 1];
     461            0 :       memcpy(buf, hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem));
     462            0 :       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            0 :       delete[] buf;
     467            0 :       return false;
     468              :     }
     469            0 :   if (next_off != NULL)
     470              :     {
     471            0 :       if (local_next_off == 0) // Last member.
     472            0 :         *next_off = this->filesize_;
     473              :       else
     474            0 :         *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         3630 : 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         3630 :   Archive_header hdr;
     487         3630 :   ssize_t got = ::read(this->fd_, &hdr, sizeof hdr);
     488         3630 :   if (got != sizeof hdr)
     489              :     {
     490            0 :       if (got < 0)
     491            0 :         go_error_at(this->location_, "%s: %m", this->filename_.c_str());
     492            0 :       else if (got > 0)
     493            0 :         go_error_at(this->location_, "%s: short archive header at %ld",
     494              :                     this->filename_.c_str(), static_cast<long>(off));
     495              :       else
     496            0 :         go_error_at(this->location_, "%s: unexpected EOF at %ld",
     497              :                     this->filename_.c_str(), static_cast<long>(off));
     498              :     }
     499         3630 :   off_t local_nested_off;
     500         3630 :   if (!this->interpret_header(&hdr, off, pname, size, &local_nested_off))
     501              :     return false;
     502         3630 :   if (nested_off != NULL)
     503         2226 :     *nested_off = local_nested_off;
     504              : 
     505         3630 :   off_t local_next_off;
     506         3630 :   local_next_off = off + sizeof(Archive_header);
     507         3630 :   if (!this->is_thin_archive_ || pname->empty() || *pname == "/")
     508         3630 :     local_next_off += *size;
     509         3630 :   if ((local_next_off & 1) != 0)
     510           19 :     ++local_next_off;
     511         3630 :   if (local_next_off > this->filesize_) // Last member.
     512              :     local_next_off = this->filesize_;
     513         3630 :   if (next_off != NULL)
     514         2928 :     *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         3630 : 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         3630 :   if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0)
     527              :     {
     528            0 :       go_error_at(this->location_, "%s: malformed archive header at %lu",
     529              :                   this->filename_.c_str(), static_cast<unsigned long>(off));
     530            0 :       return false;
     531              :     }
     532              : 
     533         3630 :   long local_size;
     534         3630 :   if (!this->parse_decimal(hdr->ar_size, sizeof hdr->ar_size, &local_size))
     535              :     {
     536            0 :       go_error_at(this->location_, "%s: malformed archive header size at %lu",
     537              :                   this->filename_.c_str(), static_cast<unsigned long>(off));
     538            0 :       return false;
     539              :     }
     540         3630 :   *size = local_size;
     541              : 
     542         3630 :   *nested_off = 0;
     543         3630 :   if (hdr->ar_name[0] != '/')
     544              :     {
     545         2226 :       const char* name_end = strchr(hdr->ar_name, '/');
     546         2226 :       if (name_end == NULL
     547         2226 :           || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name))
     548              :         {
     549            0 :           go_error_at(this->location_,
     550              :                       "%s: malformed archive header name at %lu",
     551              :                       this->filename_.c_str(), static_cast<unsigned long>(off));
     552            0 :           return false;
     553              :         }
     554         2226 :       pname->assign(hdr->ar_name, name_end - hdr->ar_name);
     555              :     }
     556         1404 :   else if (hdr->ar_name[1] == ' ')
     557              :     {
     558              :       // This is the symbol table.
     559         1404 :       pname->clear();
     560              :     }
     561            0 :   else if (hdr->ar_name[1] == 'S' && hdr->ar_name[2] == 'Y'
     562            0 :            && hdr->ar_name[3] == 'M' && hdr->ar_name[4] == '6'
     563            0 :            && hdr->ar_name[5] == '4' && hdr->ar_name[6] == '/'
     564            0 :            && hdr->ar_name[7] == ' '
     565              :           )
     566              :     {
     567              :       // 64-bit symbol table.
     568            0 :       pname->clear();
     569              :     }
     570            0 :   else if (hdr->ar_name[1] == '/')
     571              :     {
     572              :       // This is the extended name table.
     573            0 :       pname->assign(1, '/');
     574              :     }
     575              :   else
     576              :     {
     577            0 :       char* end;
     578            0 :       errno = 0;
     579            0 :       long x = strtol(hdr->ar_name + 1, &end, 10);
     580            0 :       long y = 0;
     581            0 :       if (*end == ':')
     582            0 :         y = strtol(end + 1, &end, 10);
     583            0 :       if (*end != ' '
     584            0 :           || x < 0
     585            0 :           || (x == LONG_MAX && errno == ERANGE)
     586            0 :           || static_cast<size_t>(x) >= this->extended_names_.size())
     587              :         {
     588            0 :           go_error_at(this->location_, "%s: bad extended name index at %lu",
     589              :                       this->filename_.c_str(), static_cast<unsigned long>(off));
     590            0 :           return false;
     591              :         }
     592              : 
     593            0 :       const char* name = this->extended_names_.data() + x;
     594            0 :       const char* name_end = strchr(name, '\n');
     595            0 :       if (static_cast<size_t>(name_end - name) > this->extended_names_.size()
     596            0 :           || name_end[-1] != '/')
     597              :         {
     598            0 :           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            0 :           return false;
     602              :         }
     603            0 :       pname->assign(name, name_end - 1 - name);
     604            0 :       *nested_off = y;
     605              :     }
     606              : 
     607              :   return true;
     608              : }
     609              : 
     610              : // Get the file and offset for an archive member.
     611              : 
     612              : bool
     613         1524 : 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         1524 :   if (this->is_big_archive_)
     618              :     {
     619            0 :       *memfd = this->fd_;
     620            0 :       *memoff = (off + sizeof(Archive_big_header) + hdrname.length()
     621            0 :                  + sizeof(arfmag));
     622            0 :       if ((*memoff & 1) != 0)
     623            0 :         ++*memoff;
     624            0 :       *memname = this->filename_ + '(' + hdrname + ')';
     625            0 :       return true;
     626              :     }
     627         1524 :   else if (!this->is_thin_archive_)
     628              :     {
     629         1524 :       *memfd = this->fd_;
     630         1524 :       *memoff = off + sizeof(Archive_header);
     631         1524 :       *memname = this->filename_ + '(' + hdrname + ')';
     632         1524 :       return true;
     633              :     }
     634              : 
     635            0 :   std::string filename = hdrname;
     636            0 :   if (!IS_ABSOLUTE_PATH(filename.c_str()))
     637              :     {
     638            0 :       const char* archive_path = this->filename_.c_str();
     639            0 :       const char* basename = lbasename(archive_path);
     640            0 :       if (basename > archive_path)
     641            0 :         filename.replace(0, 0,
     642            0 :                          this->filename_.substr(0, basename - archive_path));
     643              :     }
     644              : 
     645            0 :   if (nested_off > 0)
     646              :     {
     647              :       // This is a member of a nested archive.
     648            0 :       Archive_file* nfile;
     649            0 :       Nested_archive_table::const_iterator p =
     650            0 :         this->nested_archives_.find(filename);
     651            0 :       if (p != this->nested_archives_.end())
     652            0 :         nfile = p->second;
     653              :       else
     654              :         {
     655            0 :           int nfd = open(filename.c_str(), O_RDONLY | O_BINARY);
     656            0 :           if (nfd < 0)
     657              :             {
     658            0 :               go_error_at(this->location_, "%s: cannot open nested archive %s",
     659              :                           this->filename_.c_str(), filename.c_str());
     660            0 :               return false;
     661              :             }
     662            0 :           nfile = new Archive_file(filename, nfd, this->location_);
     663            0 :           if (!nfile->initialize())
     664              :             {
     665            0 :               delete nfile;
     666            0 :               return false;
     667              :             }
     668            0 :           this->nested_archives_[filename] = nfile;
     669              :         }
     670              : 
     671            0 :       std::string nname;
     672            0 :       off_t nsize;
     673            0 :       off_t nnested_off;
     674            0 :       if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off, NULL))
     675              :         return false;
     676            0 :       return nfile->get_file_and_offset(nested_off, nname, nnested_off,
     677            0 :                                         memfd, memoff, memname);
     678            0 :     }
     679              : 
     680              :   // An external member of a thin archive.
     681            0 :   *memfd = open(filename.c_str(), O_RDONLY | O_BINARY);
     682            0 :   if (*memfd < 0)
     683              :     {
     684            0 :       go_error_at(this->location_, "%s: %m", filename.c_str());
     685            0 :       return false;
     686              :     }
     687            0 :   *memoff = 0;
     688            0 :   *memname = filename;
     689            0 :   return true;
     690            0 : }
     691              : 
     692              : // An archive member iterator.  This is more-or-less copied from gold.
     693              : 
     694         7380 : class Archive_iterator
     695              : {
     696              :  public:
     697              :   // The header of an archive member.  This is what this iterator
     698              :   // points to.
     699         1404 :   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         1404 :   Archive_iterator(Archive_file* afile, off_t off)
     712         1404 :     : afile_(afile), off_(off)
     713         1404 :   { 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         1524 :   operator++()
     725              :   {
     726         1524 :     if (this->off_ == this->afile_->filesize())
     727              :       return *this;
     728         1524 :     this->off_ = this->next_off_;
     729         1524 :     this->read_next_header();
     730         1524 :     return *this;
     731              :   }
     732              : 
     733              :   Archive_iterator
     734         1524 :   operator++(int)
     735              :   {
     736         3048 :     Archive_iterator ret = *this;
     737         1524 :     ++*this;
     738         1524 :     return ret;
     739              :   }
     740              : 
     741              :   bool
     742              :   operator==(const Archive_iterator& p) const
     743              :   { return this->off_ == p->off; }
     744              : 
     745              :   bool
     746         2226 :   operator!=(const Archive_iterator& p) const
     747         2226 :   { 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         2928 : Archive_iterator::read_next_header()
     767              : {
     768         2928 :   off_t filesize = this->afile_->filesize();
     769         4332 :   while (true)
     770              :     {
     771         3630 :       if (this->off_ == filesize)
     772              :         {
     773         1404 :           this->header_.off = filesize;
     774         1404 :           return;
     775              :         }
     776              : 
     777         2226 :       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            0 :           this->header_.off = filesize;
     783            0 :           this->off_ = filesize;
     784            0 :           return;
     785              :         }
     786         2226 :       this->header_.off = this->off_;
     787              : 
     788              :       // Skip special members.
     789         2226 :       if (!this->header_.name.empty() && this->header_.name != "/")
     790              :         return;
     791              : 
     792          702 :       this->off_ = this->next_off_;
     793              :     }
     794              : }
     795              : 
     796              : // Initial iterator.
     797              : 
     798              : Archive_iterator
     799          702 : archive_begin(Archive_file* afile)
     800              : {
     801          702 :   return Archive_iterator(afile, afile->first_member_offset());
     802              : }
     803              : 
     804              : // Final iterator.
     805              : 
     806              : Archive_iterator
     807          702 : archive_end(Archive_file* afile)
     808              : {
     809          702 :   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          702 :   Stream_concatenate()
     819          702 :     : inputs_()
     820              :   { }
     821              : 
     822              :   // Add a new stream.
     823              :   void
     824          702 :   add(Import::Stream* is)
     825         1404 :   { 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      9783558 : Stream_concatenate::do_peek(size_t length, const char** bytes)
     842              : {
     843          702 :   while (true)
     844              :     {
     845      9784260 :       if (this->inputs_.empty())
     846              :         return false;
     847      9783558 :       if (this->inputs_.front()->peek(length, bytes))
     848              :         return true;
     849          702 :       delete this->inputs_.front();
     850          702 :       this->inputs_.pop_front();
     851              :     }
     852              : }
     853              : 
     854              : // Advance.
     855              : 
     856              : void
     857      6603699 : Stream_concatenate::do_advance(size_t skip)
     858              : {
     859            0 :   while (true)
     860              :     {
     861      6603699 :       if (this->inputs_.empty())
     862              :         return;
     863      6603699 :       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      6603699 :           this->inputs_.front()->advance(skip);
     869      6603699 :           return;
     870              :         }
     871            0 :       delete this->inputs_.front();
     872            0 :       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          702 : Import::find_archive_export_data(const std::string& filename, int fd,
     881              :                                  Location location)
     882              : {
     883          702 :   Archive_file afile(filename, fd, location);
     884          702 :   if (!afile.initialize())
     885              :     return NULL;
     886              : 
     887          702 :   Stream_concatenate* ret = new Stream_concatenate;
     888              : 
     889          702 :   bool any_data = false;
     890          702 :   bool any_members = false;
     891          702 :   Archive_iterator pend = archive_end(&afile);
     892         2226 :   for (Archive_iterator p = archive_begin(&afile); p != pend; p++)
     893              :     {
     894         1524 :       any_members = true;
     895         1524 :       int member_fd;
     896         1524 :       off_t member_off;
     897         1524 :       std::string member_name;
     898         1524 :       if (!afile.get_file_and_offset(p->off, p->name, p->nested_off,
     899              :                                      &member_fd, &member_off, &member_name))
     900            0 :         return NULL;
     901              : 
     902         1524 :       Import::Stream* is = Import::find_object_export_data(member_name,
     903              :                                                            member_fd,
     904              :                                                            member_off,
     905              :                                                            location);
     906         1524 :       if (is != NULL)
     907              :         {
     908          702 :           ret->add(is);
     909          702 :           any_data = true;
     910              :         }
     911         1524 :     }
     912              : 
     913          702 :   if (!any_members)
     914              :     {
     915              :       // It's normal to have an empty archive file when using gobuild.
     916            0 :       return new Stream_from_string("");
     917              :     }
     918              : 
     919          702 :   if (!any_data)
     920              :     {
     921            0 :       delete ret;
     922            0 :       return NULL;
     923              :     }
     924              : 
     925              :   return ret;
     926          702 : }
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.