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 : :
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
|