Branch data Line data Source code
1 : : // Copyright (C) 2020-2025 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 : 35852 : add_search_path (const std::string &path)
38 : : {
39 : 35852 : search_path.push_back (path);
40 : 35852 : }
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 : 30 : Import::open_package (const std::string &filename, location_t location,
67 : : const std::string &relative_import_path)
68 : : {
69 : 30 : bool is_local;
70 : 30 : if (IS_ABSOLUTE_PATH (filename))
71 : : is_local = true;
72 : 30 : else if (filename[0] == '.'
73 : 30 : && (filename[1] == '\0' || IS_DIR_SEPARATOR (filename[1])))
74 : : is_local = true;
75 : 30 : else if (filename[0] == '.' && filename[1] == '.'
76 : 30 : && (filename[2] == '\0' || IS_DIR_SEPARATOR (filename[2])))
77 : : is_local = true;
78 : : else
79 : : is_local = false;
80 : :
81 : 30 : std::string fn = filename;
82 : 0 : if (is_local && !IS_ABSOLUTE_PATH (filename)
83 : 30 : && !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 : 30 : if (!is_local)
115 : : {
116 : 30 : for (std::vector<std::string>::const_iterator p = search_path.begin ();
117 : 240 : p != search_path.end (); ++p)
118 : : {
119 : 210 : std::string indir = *p;
120 : 420 : if (!indir.empty () && indir[indir.size () - 1] != '/')
121 : 210 : indir += '/';
122 : 210 : indir += fn;
123 : 210 : auto s = Import::try_package_in_directory (indir, location);
124 : 210 : if (s.first != nullptr)
125 : 0 : return s;
126 : 210 : }
127 : : }
128 : :
129 : 30 : auto s = Import::try_package_in_directory (fn, location);
130 : 30 : if (s.first != nullptr)
131 : 24 : return s;
132 : :
133 : 6 : return std::make_pair (nullptr, std::vector<ProcMacro::Procmacro>{});
134 : 30 : }
135 : :
136 : : // Try to find the export data for FILENAME.
137 : :
138 : : std::pair<std::unique_ptr<Import::Stream>, std::vector<ProcMacro::Procmacro>>
139 : 240 : Import::try_package_in_directory (const std::string &filename,
140 : : location_t location)
141 : : {
142 : 240 : std::string found_filename = filename;
143 : 240 : int fd = open (found_filename.c_str (), O_RDONLY | O_BINARY);
144 : :
145 : 240 : 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 : 240 : if (errno != ENOENT && errno != EISDIR)
159 : 0 : rust_warning_at (location, 0, "%s: %m", filename.c_str ());
160 : :
161 : 240 : fd = Import::try_suffixes (&found_filename);
162 : 240 : if (fd < 0)
163 : 216 : 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 : 240 : Import::try_suffixes (std::string *pfilename)
190 : : {
191 : 240 : std::string filename = *pfilename + ".rox";
192 : 240 : int fd = open (filename.c_str (), O_RDONLY | O_BINARY);
193 : 240 : if (fd >= 0)
194 : : {
195 : 24 : *pfilename = filename;
196 : 24 : return fd;
197 : : }
198 : :
199 : 216 : const char *basename = lbasename (pfilename->c_str ());
200 : 216 : size_t basename_pos = basename - pfilename->c_str ();
201 : 864 : filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".so";
202 : 216 : fd = open (filename.c_str (), O_RDONLY | O_BINARY);
203 : 216 : if (fd >= 0)
204 : : {
205 : 0 : *pfilename = filename;
206 : 0 : return fd;
207 : : }
208 : :
209 : 864 : filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".a";
210 : 216 : fd = open (filename.c_str (), O_RDONLY | O_BINARY);
211 : 216 : if (fd >= 0)
212 : : {
213 : 0 : *pfilename = filename;
214 : 0 : return fd;
215 : : }
216 : :
217 : 216 : filename = *pfilename + ".o";
218 : 216 : fd = open (filename.c_str (), O_RDONLY | O_BINARY);
219 : 216 : if (fd >= 0)
220 : : {
221 : 0 : *pfilename = filename;
222 : 0 : return fd;
223 : : }
224 : :
225 : : return -1;
226 : 240 : }
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 : 6816 : Import::Stream::peek_char ()
336 : : {
337 : 6816 : const char *read;
338 : 6816 : 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 : 6816 : unsigned char ret = *read;
343 : 6816 : 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 : 6888 : Stream_from_file::do_peek (size_t length, const char **bytes)
393 : : {
394 : 6888 : if (this->data_.length () >= length)
395 : : {
396 : 3408 : *bytes = this->data_.data ();
397 : 3408 : return true;
398 : : }
399 : :
400 : 3480 : this->data_.resize (length);
401 : 3480 : ssize_t got = ::read (this->fd_, &this->data_[0], length);
402 : :
403 : 3480 : 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 : 3480 : 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 : 3480 : if (static_cast<size_t> (got) < length)
420 : : return false;
421 : :
422 : 3480 : *bytes = this->data_.data ();
423 : 3480 : return true;
424 : : }
425 : :
426 : : // Advance.
427 : :
428 : : void
429 : 3480 : Stream_from_file::do_advance (size_t skip)
430 : : {
431 : 3480 : 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 : 3480 : if (!this->data_.empty ())
438 : : {
439 : 3480 : if (this->data_.length () > skip)
440 : 0 : this->data_.erase (0, skip);
441 : : else
442 : 3480 : this->data_.clear ();
443 : : }
444 : 3480 : }
445 : :
446 : : } // namespace Rust
|