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
[-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] == '/')
300 // This is the extended name table.
301 pname
->assign(1, '/');
306 long x
= strtol(hdr
->ar_name
+ 1, &end
, 10);
309 y
= strtol(end
+ 1, &end
, 10);
312 || (x
== LONG_MAX
&& errno
== ERANGE
)
313 || static_cast<size_t>(x
) >= this->extended_names_
.size())
315 error_at(this->location_
, "%s: bad extended name index at %lu",
316 this->filename_
.c_str(), static_cast<unsigned long>(off
));
320 const char* name
= this->extended_names_
.data() + x
;
321 const char* name_end
= strchr(name
, '\n');
322 if (static_cast<size_t>(name_end
- name
) > this->extended_names_
.size()
323 || name_end
[-1] != '/')
325 error_at(this->location_
, "%s: bad extended name entry at header %lu",
326 this->filename_
.c_str(), static_cast<unsigned long>(off
));
329 pname
->assign(name
, name_end
- 1 - name
);
336 // Get the file and offset for an archive member.
339 Archive_file::get_file_and_offset(off_t off
, const std::string
& hdrname
,
340 off_t nested_off
, int* memfd
, off_t
* memoff
,
341 std::string
* memname
)
343 if (!this->is_thin_archive_
)
346 *memoff
= off
+ sizeof(Archive_header
);
347 *memname
= this->filename_
+ '(' + hdrname
+ ')';
351 std::string filename
= hdrname
;
352 if (!IS_ABSOLUTE_PATH(filename
.c_str()))
354 const char* archive_path
= this->filename_
.c_str();
355 const char* basename
= lbasename(archive_path
);
356 if (basename
> archive_path
)
357 filename
.replace(0, 0,
358 this->filename_
.substr(0, basename
- archive_path
));
363 // This is a member of a nested archive.
365 Nested_archive_table::const_iterator p
=
366 this->nested_archives_
.find(filename
);
367 if (p
!= this->nested_archives_
.end())
371 int nfd
= open(filename
.c_str(), O_RDONLY
| O_BINARY
);
374 error_at(this->location_
, "%s: can't open nested archive %s",
375 this->filename_
.c_str(), filename
.c_str());
378 nfile
= new Archive_file(filename
, nfd
, this->location_
);
379 if (!nfile
->initialize())
384 this->nested_archives_
[filename
] = nfile
;
390 if (!nfile
->read_header(nested_off
, &nname
, &nsize
, &nnested_off
))
392 return nfile
->get_file_and_offset(nested_off
, nname
, nnested_off
,
393 memfd
, memoff
, memname
);
396 // An external member of a thin archive.
397 *memfd
= open(filename
.c_str(), O_RDONLY
| O_BINARY
);
400 error_at(this->location_
, "%s: %m", filename
.c_str());
408 // An archive member iterator. This is more-or-less copied from gold.
410 class Archive_iterator
413 // The header of an archive member. This is what this iterator
417 // The name of the member.
419 // The file offset of the member.
421 // The file offset of a nested archive member.
423 // The size of the member.
427 Archive_iterator(Archive_file
* afile
, off_t off
)
428 : afile_(afile
), off_(off
)
429 { this->read_next_header(); }
433 { return this->header_
; }
437 { return &this->header_
; }
442 if (this->off_
== this->afile_
->filesize())
444 this->off_
+= sizeof(Archive_header
);
445 if (!this->afile_
->is_thin_archive())
446 this->off_
+= this->header_
.size
;
447 if ((this->off_
& 1) != 0)
449 this->read_next_header();
456 Archive_iterator ret
= *this;
462 operator==(const Archive_iterator p
) const
463 { return this->off_
== p
->off
; }
466 operator!=(const Archive_iterator p
) const
467 { return this->off_
!= p
->off
; }
473 // The underlying archive file.
474 Archive_file
* afile_
;
475 // The current offset in the file.
477 // The current archive header.
481 // Read the next archive header.
484 Archive_iterator::read_next_header()
486 off_t filesize
= this->afile_
->filesize();
489 if (filesize
- this->off_
< static_cast<off_t
>(sizeof(Archive_header
)))
491 if (filesize
!= this->off_
)
493 error_at(this->afile_
->location(),
494 "%s: short archive header at %lu",
495 this->afile_
->filename().c_str(),
496 static_cast<unsigned long>(this->off_
));
497 this->off_
= filesize
;
499 this->header_
.off
= filesize
;
503 char buf
[sizeof(Archive_header
)];
504 if (!this->afile_
->read(this->off_
, sizeof(Archive_header
), buf
))
506 this->header_
.off
= filesize
;
510 const Archive_header
* hdr
= reinterpret_cast<const Archive_header
*>(buf
);
511 if (!this->afile_
->interpret_header(hdr
, this->off_
, &this->header_
.name
,
513 &this->header_
.nested_off
))
515 this->header_
.off
= filesize
;
518 this->header_
.off
= this->off_
;
520 // Skip special members.
521 if (!this->header_
.name
.empty() && this->header_
.name
!= "/")
524 this->off_
+= sizeof(Archive_header
) + this->header_
.size
;
525 if ((this->off_
& 1) != 0)
533 archive_begin(Archive_file
* afile
)
535 return Archive_iterator(afile
, sizeof(armag
));
541 archive_end(Archive_file
* afile
)
543 return Archive_iterator(afile
, afile
->filesize());
546 // A type of Import_stream which concatenates other Import_streams
549 class Stream_concatenate
: public Import::Stream
558 add(Import::Stream
* is
)
559 { this->inputs_
.push_back(is
); }
563 do_peek(size_t, const char**);
569 std::list
<Import::Stream
*> inputs_
;
575 Stream_concatenate::do_peek(size_t length
, const char** bytes
)
579 if (this->inputs_
.empty())
581 if (this->inputs_
.front()->peek(length
, bytes
))
583 delete this->inputs_
.front();
584 this->inputs_
.pop_front();
591 Stream_concatenate::do_advance(size_t skip
)
595 if (this->inputs_
.empty())
597 if (!this->inputs_
.front()->at_eof())
599 // We just assume that this will do the right thing. It
600 // should be OK since we should never want to skip past
602 this->inputs_
.front()->advance(skip
);
605 delete this->inputs_
.front();
606 this->inputs_
.pop_front();
610 // Import data from an archive. We walk through the archive and
611 // import data from each member.
614 Import::find_archive_export_data(const std::string
& filename
, int fd
,
617 Archive_file
afile(filename
, fd
, location
);
618 if (!afile
.initialize())
621 Stream_concatenate
* ret
= new Stream_concatenate
;
623 bool any_data
= false;
624 bool any_members
= false;
625 Archive_iterator pend
= archive_end(&afile
);
626 for (Archive_iterator p
= archive_begin(&afile
); p
!= pend
; p
++)
631 std::string member_name
;
632 if (!afile
.get_file_and_offset(p
->off
, p
->name
, p
->nested_off
,
633 &member_fd
, &member_off
, &member_name
))
636 Import::Stream
* is
= Import::find_object_export_data(member_name
,
649 // It's normal to have an empty archive file when using gobuild.
650 return new Stream_from_string("");