Mercurial > hg > CbC > CbC_gcc
comparison gcc/go/gofrontend/import-archive.cc @ 111:04ced10e8804
gcc 7
author | kono |
---|---|
date | Fri, 27 Oct 2017 22:46:09 +0900 |
parents | |
children | 1830386684a0 |
comparison
equal
deleted
inserted
replaced
68:561a7518be6b | 111:04ced10e8804 |
---|---|
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 Import::is_archive_magic(const char* bytes) | |
108 { | |
109 return (memcmp(bytes, armag, Import::archive_magic_len) == 0 | |
110 || memcmp(bytes, armagt, Import::archive_magic_len) == 0 | |
111 || 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 Archive_file(const std::string& filename, int fd, Location location) | |
120 : filename_(filename), fd_(fd), filesize_(-1), first_member_offset_(0), | |
121 extended_names_(), is_thin_archive_(false), is_big_archive_(false), | |
122 location_(location), nested_archives_() | |
123 { } | |
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 filesize() const | |
137 { return this->filesize_; } | |
138 | |
139 // Return the offset of the first member. | |
140 off_t | |
141 first_member_offset() const | |
142 { 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 Archive_file::initialize() | |
232 { | |
233 struct stat st; | |
234 if (fstat(this->fd_, &st) < 0) | |
235 { | |
236 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | |
237 return false; | |
238 } | |
239 this->filesize_ = st.st_size; | |
240 | |
241 char buf[sizeof(armagt)]; | |
242 if (::lseek(this->fd_, 0, SEEK_SET) < 0 | |
243 || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt)) | |
244 { | |
245 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | |
246 return false; | |
247 } | |
248 if (memcmp(buf, armagt, sizeof(armagt)) == 0) | |
249 this->is_thin_archive_ = true; | |
250 else if (memcmp(buf, armagb, sizeof(armagb)) == 0) | |
251 this->is_big_archive_ = true; | |
252 | |
253 if (this->is_big_archive_) | |
254 return this->initialize_big_archive(); | |
255 else | |
256 return this->initialize_archive(); | |
257 } | |
258 | |
259 // Initialize a big archive (AIX). | |
260 | |
261 bool | |
262 Archive_file::initialize_big_archive() | |
263 { | |
264 Archive_fl_header flhdr; | |
265 | |
266 // Read the fixed length header. | |
267 if (::lseek(this->fd_, 0, SEEK_SET) < 0 | |
268 || ::read(this->fd_, &flhdr, sizeof(flhdr)) != sizeof(flhdr)) | |
269 { | |
270 go_error_at(this->location_, "%s: could not read archive header", | |
271 this->filename_.c_str()); | |
272 return false; | |
273 } | |
274 | |
275 // Parse offset of the first member. | |
276 long off; | |
277 if (!this->parse_decimal(flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff), &off)) | |
278 { | |
279 char* buf = new char[sizeof(flhdr.fl_fstmoff) + 1]; | |
280 memcpy(buf, flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff)); | |
281 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 delete[] buf; | |
286 return false; | |
287 } | |
288 if (off == 0) // Empty archive. | |
289 this->first_member_offset_ = this->filesize_; | |
290 else | |
291 this->first_member_offset_ = off; | |
292 return true; | |
293 } | |
294 | |
295 // Initialize a normal archive. | |
296 | |
297 bool | |
298 Archive_file::initialize_archive() | |
299 { | |
300 this->first_member_offset_ = sizeof(armag); | |
301 if (this->first_member_offset_ == this->filesize_) | |
302 { | |
303 // Empty archive. | |
304 return true; | |
305 } | |
306 | |
307 // Look for the extended name table. | |
308 std::string filename; | |
309 off_t size; | |
310 off_t next_off; | |
311 if (!this->read_header(this->first_member_offset_, &filename, | |
312 &size, NULL, &next_off)) | |
313 return false; | |
314 if (filename.empty()) | |
315 { | |
316 // We found the symbol table. | |
317 if (!this->read_header(next_off, &filename, &size, NULL, NULL)) | |
318 filename.clear(); | |
319 } | |
320 if (filename == "/") | |
321 { | |
322 char* rdbuf = new char[size]; | |
323 if (::read(this->fd_, rdbuf, size) != size) | |
324 { | |
325 go_error_at(this->location_, "%s: could not read extended names", | |
326 filename.c_str()); | |
327 delete[] rdbuf; | |
328 return false; | |
329 } | |
330 this->extended_names_.assign(rdbuf, size); | |
331 delete[] rdbuf; | |
332 } | |
333 | |
334 return true; | |
335 } | |
336 | |
337 // Read bytes from the file. | |
338 | |
339 bool | |
340 Archive_file::read(off_t offset, off_t size, char* buf) | |
341 { | |
342 if (::lseek(this->fd_, offset, SEEK_SET) < 0 | |
343 || ::read(this->fd_, buf, size) != size) | |
344 { | |
345 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | |
346 return false; | |
347 } | |
348 return true; | |
349 } | |
350 | |
351 // Parse a decimal in readable text. | |
352 | |
353 bool | |
354 Archive_file::parse_decimal(const char* str, off_t size, long* res) const | |
355 { | |
356 char* buf = new char[size + 1]; | |
357 memcpy(buf, str, size); | |
358 char* ps = buf + size; | |
359 while (ps > buf && ps[-1] == ' ') | |
360 --ps; | |
361 *ps = '\0'; | |
362 | |
363 errno = 0; | |
364 char* end; | |
365 *res = strtol(buf, &end, 10); | |
366 if (*end != '\0' | |
367 || *res < 0 | |
368 || (*res == LONG_MAX && errno == ERANGE)) | |
369 { | |
370 delete[] buf; | |
371 return false; | |
372 } | |
373 delete[] buf; | |
374 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 Archive_file::read_header(off_t off, std::string* pname, off_t* size, | |
382 off_t* nested_off, off_t* next_off) | |
383 { | |
384 if (::lseek(this->fd_, off, SEEK_SET) < 0) | |
385 { | |
386 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | |
387 return false; | |
388 } | |
389 if (this->is_big_archive_) | |
390 return this->read_big_archive_header(off, pname, size, next_off); | |
391 else | |
392 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 Archive_file::read_big_archive_header(off_t off, std::string* pname, | |
399 off_t* size, off_t* next_off) | |
400 { | |
401 Archive_big_header hdr; | |
402 ssize_t got; | |
403 | |
404 got = ::read(this->fd_, &hdr, sizeof hdr); | |
405 if (got != sizeof hdr) | |
406 { | |
407 if (got < 0) | |
408 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | |
409 else if (got > 0) | |
410 go_error_at(this->location_, "%s: short entry header at %ld", | |
411 this->filename_.c_str(), static_cast<long>(off)); | |
412 else | |
413 go_error_at(this->location_, "%s: unexpected EOF at %ld", | |
414 this->filename_.c_str(), static_cast<long>(off)); | |
415 } | |
416 | |
417 long local_size; | |
418 if (!this->parse_decimal(hdr.ar_size, sizeof(hdr.ar_size), &local_size)) | |
419 { | |
420 char* buf = new char[sizeof(hdr.ar_size) + 1]; | |
421 memcpy(buf, hdr.ar_size, sizeof(hdr.ar_size)); | |
422 go_error_at(this->location_, | |
423 ("%s: malformed ar_size in entry header at %ld" | |
424 " (expected decimal, got %s)"), | |
425 this->filename_.c_str(), static_cast<long>(off), buf); | |
426 delete[] buf; | |
427 return false; | |
428 } | |
429 *size = local_size; | |
430 | |
431 long namlen; | |
432 if (!this->parse_decimal(hdr.ar_namlen, sizeof(hdr.ar_namlen), &namlen)) | |
433 { | |
434 char* buf = new char[sizeof(hdr.ar_namlen) + 1]; | |
435 memcpy(buf, hdr.ar_namlen, sizeof(hdr.ar_namlen)); | |
436 go_error_at(this->location_, | |
437 ("%s: malformed ar_namlen in entry header at %ld" | |
438 " (expected decimal, got %s)"), | |
439 this->filename_.c_str(), static_cast<long>(off), buf); | |
440 delete[] buf; | |
441 return false; | |
442 } | |
443 // Read member name following member header. | |
444 char* rdbuf = new char[namlen]; | |
445 got = ::read(this->fd_, rdbuf, namlen); | |
446 if (got != namlen) | |
447 { | |
448 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 delete[] rdbuf; | |
452 return false; | |
453 } | |
454 pname->assign(rdbuf, namlen); | |
455 delete[] rdbuf; | |
456 | |
457 long local_next_off; | |
458 if (!this->parse_decimal(hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem), &local_next_off)) | |
459 { | |
460 char* buf = new char[sizeof(hdr.ar_nxtmem) + 1]; | |
461 memcpy(buf, hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem)); | |
462 go_error_at(this->location_, | |
463 ("%s: malformed ar_nxtmem in entry header at %ld" | |
464 " (expected decimal, got %s)"), | |
465 this->filename_.c_str(), static_cast<long>(off), buf); | |
466 delete[] buf; | |
467 return false; | |
468 } | |
469 if (next_off != NULL) | |
470 { | |
471 if (local_next_off == 0) // Last member. | |
472 *next_off = this->filesize_; | |
473 else | |
474 *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 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 Archive_header hdr; | |
487 ssize_t got = ::read(this->fd_, &hdr, sizeof hdr); | |
488 if (got != sizeof hdr) | |
489 { | |
490 if (got < 0) | |
491 go_error_at(this->location_, "%s: %m", this->filename_.c_str()); | |
492 else if (got > 0) | |
493 go_error_at(this->location_, "%s: short archive header at %ld", | |
494 this->filename_.c_str(), static_cast<long>(off)); | |
495 else | |
496 go_error_at(this->location_, "%s: unexpected EOF at %ld", | |
497 this->filename_.c_str(), static_cast<long>(off)); | |
498 } | |
499 off_t local_nested_off; | |
500 if (!this->interpret_header(&hdr, off, pname, size, &local_nested_off)) | |
501 return false; | |
502 if (nested_off != NULL) | |
503 *nested_off = local_nested_off; | |
504 | |
505 off_t local_next_off; | |
506 local_next_off = off + sizeof(Archive_header); | |
507 if (!this->is_thin_archive_ || pname->empty() || *pname == "/") | |
508 local_next_off += *size; | |
509 if ((local_next_off & 1) != 0) | |
510 ++local_next_off; | |
511 if (local_next_off > this->filesize_) // Last member. | |
512 local_next_off = this->filesize_; | |
513 if (next_off != NULL) | |
514 *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 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 if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0) | |
527 { | |
528 go_error_at(this->location_, "%s: malformed archive header at %lu", | |
529 this->filename_.c_str(), static_cast<unsigned long>(off)); | |
530 return false; | |
531 } | |
532 | |
533 long local_size; | |
534 if (!this->parse_decimal(hdr->ar_size, sizeof hdr->ar_size, &local_size)) | |
535 { | |
536 go_error_at(this->location_, "%s: malformed archive header size at %lu", | |
537 this->filename_.c_str(), static_cast<unsigned long>(off)); | |
538 return false; | |
539 } | |
540 *size = local_size; | |
541 | |
542 *nested_off = 0; | |
543 if (hdr->ar_name[0] != '/') | |
544 { | |
545 const char* name_end = strchr(hdr->ar_name, '/'); | |
546 if (name_end == NULL | |
547 || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name)) | |
548 { | |
549 go_error_at(this->location_, | |
550 "%s: malformed archive header name at %lu", | |
551 this->filename_.c_str(), static_cast<unsigned long>(off)); | |
552 return false; | |
553 } | |
554 pname->assign(hdr->ar_name, name_end - hdr->ar_name); | |
555 } | |
556 else if (hdr->ar_name[1] == ' ') | |
557 { | |
558 // This is the symbol table. | |
559 pname->clear(); | |
560 } | |
561 else if (hdr->ar_name[1] == 'S' && hdr->ar_name[2] == 'Y' | |
562 && hdr->ar_name[3] == 'M' && hdr->ar_name[4] == '6' | |
563 && hdr->ar_name[5] == '4' && hdr->ar_name[6] == '/' | |
564 && hdr->ar_name[7] == ' ' | |
565 ) | |
566 { | |
567 // 64-bit symbol table. | |
568 pname->clear(); | |
569 } | |
570 else if (hdr->ar_name[1] == '/') | |
571 { | |
572 // This is the extended name table. | |
573 pname->assign(1, '/'); | |
574 } | |
575 else | |
576 { | |
577 char* end; | |
578 errno = 0; | |
579 long x = strtol(hdr->ar_name + 1, &end, 10); | |
580 long y = 0; | |
581 if (*end == ':') | |
582 y = strtol(end + 1, &end, 10); | |
583 if (*end != ' ' | |
584 || x < 0 | |
585 || (x == LONG_MAX && errno == ERANGE) | |
586 || static_cast<size_t>(x) >= this->extended_names_.size()) | |
587 { | |
588 go_error_at(this->location_, "%s: bad extended name index at %lu", | |
589 this->filename_.c_str(), static_cast<unsigned long>(off)); | |
590 return false; | |
591 } | |
592 | |
593 const char* name = this->extended_names_.data() + x; | |
594 const char* name_end = strchr(name, '\n'); | |
595 if (static_cast<size_t>(name_end - name) > this->extended_names_.size() | |
596 || name_end[-1] != '/') | |
597 { | |
598 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 return false; | |
602 } | |
603 pname->assign(name, name_end - 1 - name); | |
604 *nested_off = y; | |
605 } | |
606 | |
607 return true; | |
608 } | |
609 | |
610 // Get the file and offset for an archive member. | |
611 | |
612 bool | |
613 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 if (this->is_big_archive_) | |
618 { | |
619 *memfd = this->fd_; | |
620 *memoff = (off + sizeof(Archive_big_header) + hdrname.length() | |
621 + sizeof(arfmag)); | |
622 if ((*memoff & 1) != 0) | |
623 ++*memoff; | |
624 *memname = this->filename_ + '(' + hdrname + ')'; | |
625 return true; | |
626 } | |
627 else if (!this->is_thin_archive_) | |
628 { | |
629 *memfd = this->fd_; | |
630 *memoff = off + sizeof(Archive_header); | |
631 *memname = this->filename_ + '(' + hdrname + ')'; | |
632 return true; | |
633 } | |
634 | |
635 std::string filename = hdrname; | |
636 if (!IS_ABSOLUTE_PATH(filename.c_str())) | |
637 { | |
638 const char* archive_path = this->filename_.c_str(); | |
639 const char* basename = lbasename(archive_path); | |
640 if (basename > archive_path) | |
641 filename.replace(0, 0, | |
642 this->filename_.substr(0, basename - archive_path)); | |
643 } | |
644 | |
645 if (nested_off > 0) | |
646 { | |
647 // This is a member of a nested archive. | |
648 Archive_file* nfile; | |
649 Nested_archive_table::const_iterator p = | |
650 this->nested_archives_.find(filename); | |
651 if (p != this->nested_archives_.end()) | |
652 nfile = p->second; | |
653 else | |
654 { | |
655 int nfd = open(filename.c_str(), O_RDONLY | O_BINARY); | |
656 if (nfd < 0) | |
657 { | |
658 go_error_at(this->location_, "%s: can't open nested archive %s", | |
659 this->filename_.c_str(), filename.c_str()); | |
660 return false; | |
661 } | |
662 nfile = new Archive_file(filename, nfd, this->location_); | |
663 if (!nfile->initialize()) | |
664 { | |
665 delete nfile; | |
666 return false; | |
667 } | |
668 this->nested_archives_[filename] = nfile; | |
669 } | |
670 | |
671 std::string nname; | |
672 off_t nsize; | |
673 off_t nnested_off; | |
674 if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off, NULL)) | |
675 return false; | |
676 return nfile->get_file_and_offset(nested_off, nname, nnested_off, | |
677 memfd, memoff, memname); | |
678 } | |
679 | |
680 // An external member of a thin archive. | |
681 *memfd = open(filename.c_str(), O_RDONLY | O_BINARY); | |
682 if (*memfd < 0) | |
683 { | |
684 go_error_at(this->location_, "%s: %m", filename.c_str()); | |
685 return false; | |
686 } | |
687 *memoff = 0; | |
688 *memname = filename; | |
689 return true; | |
690 } | |
691 | |
692 // An archive member iterator. This is more-or-less copied from gold. | |
693 | |
694 class Archive_iterator | |
695 { | |
696 public: | |
697 // The header of an archive member. This is what this iterator | |
698 // points to. | |
699 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 Archive_iterator(Archive_file* afile, off_t off) | |
712 : afile_(afile), off_(off) | |
713 { 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 operator++() | |
725 { | |
726 if (this->off_ == this->afile_->filesize()) | |
727 return *this; | |
728 this->off_ = this->next_off_; | |
729 this->read_next_header(); | |
730 return *this; | |
731 } | |
732 | |
733 Archive_iterator | |
734 operator++(int) | |
735 { | |
736 Archive_iterator ret = *this; | |
737 ++*this; | |
738 return ret; | |
739 } | |
740 | |
741 bool | |
742 operator==(const Archive_iterator& p) const | |
743 { return this->off_ == p->off; } | |
744 | |
745 bool | |
746 operator!=(const Archive_iterator& p) const | |
747 { 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 Archive_iterator::read_next_header() | |
767 { | |
768 off_t filesize = this->afile_->filesize(); | |
769 while (true) | |
770 { | |
771 if (this->off_ == filesize) | |
772 { | |
773 this->header_.off = filesize; | |
774 return; | |
775 } | |
776 | |
777 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 this->header_.off = filesize; | |
783 return; | |
784 } | |
785 this->header_.off = this->off_; | |
786 | |
787 // Skip special members. | |
788 if (!this->header_.name.empty() && this->header_.name != "/") | |
789 return; | |
790 | |
791 this->off_ = this->next_off_; | |
792 } | |
793 } | |
794 | |
795 // Initial iterator. | |
796 | |
797 Archive_iterator | |
798 archive_begin(Archive_file* afile) | |
799 { | |
800 return Archive_iterator(afile, afile->first_member_offset()); | |
801 } | |
802 | |
803 // Final iterator. | |
804 | |
805 Archive_iterator | |
806 archive_end(Archive_file* afile) | |
807 { | |
808 return Archive_iterator(afile, afile->filesize()); | |
809 } | |
810 | |
811 // A type of Import_stream which concatenates other Import_streams | |
812 // together. | |
813 | |
814 class Stream_concatenate : public Import::Stream | |
815 { | |
816 public: | |
817 Stream_concatenate() | |
818 : inputs_() | |
819 { } | |
820 | |
821 // Add a new stream. | |
822 void | |
823 add(Import::Stream* is) | |
824 { this->inputs_.push_back(is); } | |
825 | |
826 protected: | |
827 bool | |
828 do_peek(size_t, const char**); | |
829 | |
830 void | |
831 do_advance(size_t); | |
832 | |
833 private: | |
834 std::list<Import::Stream*> inputs_; | |
835 }; | |
836 | |
837 // Peek ahead. | |
838 | |
839 bool | |
840 Stream_concatenate::do_peek(size_t length, const char** bytes) | |
841 { | |
842 while (true) | |
843 { | |
844 if (this->inputs_.empty()) | |
845 return false; | |
846 if (this->inputs_.front()->peek(length, bytes)) | |
847 return true; | |
848 delete this->inputs_.front(); | |
849 this->inputs_.pop_front(); | |
850 } | |
851 } | |
852 | |
853 // Advance. | |
854 | |
855 void | |
856 Stream_concatenate::do_advance(size_t skip) | |
857 { | |
858 while (true) | |
859 { | |
860 if (this->inputs_.empty()) | |
861 return; | |
862 if (!this->inputs_.front()->at_eof()) | |
863 { | |
864 // We just assume that this will do the right thing. It | |
865 // should be OK since we should never want to skip past | |
866 // multiple streams. | |
867 this->inputs_.front()->advance(skip); | |
868 return; | |
869 } | |
870 delete this->inputs_.front(); | |
871 this->inputs_.pop_front(); | |
872 } | |
873 } | |
874 | |
875 // Import data from an archive. We walk through the archive and | |
876 // import data from each member. | |
877 | |
878 Import::Stream* | |
879 Import::find_archive_export_data(const std::string& filename, int fd, | |
880 Location location) | |
881 { | |
882 Archive_file afile(filename, fd, location); | |
883 if (!afile.initialize()) | |
884 return NULL; | |
885 | |
886 Stream_concatenate* ret = new Stream_concatenate; | |
887 | |
888 bool any_data = false; | |
889 bool any_members = false; | |
890 Archive_iterator pend = archive_end(&afile); | |
891 for (Archive_iterator p = archive_begin(&afile); p != pend; p++) | |
892 { | |
893 any_members = true; | |
894 int member_fd; | |
895 off_t member_off; | |
896 std::string member_name; | |
897 if (!afile.get_file_and_offset(p->off, p->name, p->nested_off, | |
898 &member_fd, &member_off, &member_name)) | |
899 return NULL; | |
900 | |
901 Import::Stream* is = Import::find_object_export_data(member_name, | |
902 member_fd, | |
903 member_off, | |
904 location); | |
905 if (is != NULL) | |
906 { | |
907 ret->add(is); | |
908 any_data = true; | |
909 } | |
910 } | |
911 | |
912 if (!any_members) | |
913 { | |
914 // It's normal to have an empty archive file when using gobuild. | |
915 return new Stream_from_string(""); | |
916 } | |
917 | |
918 if (!any_data) | |
919 { | |
920 delete ret; | |
921 return NULL; | |
922 } | |
923 | |
924 return ret; | |
925 } |