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
, source_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.
136 source_location location_
;
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
));
280 if (hdr
->ar_name
[0] != '/')
282 const char* name_end
= strchr(hdr
->ar_name
, '/');
284 || name_end
- hdr
->ar_name
>= static_cast<int>(sizeof hdr
->ar_name
))
286 error_at(this->location_
, "%s: malformed archive header name at %lu",
287 this->filename_
.c_str(), static_cast<unsigned long>(off
));
290 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
);
330 if (nested_off
!= NULL
)
337 // Get the file and offset for an archive member.
340 Archive_file::get_file_and_offset(off_t off
, const std::string
& hdrname
,
341 off_t nested_off
, int* memfd
, off_t
* memoff
,
342 std::string
* memname
)
344 if (!this->is_thin_archive_
)
347 *memoff
= off
+ sizeof(Archive_header
);
348 *memname
= this->filename_
+ '(' + hdrname
+ ')';
352 std::string filename
= hdrname
;
353 if (!IS_ABSOLUTE_PATH(filename
.c_str()))
355 const char* archive_path
= this->filename_
.c_str();
356 const char* basename
= lbasename(archive_path
);
357 if (basename
> archive_path
)
358 filename
.replace(0, 0,
359 this->filename_
.substr(0, basename
- archive_path
));
364 // This is a member of a nested archive.
366 Nested_archive_table::const_iterator p
=
367 this->nested_archives_
.find(filename
);
368 if (p
!= this->nested_archives_
.end())
372 int nfd
= open(filename
.c_str(), O_RDONLY
| O_BINARY
);
375 error_at(this->location_
, "%s: can't open nested archive %s",
376 this->filename_
.c_str(), filename
.c_str());
379 nfile
= new Archive_file(filename
, nfd
, this->location_
);
380 if (!nfile
->initialize())
385 this->nested_archives_
[filename
] = nfile
;
391 if (!nfile
->read_header(nested_off
, &nname
, &nsize
, &nnested_off
))
393 return nfile
->get_file_and_offset(nested_off
, nname
, nnested_off
,
394 memfd
, memoff
, memname
);
397 // An external member of a thin archive.
398 *memfd
= open(filename
.c_str(), O_RDONLY
| O_BINARY
);
401 error_at(this->location_
, "%s: %m", filename
.c_str());
409 // An archive member iterator. This is more-or-less copied from gold.
411 class Archive_iterator
414 // The header of an archive member. This is what this iterator
418 // The name of the member.
420 // The file offset of the member.
422 // The file offset of a nested archive member.
424 // The size of the member.
428 Archive_iterator(Archive_file
* afile
, off_t off
)
429 : afile_(afile
), off_(off
)
430 { this->read_next_header(); }
434 { return this->header_
; }
438 { return &this->header_
; }
443 if (this->off_
== this->afile_
->filesize())
445 this->off_
+= sizeof(Archive_header
);
446 if (!this->afile_
->is_thin_archive())
447 this->off_
+= this->header_
.size
;
448 if ((this->off_
& 1) != 0)
450 this->read_next_header();
457 Archive_iterator ret
= *this;
463 operator==(const Archive_iterator p
) const
464 { return this->off_
== p
->off
; }
467 operator!=(const Archive_iterator p
) const
468 { return this->off_
!= p
->off
; }
474 // The underlying archive file.
475 Archive_file
* afile_
;
476 // The current offset in the file.
478 // The current archive header.
482 // Read the next archive header.
485 Archive_iterator::read_next_header()
487 off_t filesize
= this->afile_
->filesize();
490 if (filesize
- this->off_
< static_cast<off_t
>(sizeof(Archive_header
)))
492 if (filesize
!= this->off_
)
494 error_at(this->afile_
->location(),
495 "%s: short archive header at %lu",
496 this->afile_
->filename().c_str(),
497 static_cast<unsigned long>(this->off_
));
498 this->off_
= filesize
;
500 this->header_
.off
= filesize
;
504 char buf
[sizeof(Archive_header
)];
505 if (!this->afile_
->read(this->off_
, sizeof(Archive_header
), buf
))
507 this->header_
.off
= filesize
;
511 const Archive_header
* hdr
= reinterpret_cast<const Archive_header
*>(buf
);
512 if (!this->afile_
->interpret_header(hdr
, this->off_
, &this->header_
.name
,
514 &this->header_
.nested_off
))
516 this->header_
.off
= filesize
;
519 this->header_
.off
= this->off_
;
521 // Skip special members.
522 if (!this->header_
.name
.empty() && this->header_
.name
!= "/")
525 this->off_
+= sizeof(Archive_header
) + this->header_
.size
;
526 if ((this->off_
& 1) != 0)
534 archive_begin(Archive_file
* afile
)
536 return Archive_iterator(afile
, sizeof(armag
));
542 archive_end(Archive_file
* afile
)
544 return Archive_iterator(afile
, afile
->filesize());
547 // A type of Import_stream which concatenates other Import_streams
550 class Stream_concatenate
: public Import::Stream
559 add(Import::Stream
* is
)
560 { this->inputs_
.push_back(is
); }
564 do_peek(size_t, const char**);
570 std::list
<Import::Stream
*> inputs_
;
576 Stream_concatenate::do_peek(size_t length
, const char** bytes
)
580 if (this->inputs_
.empty())
582 if (this->inputs_
.front()->peek(length
, bytes
))
584 delete this->inputs_
.front();
585 this->inputs_
.pop_front();
592 Stream_concatenate::do_advance(size_t skip
)
596 if (this->inputs_
.empty())
598 if (!this->inputs_
.front()->at_eof())
600 // We just assume that this will do the right thing. It
601 // should be OK since we should never want to skip past
603 this->inputs_
.front()->advance(skip
);
606 delete this->inputs_
.front();
607 this->inputs_
.pop_front();
611 // Import data from an archive. We walk through the archive and
612 // import data from each member.
615 Import::find_archive_export_data(const std::string
& filename
, int fd
,
616 source_location location
)
618 Archive_file
afile(filename
, fd
, location
);
619 if (!afile
.initialize())
622 Stream_concatenate
* ret
= new Stream_concatenate
;
624 bool any_data
= false;
625 bool any_members
= false;
626 Archive_iterator pend
= archive_end(&afile
);
627 for (Archive_iterator p
= archive_begin(&afile
); p
!= pend
; p
++)
632 std::string member_name
;
633 if (!afile
.get_file_and_offset(p
->off
, p
->name
, p
->nested_off
,
634 &member_fd
, &member_off
, &member_name
))
637 Import::Stream
* is
= Import::find_object_export_data(member_name
,
650 // It's normal to have an empty archive file when using gobuild.
651 return new Stream_from_string("");