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 "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 : 5856 : 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 : 1524 : 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 : }
|