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