LCOV - code coverage report
Current view: top level - gcc/rust/metadata - rust-imports.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 59.4 % 187 111
Test Date: 2026-02-28 14:20:25 Functions: 78.9 % 19 15
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2020-2026 Free Software Foundation, Inc.
       2              : 
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : #include "rust-system.h"
      20              : #include "rust-diagnostics.h"
      21              : #include "rust-imports.h"
      22              : #include "rust-object-export.h"
      23              : #include "rust-export-metadata.h"
      24              : 
      25              : #ifndef O_BINARY
      26              : #define O_BINARY 0
      27              : #endif
      28              : 
      29              : namespace Rust {
      30              : 
      31              : // The list of paths we search for import files.
      32              : static std::vector<std::string> search_path;
      33              : 
      34              : // Add a directory to the search path.  This is called from the option
      35              : // handling language hook.
      36              : void
      37        32450 : add_search_path (const std::string &path)
      38              : {
      39        32450 :   search_path.push_back (path);
      40        32450 : }
      41              : 
      42              : // Find import data.  This searches the file system for FILENAME and
      43              : // returns a pointer to a Stream object to read the data that it
      44              : // exports.  If the file is not found, it returns NULL.
      45              : 
      46              : // When FILENAME is not an absolute path and does not start with ./ or
      47              : // ../, we use the search path provided by -I and -L options.
      48              : 
      49              : // When FILENAME does start with ./ or ../, we use
      50              : // RELATIVE_IMPORT_PATH as a prefix.
      51              : 
      52              : // When FILENAME does not exist, we try modifying FILENAME to find the
      53              : // file.  We use the first of these which exists:
      54              : //   * We append ".gox".
      55              : //   * We turn the base of FILENAME into libFILENAME.so.
      56              : //   * We turn the base of FILENAME into libFILENAME.a.
      57              : //   * We append ".o".
      58              : 
      59              : // When using a search path, we apply each of these transformations at
      60              : // each entry on the search path before moving on to the next entry.
      61              : // If the file exists, but does not contain any Rust export data, we
      62              : // stop; we do not keep looking for another file with the same name
      63              : // later in the search path.
      64              : 
      65              : std::pair<std::unique_ptr<Import::Stream>, std::vector<ProcMacro::Procmacro>>
      66           27 : Import::open_package (const std::string &filename, location_t location,
      67              :                       const std::string &relative_import_path)
      68              : {
      69           27 :   bool is_local;
      70           27 :   if (IS_ABSOLUTE_PATH (filename))
      71              :     is_local = true;
      72           27 :   else if (filename[0] == '.'
      73           27 :            && (filename[1] == '\0' || IS_DIR_SEPARATOR (filename[1])))
      74              :     is_local = true;
      75           27 :   else if (filename[0] == '.' && filename[1] == '.'
      76           27 :            && (filename[2] == '\0' || IS_DIR_SEPARATOR (filename[2])))
      77              :     is_local = true;
      78              :   else
      79              :     is_local = false;
      80              : 
      81           27 :   std::string fn = filename;
      82            0 :   if (is_local && !IS_ABSOLUTE_PATH (filename)
      83           27 :       && !relative_import_path.empty ())
      84              :     {
      85            0 :       if (fn == ".")
      86              :         {
      87              :           // A special case.
      88            0 :           fn = relative_import_path;
      89              :         }
      90            0 :       else if (fn[0] == '.' && fn[1] == '.'
      91            0 :                && (fn[2] == '\0' || IS_DIR_SEPARATOR (fn[2])))
      92              :         {
      93              :           // We are going to join relative_import_path and fn, and it
      94              :           // will look like DIR/../PATH.  But DIR does not necessarily
      95              :           // exist in this case, and if it doesn't the use of .. will
      96              :           // fail although it shouldn't.  The gc compiler uses
      97              :           // path.Join here, which cleans up the .., so we need to do
      98              :           // the same.
      99            0 :           size_t index;
     100            0 :           for (index = relative_import_path.length () - 1;
     101            0 :                index > 0 && !IS_DIR_SEPARATOR (relative_import_path[index]);
     102              :                index--)
     103              :             ;
     104            0 :           if (index > 0)
     105            0 :             fn = relative_import_path.substr (0, index) + fn.substr (2);
     106              :           else
     107            0 :             fn = relative_import_path + '/' + fn;
     108              :         }
     109              :       else
     110            0 :         fn = relative_import_path + '/' + fn;
     111              :       is_local = false;
     112              :     }
     113              : 
     114           27 :   if (!is_local)
     115              :     {
     116           27 :       for (std::vector<std::string>::const_iterator p = search_path.begin ();
     117          216 :            p != search_path.end (); ++p)
     118              :         {
     119          189 :           std::string indir = *p;
     120          378 :           if (!indir.empty () && indir[indir.size () - 1] != '/')
     121          189 :             indir += '/';
     122          189 :           indir += fn;
     123          189 :           auto s = Import::try_package_in_directory (indir, location);
     124          189 :           if (s.first != nullptr)
     125            0 :             return s;
     126          189 :         }
     127              :     }
     128              : 
     129           27 :   auto s = Import::try_package_in_directory (fn, location);
     130           27 :   if (s.first != nullptr)
     131           24 :     return s;
     132              : 
     133            3 :   return std::make_pair (nullptr, std::vector<ProcMacro::Procmacro>{});
     134           27 : }
     135              : 
     136              : // Try to find the export data for FILENAME.
     137              : 
     138              : std::pair<std::unique_ptr<Import::Stream>, std::vector<ProcMacro::Procmacro>>
     139          216 : Import::try_package_in_directory (const std::string &filename,
     140              :                                   location_t location)
     141              : {
     142          216 :   std::string found_filename = filename;
     143          216 :   int fd = open (found_filename.c_str (), O_RDONLY | O_BINARY);
     144              : 
     145          216 :   if (fd >= 0)
     146              :     {
     147            0 :       struct stat s;
     148            0 :       if (fstat (fd, &s) >= 0 && S_ISDIR (s.st_mode))
     149              :         {
     150            0 :           close (fd);
     151            0 :           fd = -1;
     152            0 :           errno = EISDIR;
     153              :         }
     154              :     }
     155              : 
     156            0 :   if (fd < 0)
     157              :     {
     158          216 :       if (errno != ENOENT && errno != EISDIR)
     159            0 :         rust_warning_at (location, 0, "%s: %m", filename.c_str ());
     160              : 
     161          216 :       fd = Import::try_suffixes (&found_filename);
     162          216 :       if (fd < 0)
     163          192 :         return std::make_pair (nullptr, std::vector<ProcMacro::Procmacro>{});
     164              :     }
     165              : 
     166           48 :   auto macros = load_macros (found_filename);
     167              : 
     168              :   // The export data may not be in this file.
     169           24 :   std::unique_ptr<Stream> s
     170           24 :     = Import::find_export_data (found_filename, fd, location);
     171           24 :   if (s != nullptr)
     172           24 :     return std::make_pair (std::move (s), macros);
     173              : 
     174            0 :   close (fd);
     175              : 
     176            0 :   if (macros.empty ())
     177            0 :     rust_error_at (location,
     178              :                    "%s exists but does not contain any Rust export data",
     179              :                    found_filename.c_str ());
     180              : 
     181            0 :   return std::make_pair (nullptr, macros);
     182           24 : }
     183              : 
     184              : // Given import "*PFILENAME", where *PFILENAME does not exist, try
     185              : // various suffixes.  If we find one, set *PFILENAME to the one we
     186              : // found.  Return the open file descriptor.
     187              : 
     188              : int
     189          216 : Import::try_suffixes (std::string *pfilename)
     190              : {
     191          216 :   std::string filename = *pfilename + ".rox";
     192          216 :   int fd = open (filename.c_str (), O_RDONLY | O_BINARY);
     193          216 :   if (fd >= 0)
     194              :     {
     195           24 :       *pfilename = filename;
     196           24 :       return fd;
     197              :     }
     198              : 
     199          192 :   const char *basename = lbasename (pfilename->c_str ());
     200          192 :   size_t basename_pos = basename - pfilename->c_str ();
     201          768 :   filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".so";
     202          192 :   fd = open (filename.c_str (), O_RDONLY | O_BINARY);
     203          192 :   if (fd >= 0)
     204              :     {
     205            0 :       *pfilename = filename;
     206            0 :       return fd;
     207              :     }
     208              : 
     209          768 :   filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".a";
     210          192 :   fd = open (filename.c_str (), O_RDONLY | O_BINARY);
     211          192 :   if (fd >= 0)
     212              :     {
     213            0 :       *pfilename = filename;
     214            0 :       return fd;
     215              :     }
     216              : 
     217          192 :   filename = *pfilename + ".o";
     218          192 :   fd = open (filename.c_str (), O_RDONLY | O_BINARY);
     219          192 :   if (fd >= 0)
     220              :     {
     221            0 :       *pfilename = filename;
     222            0 :       return fd;
     223              :     }
     224              : 
     225              :   return -1;
     226          216 : }
     227              : 
     228              : // Look for export data in the file descriptor FD.
     229              : 
     230              : std::unique_ptr<Import::Stream>
     231           24 : Import::find_export_data (const std::string &filename, int fd,
     232              :                           location_t location)
     233              : {
     234              :   // See if we can read this as an object file.
     235           24 :   std::unique_ptr<Import::Stream> stream
     236           24 :     = Import::find_object_export_data (filename, fd, 0, location);
     237           24 :   if (stream != nullptr)
     238            0 :     return stream;
     239              : 
     240           24 :   const int len = sizeof (Metadata::kMagicHeader);
     241           24 :   if (lseek (fd, 0, SEEK_SET) < 0)
     242              :     {
     243            0 :       rust_error_at (location, "lseek %s failed: %m", filename.c_str ());
     244            0 :       return nullptr;
     245              :     }
     246              : 
     247           24 :   char buf[len];
     248           24 :   ssize_t c = ::read (fd, buf, len);
     249           24 :   if (c < len)
     250            0 :     return nullptr;
     251              : 
     252              :   // Check for a file containing nothing but Rust export data.
     253              :   // if (memcmp (buf, Export::cur_magic, Export::magic_len) == 0
     254              :   //     || memcmp (buf, Export::v1_magic, Export::magic_len) == 0
     255              :   //     || memcmp (buf, Export::v2_magic, Export::magic_len) == 0)
     256              :   //
     257              :   // FIXME we need to work out a better header
     258              :   //
     259           24 :   if (memcmp (buf, Metadata::kMagicHeader, sizeof (Metadata::kMagicHeader))
     260              :       == 0)
     261           24 :     return std::make_unique<Stream_from_file> (fd);
     262              : 
     263              :   // See if we can read this as an archive.
     264            0 :   if (Import::is_archive_magic (buf))
     265            0 :     return Import::find_archive_export_data (filename, fd, location);
     266              : 
     267            0 :   return nullptr;
     268           24 : }
     269              : 
     270              : // Look for export data in an object file.
     271              : 
     272              : std::unique_ptr<Import::Stream>
     273           24 : Import::find_object_export_data (const std::string &filename, int fd,
     274              :                                  off_t offset, location_t location)
     275              : {
     276           24 :   char *buf;
     277           24 :   size_t len;
     278           24 :   int err;
     279           24 :   const char *errmsg = rust_read_export_data (fd, offset, &buf, &len, &err);
     280           24 :   if (errmsg != nullptr)
     281              :     {
     282            0 :       if (err == 0)
     283            0 :         rust_error_at (location, "%s: %s", filename.c_str (), errmsg);
     284              :       else
     285            0 :         rust_error_at (location, "%s: %s: %s", filename.c_str (), errmsg,
     286              :                        xstrerror (err));
     287            0 :       return nullptr;
     288              :     }
     289              : 
     290           24 :   if (buf == nullptr)
     291           24 :     return nullptr;
     292              : 
     293            0 :   return std::make_unique<Stream_from_buffer> (buf, len);
     294              : }
     295              : 
     296              : // Class Import.
     297              : 
     298              : // Construct an Import object.  We make the builtin_types_ vector
     299              : // large enough to hold all the builtin types.
     300              : 
     301            0 : Import::Import (std::unique_ptr<Stream> stream, location_t location)
     302            0 :   : stream_ (std::move (stream)), location_ (location)
     303            0 : {}
     304              : 
     305              : // Import the data in the associated stream.
     306              : 
     307              : // Read LENGTH bytes from the stream.
     308              : 
     309              : void
     310            0 : Import::read (size_t length, std::string *out)
     311              : {
     312            0 :   const char *data;
     313            0 :   if (!this->stream_->peek (length, &data))
     314              :     {
     315            0 :       if (!this->stream_->saw_error ())
     316            0 :         rust_error_at (this->location_, "import error at %d: expected %d bytes",
     317              :                        this->stream_->pos (), static_cast<int> (length));
     318            0 :       this->stream_->set_saw_error ();
     319            0 :       *out = std::string ("");
     320            0 :       return;
     321              :     }
     322            0 :   *out = std::string (data, length);
     323            0 :   this->advance (length);
     324              : }
     325              : 
     326              : // Class Import::Stream.
     327              : 
     328           24 : Import::Stream::Stream () : pos_ (0), saw_error_ (false) {}
     329              : 
     330           24 : Import::Stream::~Stream () {}
     331              : 
     332              : // Return the next character to come from the stream.
     333              : 
     334              : int
     335         6160 : Import::Stream::peek_char ()
     336              : {
     337         6160 :   const char *read;
     338         6160 :   if (!this->do_peek (1, &read))
     339              :     return -1;
     340              :   // Make sure we return an unsigned char, so that we don't get
     341              :   // confused by \xff.
     342         6160 :   unsigned char ret = *read;
     343         6160 :   return ret;
     344              : }
     345              : 
     346              : // Return true if the next LENGTH characters from the stream match
     347              : // BYTES
     348              : 
     349              : bool
     350            0 : Import::Stream::match_bytes (const char *bytes, size_t length)
     351              : {
     352            0 :   const char *read;
     353            0 :   if (!this->do_peek (length, &read))
     354              :     return false;
     355            0 :   return memcmp (bytes, read, length) == 0;
     356              : }
     357              : 
     358              : // Require that the next LENGTH bytes from the stream match BYTES.
     359              : 
     360              : void
     361           48 : Import::Stream::require_bytes (location_t location, const char *bytes,
     362              :                                size_t length)
     363              : {
     364           48 :   const char *read;
     365           48 :   if (!this->do_peek (length, &read) || memcmp (bytes, read, length) != 0)
     366              :     {
     367            0 :       if (!this->saw_error_)
     368            0 :         rust_error_at (location, "import error at %d: expected %<%.*s%>",
     369              :                        this->pos (), static_cast<int> (length), bytes);
     370            0 :       this->saw_error_ = true;
     371            0 :       return;
     372              :     }
     373           48 :   this->advance (length);
     374              : }
     375              : 
     376              : // Class Stream_from_file.
     377              : 
     378           24 : Stream_from_file::Stream_from_file (int fd) : fd_ (fd), data_ ()
     379              : {
     380           24 :   if (lseek (fd, 0, SEEK_SET) != 0)
     381              :     {
     382            0 :       rust_fatal_error (UNKNOWN_LOCATION, "lseek failed: %m");
     383              :       this->set_saw_error ();
     384              :     }
     385           24 : }
     386              : 
     387           48 : Stream_from_file::~Stream_from_file () { close (this->fd_); }
     388              : 
     389              : // Read next bytes.
     390              : 
     391              : bool
     392         6232 : Stream_from_file::do_peek (size_t length, const char **bytes)
     393              : {
     394         6232 :   if (this->data_.length () >= length)
     395              :     {
     396         3080 :       *bytes = this->data_.data ();
     397         3080 :       return true;
     398              :     }
     399              : 
     400         3152 :   this->data_.resize (length);
     401         3152 :   ssize_t got = ::read (this->fd_, &this->data_[0], length);
     402              : 
     403         3152 :   if (got < 0)
     404              :     {
     405            0 :       if (!this->saw_error ())
     406            0 :         rust_fatal_error (UNKNOWN_LOCATION, "read failed: %m");
     407            0 :       this->set_saw_error ();
     408            0 :       return false;
     409              :     }
     410              : 
     411         3152 :   if (lseek (this->fd_, -got, SEEK_CUR) < 0)
     412              :     {
     413            0 :       if (!this->saw_error ())
     414            0 :         rust_fatal_error (UNKNOWN_LOCATION, "lseek failed: %m");
     415            0 :       this->set_saw_error ();
     416            0 :       return false;
     417              :     }
     418              : 
     419         3152 :   if (static_cast<size_t> (got) < length)
     420              :     return false;
     421              : 
     422         3152 :   *bytes = this->data_.data ();
     423         3152 :   return true;
     424              : }
     425              : 
     426              : // Advance.
     427              : 
     428              : void
     429         3152 : Stream_from_file::do_advance (size_t skip)
     430              : {
     431         3152 :   if (lseek (this->fd_, skip, SEEK_CUR) < 0)
     432              :     {
     433            0 :       if (!this->saw_error ())
     434            0 :         rust_fatal_error (UNKNOWN_LOCATION, "lseek failed: %m");
     435            0 :       this->set_saw_error ();
     436              :     }
     437         3152 :   if (!this->data_.empty ())
     438              :     {
     439         3152 :       if (this->data_.length () > skip)
     440            0 :         this->data_.erase (0, skip);
     441              :       else
     442         3152 :         this->data_.clear ();
     443              :     }
     444         3152 : }
     445              : 
     446              : } // 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.