OSDN Git Service

PR go/47158
[pf3gnuchains/gcc-fork.git] / gcc / go / gofrontend / import-archive.cc
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 "import.h"
10
11 #ifndef O_BINARY
12 #define O_BINARY 0
13 #endif
14
15 // Archive magic numbers.
16
17 static const char armag[] =
18 {
19   '!', '<', 'a', 'r', 'c', 'h', '>', '\n'
20 };
21
22 static const char armagt[] =
23 {
24   '!', '<', 't', 'h', 'i', 'n', '>', '\n'
25 };
26
27 static const char arfmag[2] = { '`', '\n' };
28
29 // The header of an entry in an archive.  This is all readable text,
30 // padded with spaces where necesary.
31
32 struct Archive_header
33 {
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];
48 };
49
50 // The functions in this file extract Go export data from an archive.
51
52 const int Import::archive_magic_len;
53
54 // Return true if BYTES, which are from the start of the file, are an
55 // archive magic number.
56
57 bool
58 Import::is_archive_magic(const char* bytes)
59 {
60   return (memcmp(bytes, armag, Import::archive_magic_len) == 0
61           || memcmp(bytes, armagt, Import::archive_magic_len) == 0);
62 }
63
64 // An object used to read an archive file.
65
66 class Archive_file
67 {
68  public:
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_()
72   { }
73
74   // Initialize.
75   bool
76   initialize();
77
78   // Return the file name.
79   const std::string&
80   filename() const
81   { return this->filename_; }
82
83   // Get the file size.
84   off_t
85   filesize() const
86   { return this->filesize_; }
87
88   // Return whether this is a thin archive.
89   bool
90   is_thin_archive() const
91   { return this->is_thin_archive_; }
92
93   // Return the location of the import statement.
94   source_location
95   location() const
96   { return this->location_; }
97
98   // Read bytes.
99   bool
100   read(off_t offset, off_t size, char*);
101
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);
106
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;
114
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);
120
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;
124
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   source_location location_;
137   // Table of nested archives.
138   Nested_archive_table nested_archives_;
139 };
140
141 bool
142 Archive_file::initialize()
143 {
144   struct stat st;
145   if (fstat(this->fd_, &st) < 0)
146     {
147       error_at(this->location_, "%s: %m", this->filename_.c_str());
148       return false;
149     }
150   this->filesize_ = st.st_size;
151
152   char buf[sizeof(armagt)];
153   if (::lseek(this->fd_, 0, SEEK_SET) < 0
154       || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt))
155     {
156       error_at(this->location_, "%s: %m", this->filename_.c_str());
157       return false;
158     }
159   this->is_thin_archive_ = memcmp(buf, armagt, sizeof(armagt)) == 0;
160
161   if (this->filesize_ == sizeof(armag))
162     {
163       // Empty archive.
164       return true;
165     }
166
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())
173     {
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();
180     }
181   if (filename == "/")
182     {
183       char* rdbuf = new char[size];
184       if (::read(this->fd_, rdbuf, size) != size)
185         {
186           error_at(this->location_, "%s: could not read extended names",
187                    filename.c_str());
188           delete[] rdbuf;
189           return false;
190         }
191       this->extended_names_.assign(rdbuf, size);
192       delete[] rdbuf;
193     }
194
195   return true;
196 }
197
198 // Read bytes from the file.
199
200 bool
201 Archive_file::read(off_t offset, off_t size, char* buf)
202 {
203   if (::lseek(this->fd_, offset, SEEK_SET) < 0
204       || ::read(this->fd_, buf, size) != size)
205     {
206       error_at(this->location_, "%s: %m", this->filename_.c_str());
207       return false;
208     }
209   return true;
210 }
211
212 // Read the header at OFF.  Set *PNAME to the name, *SIZE to the size,
213 // and *NESTED_OFF to the nested offset.
214
215 bool
216 Archive_file::read_header(off_t off, std::string* pname, off_t* size,
217                           off_t* nested_off)
218 {
219   Archive_header hdr;
220   if (::lseek(this->fd_, off, SEEK_SET) < 0)
221     {
222       error_at(this->location_, "%s: %m", this->filename_.c_str());
223       return false;
224     }
225   ssize_t got = ::read(this->fd_, &hdr, sizeof hdr);
226   if (got != sizeof hdr)
227     {
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));
236     }
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;
243 }
244
245 // Interpret the header of HDR, the header of the archive member at
246 // file offset OFF.
247
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
252 {
253   if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0)
254     {
255       error_at(this->location_, "%s: malformed archive header at %lu",
256                this->filename_.c_str(), static_cast<unsigned long>(off));
257       return false;
258     }
259
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] == ' ')
265     --ps;
266   *ps = '\0';
267
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))
274     {
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;
278     }
279
280   if (hdr->ar_name[0] != '/')
281     {
282       const char* name_end = strchr(hdr->ar_name, '/');
283       if (name_end == NULL
284           || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name))
285         {
286           error_at(this->location_, "%s: malformed archive header name at %lu",
287                    this->filename_.c_str(), static_cast<unsigned long>(off));
288           return false;
289         }
290       pname->assign(hdr->ar_name, name_end - hdr->ar_name);
291       *nested_off = 0;
292     }
293   else if (hdr->ar_name[1] == ' ')
294     {
295       // This is the symbol table.
296       pname->clear();
297     }
298   else if (hdr->ar_name[1] == '/')
299     {
300       // This is the extended name table.
301       pname->assign(1, '/');
302     }
303   else
304     {
305       errno = 0;
306       long x = strtol(hdr->ar_name + 1, &end, 10);
307       long y = 0;
308       if (*end == ':')
309         y = strtol(end + 1, &end, 10);
310       if (*end != ' '
311           || x < 0
312           || (x == LONG_MAX && errno == ERANGE)
313           || static_cast<size_t>(x) >= this->extended_names_.size())
314         {
315           error_at(this->location_, "%s: bad extended name index at %lu",
316                    this->filename_.c_str(), static_cast<unsigned long>(off));
317           return false;
318         }
319
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] != '/')
324         {
325           error_at(this->location_, "%s: bad extended name entry at header %lu",
326                    this->filename_.c_str(), static_cast<unsigned long>(off));
327           return false;
328         }
329       pname->assign(name, name_end - 1 - name);
330       if (nested_off != NULL)
331         *nested_off = y;
332     }
333
334   return true;
335 }
336
337 // Get the file and offset for an archive member.
338
339 bool
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)
343 {
344   if (!this->is_thin_archive_)
345     {
346       *memfd = this->fd_;
347       *memoff = off + sizeof(Archive_header);
348       *memname = this->filename_ + '(' + hdrname + ')';
349       return true;
350     }
351
352   std::string filename = hdrname;
353   if (!IS_ABSOLUTE_PATH(filename.c_str()))
354     {
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));
360     }
361
362   if (nested_off > 0)
363     {
364       // This is a member of a nested archive.
365       Archive_file* nfile;
366       Nested_archive_table::const_iterator p =
367         this->nested_archives_.find(filename);
368       if (p != this->nested_archives_.end())
369         nfile = p->second;
370       else
371         {
372           int nfd = open(filename.c_str(), O_RDONLY | O_BINARY);
373           if (nfd < 0)
374             {
375               error_at(this->location_, "%s: can't open nested archive %s",
376                        this->filename_.c_str(), filename.c_str());
377               return false;
378             }
379           nfile = new Archive_file(filename, nfd, this->location_);
380           if (!nfile->initialize())
381             {
382               delete nfile;
383               return false;
384             }
385           this->nested_archives_[filename] = nfile;
386         }
387
388       std::string nname;
389       off_t nsize;
390       off_t nnested_off;
391       if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off))
392         return false;
393       return nfile->get_file_and_offset(nested_off, nname, nnested_off,
394                                         memfd, memoff, memname);
395     }
396
397   // An external member of a thin archive.
398   *memfd = open(filename.c_str(), O_RDONLY | O_BINARY);
399   if (*memfd < 0)
400     {
401       error_at(this->location_, "%s: %m", filename.c_str());
402       return false;
403     }
404   *memoff = 0;
405   *memname = filename;
406   return true;
407 }
408
409 // An archive member iterator.  This is more-or-less copied from gold.
410
411 class Archive_iterator
412 {
413  public:
414   // The header of an archive member.  This is what this iterator
415   // points to.
416   struct Header
417   {
418     // The name of the member.
419     std::string name;
420     // The file offset of the member.
421     off_t off;
422     // The file offset of a nested archive member.
423     off_t nested_off;
424     // The size of the member.
425     off_t size;
426   };
427
428   Archive_iterator(Archive_file* afile, off_t off)
429     : afile_(afile), off_(off)
430   { this->read_next_header(); }
431
432   const Header&
433   operator*() const
434   { return this->header_; }
435
436   const Header*
437   operator->() const
438   { return &this->header_; }
439
440   Archive_iterator&
441   operator++()
442   {
443     if (this->off_ == this->afile_->filesize())
444       return *this;
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)
449       ++this->off_;
450     this->read_next_header();
451     return *this;
452   }
453
454   Archive_iterator
455   operator++(int)
456   {
457     Archive_iterator ret = *this;
458     ++*this;
459     return ret;
460   }
461
462   bool
463   operator==(const Archive_iterator p) const
464   { return this->off_ == p->off; }
465
466   bool
467   operator!=(const Archive_iterator p) const
468   { return this->off_ != p->off; }
469
470  private:
471   void
472   read_next_header();
473
474   // The underlying archive file.
475   Archive_file* afile_;
476   // The current offset in the file.
477   off_t off_;
478   // The current archive header.
479   Header header_;
480 };
481
482 // Read the next archive header.
483
484 void
485 Archive_iterator::read_next_header()
486 {
487   off_t filesize = this->afile_->filesize();
488   while (true)
489     {
490       if (filesize - this->off_ < static_cast<off_t>(sizeof(Archive_header)))
491         {
492           if (filesize != this->off_)
493             {
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;
499             }
500           this->header_.off = filesize;
501           return;
502         }
503
504       char buf[sizeof(Archive_header)];
505       if (!this->afile_->read(this->off_, sizeof(Archive_header), buf))
506         {
507           this->header_.off = filesize;
508           return;
509         }
510
511       const Archive_header* hdr = reinterpret_cast<const Archive_header*>(buf);
512       if (!this->afile_->interpret_header(hdr, this->off_, &this->header_.name,
513                                           &this->header_.size,
514                                           &this->header_.nested_off))
515         {
516           this->header_.off = filesize;
517           return;
518         }
519       this->header_.off = this->off_;
520
521       // Skip special members.
522       if (!this->header_.name.empty() && this->header_.name != "/")
523         return;
524
525       this->off_ += sizeof(Archive_header) + this->header_.size;
526       if ((this->off_ & 1) != 0)
527         ++this->off_;
528     }
529 }
530
531 // Initial iterator.
532
533 Archive_iterator
534 archive_begin(Archive_file* afile)
535 {
536   return Archive_iterator(afile, sizeof(armag));
537 }
538
539 // Final iterator.
540
541 Archive_iterator
542 archive_end(Archive_file* afile)
543 {
544   return Archive_iterator(afile, afile->filesize());
545 }
546
547 // A type of Import_stream which concatenates other Import_streams
548 // together.
549
550 class Stream_concatenate : public Import::Stream
551 {
552  public:
553   Stream_concatenate()
554     : inputs_()
555   { }
556
557   // Add a new stream.
558   void
559   add(Import::Stream* is)
560   { this->inputs_.push_back(is); }
561
562  protected:
563   bool
564   do_peek(size_t, const char**);
565
566   void
567   do_advance(size_t);
568
569  private:
570   std::list<Import::Stream*> inputs_;
571 };
572
573 // Peek ahead.
574
575 bool
576 Stream_concatenate::do_peek(size_t length, const char** bytes)
577 {
578   while (true)
579     {
580       if (this->inputs_.empty())
581         return false;
582       if (this->inputs_.front()->peek(length, bytes))
583         return true;
584       delete this->inputs_.front();
585       this->inputs_.pop_front();
586     }
587 }
588
589 // Advance.
590
591 void
592 Stream_concatenate::do_advance(size_t skip)
593 {
594   while (true)
595     {
596       if (this->inputs_.empty())
597         return;
598       if (!this->inputs_.front()->at_eof())
599         {
600           // We just assume that this will do the right thing.  It
601           // should be OK since we should never want to skip past
602           // multiple streams.
603           this->inputs_.front()->advance(skip);
604           return;
605         }
606       delete this->inputs_.front();
607       this->inputs_.pop_front();
608     }
609 }
610
611 // Import data from an archive.  We walk through the archive and
612 // import data from each member.
613
614 Import::Stream*
615 Import::find_archive_export_data(const std::string& filename, int fd,
616                                  source_location location)
617 {
618   Archive_file afile(filename, fd, location);
619   if (!afile.initialize())
620     return NULL;
621
622   Stream_concatenate* ret = new Stream_concatenate;
623
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++)
628     {
629       any_members = true;
630       int member_fd;
631       off_t member_off;
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))
635         return NULL;
636
637       Import::Stream* is = Import::find_object_export_data(member_name,
638                                                            member_fd,
639                                                            member_off,
640                                                            location);
641       if (is != NULL)
642         {
643           ret->add(is);
644           any_data = true;
645         }
646     }
647
648   if (!any_members)
649     {
650       // It's normal to have an empty archive file when using gobuild.
651       return new Stream_from_string("");
652     }
653
654   if (!any_data)
655     {
656       delete ret;
657       return NULL;
658     }
659
660   return ret;
661 }