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