2014-10-24 Richard Biener <rguenther@suse.de>
[official-gcc.git] / gcc / go / gofrontend / import-archive.cc
blob43057558228c4ecb65268de22bace67e15223f02
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.
7 #include "go-system.h"
9 #include "import.h"
11 #ifndef O_BINARY
12 #define O_BINARY 0
13 #endif
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.
32 struct Archive_header
34 // The entry name.
35 char ar_name[16];
36 // The file modification time.
37 char ar_date[12];
38 // The user's UID in decimal.
39 char ar_uid[6];
40 // The user's GID in decimal.
41 char ar_gid[6];
42 // The file mode in octal.
43 char ar_mode[8];
44 // The file size in decimal.
45 char ar_size[10];
46 // The final magic code.
47 char ar_fmag[2];
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.
57 bool
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.
66 class Archive_file
68 public:
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_()
72 { }
74 // Initialize.
75 bool
76 initialize();
78 // Return the file name.
79 const std::string&
80 filename() const
81 { return this->filename_; }
83 // Get the file size.
84 off_t
85 filesize() const
86 { return this->filesize_; }
88 // Return whether this is a thin archive.
89 bool
90 is_thin_archive() const
91 { return this->is_thin_archive_; }
93 // Return the location of the import statement.
94 Location
95 location() const
96 { return this->location_; }
98 // Read bytes.
99 bool
100 read(off_t offset, off_t size, char*);
102 // Read the archive header at OFF, setting *PNAME, *SIZE, and
103 // *NESTED_OFF.
104 bool
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.
111 bool
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.
116 bool
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);
121 private:
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.
128 int fd_;
129 // The file size;
130 off_t filesize_;
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 Location location_;
137 // Table of nested archives.
138 Nested_archive_table nested_archives_;
141 bool
142 Archive_file::initialize()
144 struct stat st;
145 if (fstat(this->fd_, &st) < 0)
147 error_at(this->location_, "%s: %m", this->filename_.c_str());
148 return false;
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());
157 return false;
159 this->is_thin_archive_ = memcmp(buf, armagt, sizeof(armagt)) == 0;
161 if (this->filesize_ == sizeof(armag))
163 // Empty archive.
164 return true;
167 // Look for the extended name table.
168 std::string filename;
169 off_t size;
170 if (!this->read_header(sizeof(armagt), &filename, &size, NULL))
171 return false;
172 if (filename.empty())
174 // We found the symbol table.
175 off_t off = sizeof(armagt) + sizeof(Archive_header) + size;
176 if ((off & 1) != 0)
177 ++off;
178 if (!this->read_header(off, &filename, &size, NULL))
179 filename.clear();
181 if (filename == "/")
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",
187 filename.c_str());
188 delete[] rdbuf;
189 return false;
191 this->extended_names_.assign(rdbuf, size);
192 delete[] rdbuf;
195 return true;
198 // Read bytes from the file.
200 bool
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());
207 return false;
209 return true;
212 // Read the header at OFF. Set *PNAME to the name, *SIZE to the size,
213 // and *NESTED_OFF to the nested offset.
215 bool
216 Archive_file::read_header(off_t off, std::string* pname, off_t* size,
217 off_t* nested_off)
219 Archive_header hdr;
220 if (::lseek(this->fd_, off, SEEK_SET) < 0)
222 error_at(this->location_, "%s: %m", this->filename_.c_str());
223 return false;
225 ssize_t got = ::read(this->fd_, &hdr, sizeof hdr);
226 if (got != sizeof hdr)
228 if (got < 0)
229 error_at(this->location_, "%s: %m", this->filename_.c_str());
230 else if (got > 0)
231 error_at(this->location_, "%s: short archive header at %ld",
232 this->filename_.c_str(), static_cast<long>(off));
233 else
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))
239 return false;
240 if (nested_off != NULL)
241 *nested_off = local_nested_off;
242 return true;
245 // Interpret the header of HDR, the header of the archive member at
246 // file offset OFF.
248 bool
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));
257 return false;
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] == ' ')
265 --ps;
266 *ps = '\0';
268 errno = 0;
269 char* end;
270 *size = strtol(size_string, &end, 10);
271 if (*end != '\0'
272 || *size < 0
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));
277 return false;
280 *nested_off = 0;
281 if (hdr->ar_name[0] != '/')
283 const char* name_end = strchr(hdr->ar_name, '/');
284 if (name_end == NULL
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));
289 return false;
291 pname->assign(hdr->ar_name, name_end - hdr->ar_name);
293 else if (hdr->ar_name[1] == ' ')
295 // This is the symbol table.
296 pname->clear();
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.
305 pname->clear();
307 else if (hdr->ar_name[1] == '/')
309 // This is the extended name table.
310 pname->assign(1, '/');
312 else
314 errno = 0;
315 long x = strtol(hdr->ar_name + 1, &end, 10);
316 long y = 0;
317 if (*end == ':')
318 y = strtol(end + 1, &end, 10);
319 if (*end != ' '
320 || x < 0
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));
326 return false;
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));
336 return false;
338 pname->assign(name, name_end - 1 - name);
339 *nested_off = y;
342 return true;
345 // Get the file and offset for an archive member.
347 bool
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_)
354 *memfd = this->fd_;
355 *memoff = off + sizeof(Archive_header);
356 *memname = this->filename_ + '(' + hdrname + ')';
357 return true;
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));
370 if (nested_off > 0)
372 // This is a member of a nested archive.
373 Archive_file* nfile;
374 Nested_archive_table::const_iterator p =
375 this->nested_archives_.find(filename);
376 if (p != this->nested_archives_.end())
377 nfile = p->second;
378 else
380 int nfd = open(filename.c_str(), O_RDONLY | O_BINARY);
381 if (nfd < 0)
383 error_at(this->location_, "%s: can't open nested archive %s",
384 this->filename_.c_str(), filename.c_str());
385 return false;
387 nfile = new Archive_file(filename, nfd, this->location_);
388 if (!nfile->initialize())
390 delete nfile;
391 return false;
393 this->nested_archives_[filename] = nfile;
396 std::string nname;
397 off_t nsize;
398 off_t nnested_off;
399 if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off))
400 return false;
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);
407 if (*memfd < 0)
409 error_at(this->location_, "%s: %m", filename.c_str());
410 return false;
412 *memoff = 0;
413 *memname = filename;
414 return true;
417 // An archive member iterator. This is more-or-less copied from gold.
419 class Archive_iterator
421 public:
422 // The header of an archive member. This is what this iterator
423 // points to.
424 struct Header
426 // The name of the member.
427 std::string name;
428 // The file offset of the member.
429 off_t off;
430 // The file offset of a nested archive member.
431 off_t nested_off;
432 // The size of the member.
433 off_t size;
436 Archive_iterator(Archive_file* afile, off_t off)
437 : afile_(afile), off_(off)
438 { this->read_next_header(); }
440 const Header&
441 operator*() const
442 { return this->header_; }
444 const Header*
445 operator->() const
446 { return &this->header_; }
448 Archive_iterator&
449 operator++()
451 if (this->off_ == this->afile_->filesize())
452 return *this;
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)
457 ++this->off_;
458 this->read_next_header();
459 return *this;
462 Archive_iterator
463 operator++(int)
465 Archive_iterator ret = *this;
466 ++*this;
467 return ret;
470 bool
471 operator==(const Archive_iterator p) const
472 { return this->off_ == p->off; }
474 bool
475 operator!=(const Archive_iterator p) const
476 { return this->off_ != p->off; }
478 private:
479 void
480 read_next_header();
482 // The underlying archive file.
483 Archive_file* afile_;
484 // The current offset in the file.
485 off_t off_;
486 // The current archive header.
487 Header header_;
490 // Read the next archive header.
492 void
493 Archive_iterator::read_next_header()
495 off_t filesize = this->afile_->filesize();
496 while (true)
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;
509 return;
512 char buf[sizeof(Archive_header)];
513 if (!this->afile_->read(this->off_, sizeof(Archive_header), buf))
515 this->header_.off = filesize;
516 return;
519 const Archive_header* hdr = reinterpret_cast<const Archive_header*>(buf);
520 if (!this->afile_->interpret_header(hdr, this->off_, &this->header_.name,
521 &this->header_.size,
522 &this->header_.nested_off))
524 this->header_.off = filesize;
525 return;
527 this->header_.off = this->off_;
529 // Skip special members.
530 if (!this->header_.name.empty() && this->header_.name != "/")
531 return;
533 this->off_ += sizeof(Archive_header) + this->header_.size;
534 if ((this->off_ & 1) != 0)
535 ++this->off_;
539 // Initial iterator.
541 Archive_iterator
542 archive_begin(Archive_file* afile)
544 return Archive_iterator(afile, sizeof(armag));
547 // Final iterator.
549 Archive_iterator
550 archive_end(Archive_file* afile)
552 return Archive_iterator(afile, afile->filesize());
555 // A type of Import_stream which concatenates other Import_streams
556 // together.
558 class Stream_concatenate : public Import::Stream
560 public:
561 Stream_concatenate()
562 : inputs_()
565 // Add a new stream.
566 void
567 add(Import::Stream* is)
568 { this->inputs_.push_back(is); }
570 protected:
571 bool
572 do_peek(size_t, const char**);
574 void
575 do_advance(size_t);
577 private:
578 std::list<Import::Stream*> inputs_;
581 // Peek ahead.
583 bool
584 Stream_concatenate::do_peek(size_t length, const char** bytes)
586 while (true)
588 if (this->inputs_.empty())
589 return false;
590 if (this->inputs_.front()->peek(length, bytes))
591 return true;
592 delete this->inputs_.front();
593 this->inputs_.pop_front();
597 // Advance.
599 void
600 Stream_concatenate::do_advance(size_t skip)
602 while (true)
604 if (this->inputs_.empty())
605 return;
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
610 // multiple streams.
611 this->inputs_.front()->advance(skip);
612 return;
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.
622 Import::Stream*
623 Import::find_archive_export_data(const std::string& filename, int fd,
624 Location location)
626 Archive_file afile(filename, fd, location);
627 if (!afile.initialize())
628 return NULL;
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++)
637 any_members = true;
638 int member_fd;
639 off_t member_off;
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))
643 return NULL;
645 Import::Stream* is = Import::find_object_export_data(member_name,
646 member_fd,
647 member_off,
648 location);
649 if (is != NULL)
651 ret->add(is);
652 any_data = true;
656 if (!any_members)
658 // It's normal to have an empty archive file when using gobuild.
659 return new Stream_from_string("");
662 if (!any_data)
664 delete ret;
665 return NULL;
668 return ret;