1 // import-archive.cc -- Go frontend read import data from an archive file.
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.
15 // Archive magic numbers.
17 static const char armag
[] =
19 '!', '<', 'a', 'r', 'c', 'h', '>', '\n'
22 static const char armagt
[] =
24 '!', '<', 't', 'h', 'i', 'n', '>', '\n'
27 static const char arfmag
[2] = { '`', '\n' };
29 // The header of an entry in an archive. This is all readable text,
30 // padded with spaces where necesary.
36 // The file modification time.
38 // The user's UID in decimal.
40 // The user's GID in decimal.
42 // The file mode in octal.
44 // The file size in decimal.
46 // The final magic code.
50 // The functions in this file extract Go export data from an archive.
52 const int Import::archive_magic_len
;
54 // Return true if BYTES, which are from the start of the file, are an
55 // archive magic number.
58 Import::is_archive_magic(const char* bytes
)
60 return (memcmp(bytes
, armag
, Import::archive_magic_len
) == 0
61 || memcmp(bytes
, armagt
, Import::archive_magic_len
) == 0);
64 // An object used to read an archive file.
69 Archive_file(const std::string
& filename
, int fd
, Location location
)
70 : filename_(filename
), fd_(fd
), filesize_(-1), extended_names_(),
71 is_thin_archive_(false), location_(location
), nested_archives_()
78 // Return the file name.
81 { return this->filename_
; }
86 { return this->filesize_
; }
88 // Return whether this is a thin archive.
90 is_thin_archive() const
91 { return this->is_thin_archive_
; }
93 // Return the location of the import statement.
96 { return this->location_
; }
100 read(off_t offset
, off_t size
, char*);
102 // Read the archive header at OFF, setting *PNAME, *SIZE, and
105 read_header(off_t off
, std::string
* pname
, off_t
* size
, off_t
* nested_off
);
107 // Interpret the header of HDR, the header of the archive member at
108 // file offset OFF. Return whether it succeeded. Set *SIZE to the
109 // size of the member. Set *PNAME to the name of the member. Set
110 // *NESTED_OFF to the offset in a nested archive.
112 interpret_header(const Archive_header
* hdr
, off_t off
,
113 std::string
* pname
, off_t
* size
, off_t
* nested_off
) const;
115 // Get the file and offset for an archive member.
117 get_file_and_offset(off_t off
, const std::string
& hdrname
,
118 off_t nested_off
, int* memfd
, off_t
* memoff
,
119 std::string
* memname
);
122 // For keeping track of open nested archives in a thin archive file.
123 typedef std::map
<std::string
, Archive_file
*> Nested_archive_table
;
125 // The name of the file.
126 std::string filename_
;
127 // The file descriptor.
131 // The extended name table.
132 std::string extended_names_
;
133 // Whether this is a thin archive.
134 bool is_thin_archive_
;
135 // The location of the import statements.
137 // Table of nested archives.
138 Nested_archive_table nested_archives_
;
142 Archive_file::initialize()
145 if (fstat(this->fd_
, &st
) < 0)
147 error_at(this->location_
, "%s: %m", this->filename_
.c_str());
150 this->filesize_
= st
.st_size
;
152 char buf
[sizeof(armagt
)];
153 if (::lseek(this->fd_
, 0, SEEK_SET
) < 0
154 || ::read(this->fd_
, buf
, sizeof(armagt
)) != sizeof(armagt
))
156 error_at(this->location_
, "%s: %m", this->filename_
.c_str());
159 this->is_thin_archive_
= memcmp(buf
, armagt
, sizeof(armagt
)) == 0;
161 if (this->filesize_
== sizeof(armag
))
167 // Look for the extended name table.
168 std::string filename
;
170 if (!this->read_header(sizeof(armagt
), &filename
, &size
, NULL
))
172 if (filename
.empty())
174 // We found the symbol table.
175 off_t off
= sizeof(armagt
) + sizeof(Archive_header
) + size
;
178 if (!this->read_header(off
, &filename
, &size
, NULL
))
183 char* rdbuf
= new char[size
];
184 if (::read(this->fd_
, rdbuf
, size
) != size
)
186 error_at(this->location_
, "%s: could not read extended names",
191 this->extended_names_
.assign(rdbuf
, size
);
198 // Read bytes from the file.
201 Archive_file::read(off_t offset
, off_t size
, char* buf
)
203 if (::lseek(this->fd_
, offset
, SEEK_SET
) < 0
204 || ::read(this->fd_
, buf
, size
) != size
)
206 error_at(this->location_
, "%s: %m", this->filename_
.c_str());
212 // Read the header at OFF. Set *PNAME to the name, *SIZE to the size,
213 // and *NESTED_OFF to the nested offset.
216 Archive_file::read_header(off_t off
, std::string
* pname
, off_t
* size
,
220 if (::lseek(this->fd_
, off
, SEEK_SET
) < 0)
222 error_at(this->location_
, "%s: %m", this->filename_
.c_str());
225 ssize_t got
= ::read(this->fd_
, &hdr
, sizeof hdr
);
226 if (got
!= sizeof hdr
)
229 error_at(this->location_
, "%s: %m", this->filename_
.c_str());
231 error_at(this->location_
, "%s: short archive header at %ld",
232 this->filename_
.c_str(), static_cast<long>(off
));
234 error_at(this->location_
, "%s: unexpected EOF at %ld",
235 this->filename_
.c_str(), static_cast<long>(off
));
237 off_t local_nested_off
;
238 if (!this->interpret_header(&hdr
, off
, pname
, size
, &local_nested_off
))
240 if (nested_off
!= NULL
)
241 *nested_off
= local_nested_off
;
245 // Interpret the header of HDR, the header of the archive member at
249 Archive_file::interpret_header(const Archive_header
* hdr
, off_t off
,
250 std::string
* pname
, off_t
* size
,
251 off_t
* nested_off
) const
253 if (memcmp(hdr
->ar_fmag
, arfmag
, sizeof arfmag
) != 0)
255 error_at(this->location_
, "%s: malformed archive header at %lu",
256 this->filename_
.c_str(), static_cast<unsigned long>(off
));
260 const int size_string_size
= sizeof hdr
->ar_size
;
261 char size_string
[size_string_size
+ 1];
262 memcpy(size_string
, hdr
->ar_size
, size_string_size
);
263 char* ps
= size_string
+ size_string_size
;
264 while (ps
> size_string
&& ps
[-1] == ' ')
270 *size
= strtol(size_string
, &end
, 10);
273 || (*size
== LONG_MAX
&& errno
== ERANGE
))
275 error_at(this->location_
, "%s: malformed archive header size at %lu",
276 this->filename_
.c_str(), static_cast<unsigned long>(off
));
281 if (hdr
->ar_name
[0] != '/')
283 const char* name_end
= strchr(hdr
->ar_name
, '/');
285 || name_end
- hdr
->ar_name
>= static_cast<int>(sizeof hdr
->ar_name
))
287 error_at(this->location_
, "%s: malformed archive header name at %lu",
288 this->filename_
.c_str(), static_cast<unsigned long>(off
));
291 pname
->assign(hdr
->ar_name
, name_end
- hdr
->ar_name
);
293 else if (hdr
->ar_name
[1] == ' ')
295 // This is the symbol table.
298 else if (hdr
->ar_name
[1] == 'S' && hdr
->ar_name
[2] == 'Y'
299 && hdr
->ar_name
[3] == 'M' && hdr
->ar_name
[4] == '6'
300 && hdr
->ar_name
[5] == '4' && hdr
->ar_name
[6] == '/'
301 && hdr
->ar_name
[7] == ' '
304 // 64-bit symbol table.
307 else if (hdr
->ar_name
[1] == '/')
309 // This is the extended name table.
310 pname
->assign(1, '/');
315 long x
= strtol(hdr
->ar_name
+ 1, &end
, 10);
318 y
= strtol(end
+ 1, &end
, 10);
321 || (x
== LONG_MAX
&& errno
== ERANGE
)
322 || static_cast<size_t>(x
) >= this->extended_names_
.size())
324 error_at(this->location_
, "%s: bad extended name index at %lu",
325 this->filename_
.c_str(), static_cast<unsigned long>(off
));
329 const char* name
= this->extended_names_
.data() + x
;
330 const char* name_end
= strchr(name
, '\n');
331 if (static_cast<size_t>(name_end
- name
) > this->extended_names_
.size()
332 || name_end
[-1] != '/')
334 error_at(this->location_
, "%s: bad extended name entry at header %lu",
335 this->filename_
.c_str(), static_cast<unsigned long>(off
));
338 pname
->assign(name
, name_end
- 1 - name
);
345 // Get the file and offset for an archive member.
348 Archive_file::get_file_and_offset(off_t off
, const std::string
& hdrname
,
349 off_t nested_off
, int* memfd
, off_t
* memoff
,
350 std::string
* memname
)
352 if (!this->is_thin_archive_
)
355 *memoff
= off
+ sizeof(Archive_header
);
356 *memname
= this->filename_
+ '(' + hdrname
+ ')';
360 std::string filename
= hdrname
;
361 if (!IS_ABSOLUTE_PATH(filename
.c_str()))
363 const char* archive_path
= this->filename_
.c_str();
364 const char* basename
= lbasename(archive_path
);
365 if (basename
> archive_path
)
366 filename
.replace(0, 0,
367 this->filename_
.substr(0, basename
- archive_path
));
372 // This is a member of a nested archive.
374 Nested_archive_table::const_iterator p
=
375 this->nested_archives_
.find(filename
);
376 if (p
!= this->nested_archives_
.end())
380 int nfd
= open(filename
.c_str(), O_RDONLY
| O_BINARY
);
383 error_at(this->location_
, "%s: can't open nested archive %s",
384 this->filename_
.c_str(), filename
.c_str());
387 nfile
= new Archive_file(filename
, nfd
, this->location_
);
388 if (!nfile
->initialize())
393 this->nested_archives_
[filename
] = nfile
;
399 if (!nfile
->read_header(nested_off
, &nname
, &nsize
, &nnested_off
))
401 return nfile
->get_file_and_offset(nested_off
, nname
, nnested_off
,
402 memfd
, memoff
, memname
);
405 // An external member of a thin archive.
406 *memfd
= open(filename
.c_str(), O_RDONLY
| O_BINARY
);
409 error_at(this->location_
, "%s: %m", filename
.c_str());
417 // An archive member iterator. This is more-or-less copied from gold.
419 class Archive_iterator
422 // The header of an archive member. This is what this iterator
426 // The name of the member.
428 // The file offset of the member.
430 // The file offset of a nested archive member.
432 // The size of the member.
436 Archive_iterator(Archive_file
* afile
, off_t off
)
437 : afile_(afile
), off_(off
)
438 { this->read_next_header(); }
442 { return this->header_
; }
446 { return &this->header_
; }
451 if (this->off_
== this->afile_
->filesize())
453 this->off_
+= sizeof(Archive_header
);
454 if (!this->afile_
->is_thin_archive())
455 this->off_
+= this->header_
.size
;
456 if ((this->off_
& 1) != 0)
458 this->read_next_header();
465 Archive_iterator ret
= *this;
471 operator==(const Archive_iterator p
) const
472 { return this->off_
== p
->off
; }
475 operator!=(const Archive_iterator p
) const
476 { return this->off_
!= p
->off
; }
482 // The underlying archive file.
483 Archive_file
* afile_
;
484 // The current offset in the file.
486 // The current archive header.
490 // Read the next archive header.
493 Archive_iterator::read_next_header()
495 off_t filesize
= this->afile_
->filesize();
498 if (filesize
- this->off_
< static_cast<off_t
>(sizeof(Archive_header
)))
500 if (filesize
!= this->off_
)
502 error_at(this->afile_
->location(),
503 "%s: short archive header at %lu",
504 this->afile_
->filename().c_str(),
505 static_cast<unsigned long>(this->off_
));
506 this->off_
= filesize
;
508 this->header_
.off
= filesize
;
512 char buf
[sizeof(Archive_header
)];
513 if (!this->afile_
->read(this->off_
, sizeof(Archive_header
), buf
))
515 this->header_
.off
= filesize
;
519 const Archive_header
* hdr
= reinterpret_cast<const Archive_header
*>(buf
);
520 if (!this->afile_
->interpret_header(hdr
, this->off_
, &this->header_
.name
,
522 &this->header_
.nested_off
))
524 this->header_
.off
= filesize
;
527 this->header_
.off
= this->off_
;
529 // Skip special members.
530 if (!this->header_
.name
.empty() && this->header_
.name
!= "/")
533 this->off_
+= sizeof(Archive_header
) + this->header_
.size
;
534 if ((this->off_
& 1) != 0)
542 archive_begin(Archive_file
* afile
)
544 return Archive_iterator(afile
, sizeof(armag
));
550 archive_end(Archive_file
* afile
)
552 return Archive_iterator(afile
, afile
->filesize());
555 // A type of Import_stream which concatenates other Import_streams
558 class Stream_concatenate
: public Import::Stream
567 add(Import::Stream
* is
)
568 { this->inputs_
.push_back(is
); }
572 do_peek(size_t, const char**);
578 std::list
<Import::Stream
*> inputs_
;
584 Stream_concatenate::do_peek(size_t length
, const char** bytes
)
588 if (this->inputs_
.empty())
590 if (this->inputs_
.front()->peek(length
, bytes
))
592 delete this->inputs_
.front();
593 this->inputs_
.pop_front();
600 Stream_concatenate::do_advance(size_t skip
)
604 if (this->inputs_
.empty())
606 if (!this->inputs_
.front()->at_eof())
608 // We just assume that this will do the right thing. It
609 // should be OK since we should never want to skip past
611 this->inputs_
.front()->advance(skip
);
614 delete this->inputs_
.front();
615 this->inputs_
.pop_front();
619 // Import data from an archive. We walk through the archive and
620 // import data from each member.
623 Import::find_archive_export_data(const std::string
& filename
, int fd
,
626 Archive_file
afile(filename
, fd
, location
);
627 if (!afile
.initialize())
630 Stream_concatenate
* ret
= new Stream_concatenate
;
632 bool any_data
= false;
633 bool any_members
= false;
634 Archive_iterator pend
= archive_end(&afile
);
635 for (Archive_iterator p
= archive_begin(&afile
); p
!= pend
; p
++)
640 std::string member_name
;
641 if (!afile
.get_file_and_offset(p
->off
, p
->name
, p
->nested_off
,
642 &member_fd
, &member_off
, &member_name
))
645 Import::Stream
* is
= Import::find_object_export_data(member_name
,
658 // It's normal to have an empty archive file when using gobuild.
659 return new Stream_from_string("");