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: 2024-04-13 14:00:49 Functions: 78.9 % 19 15
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

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

Generated by: LCOV version 2.1-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.