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