111
|
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 }
|