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

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.