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 : 26024 : add_search_path (const std::string &path)
39 : : {
40 : 26024 : search_path.push_back (path);
41 : 26024 : }
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 : 216 : p != search_path.end (); ++p)
119 : : {
120 : 189 : std::string indir = *p;
121 : 189 : if (!indir.empty () && indir[indir.size () - 1] != '/')
122 : 189 : indir += '/';
123 : 189 : indir += fn;
124 : 189 : auto s = Import::try_package_in_directory (indir, location);
125 : 189 : if (s.first != nullptr)
126 : 0 : return s;
127 : 189 : }
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 : 216 : Import::try_package_in_directory (const std::string &filename,
141 : : location_t location)
142 : : {
143 : 216 : std::string found_filename = filename;
144 : 216 : int fd = open (found_filename.c_str (), O_RDONLY | O_BINARY);
145 : :
146 : 216 : 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 : 216 : if (errno != ENOENT && errno != EISDIR)
160 : 0 : rust_warning_at (location, 0, "%s: %m", filename.c_str ());
161 : :
162 : 216 : fd = Import::try_suffixes (&found_filename);
163 : 216 : if (fd < 0)
164 : 200 : 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 (nullptr, 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 : 216 : Import::try_suffixes (std::string *pfilename)
191 : : {
192 : 216 : std::string filename = *pfilename + ".rox";
193 : 216 : int fd = open (filename.c_str (), O_RDONLY | O_BINARY);
194 : 216 : if (fd >= 0)
195 : : {
196 : 16 : *pfilename = filename;
197 : 16 : return fd;
198 : : }
199 : :
200 : 200 : const char *basename = lbasename (pfilename->c_str ());
201 : 200 : size_t basename_pos = basename - pfilename->c_str ();
202 : 800 : filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".so";
203 : 200 : fd = open (filename.c_str (), O_RDONLY | O_BINARY);
204 : 200 : if (fd >= 0)
205 : : {
206 : 0 : *pfilename = filename;
207 : 0 : return fd;
208 : : }
209 : :
210 : 800 : filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".a";
211 : 200 : fd = open (filename.c_str (), O_RDONLY | O_BINARY);
212 : 200 : if (fd >= 0)
213 : : {
214 : 0 : *pfilename = filename;
215 : 0 : return fd;
216 : : }
217 : :
218 : 200 : filename = *pfilename + ".o";
219 : 200 : fd = open (filename.c_str (), O_RDONLY | O_BINARY);
220 : 200 : if (fd >= 0)
221 : : {
222 : 0 : *pfilename = filename;
223 : 0 : return fd;
224 : : }
225 : :
226 : : return -1;
227 : 216 : }
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 : 4080 : Import::Stream::peek_char ()
337 : : {
338 : 4080 : const char *read;
339 : 4080 : 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 : 4080 : unsigned char ret = *read;
344 : 4080 : 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 : 4128 : Stream_from_file::do_peek (size_t length, const char **bytes)
394 : : {
395 : 4128 : if (this->data_.length () >= length)
396 : : {
397 : 2040 : *bytes = this->data_.data ();
398 : 2040 : return true;
399 : : }
400 : :
401 : 2088 : this->data_.resize (length);
402 : 2088 : ssize_t got = ::read (this->fd_, &this->data_[0], length);
403 : :
404 : 2088 : 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 : 2088 : 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 : 2088 : if (static_cast<size_t> (got) < length)
421 : : return false;
422 : :
423 : 2088 : *bytes = this->data_.data ();
424 : 2088 : return true;
425 : : }
426 : :
427 : : // Advance.
428 : :
429 : : void
430 : 2088 : Stream_from_file::do_advance (size_t skip)
431 : : {
432 : 2088 : 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 : 2088 : if (!this->data_.empty ())
439 : : {
440 : 2088 : if (this->data_.length () > skip)
441 : 0 : this->data_.erase (0, skip);
442 : : else
443 : 2088 : this->data_.clear ();
444 : : }
445 : 2088 : }
446 : :
447 : : } // namespace Rust
|