--- /dev/null
+/* Handling of module export data in library archives.
+ This code has bee adapted from the Go front-end.
+
+ Copyright (C) 2009 The Go Authors.
+ Copyright (C) 2026 Jose E. Marchesi.
+
+ GCC is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GCC is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GCC; see the file COPYING3. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#define INCLUDE_MEMORY
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "vec.h"
+
+#include <map>
+#include <string>
+
+#include "a68.h"
+
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+// Archive magic numbers.
+
+static const char armag[] =
+{
+ '!', '<', 'a', 'r', 'c', 'h', '>', '\n'
+};
+
+static const char armagt[] =
+{
+ '!', '<', 't', 'h', 'i', 'n', '>', '\n'
+};
+
+static const char armagb[] =
+{
+ '<', 'b', 'i', 'g', 'a', 'f', '>', '\n'
+};
+
+static const char arfmag[2] = { '`', '\n' };
+
+// Archive fixed length header for AIX big format.
+
+struct Archive_fl_header
+{
+ // Archive magic string.
+ char fl_magic[8];
+ // Offset to member table.
+ char fl_memoff[20];
+ // Offset to global symbol table.
+ char fl_gstoff[20];
+ // Offset to global symbol table for 64-bit objects.
+ char fl_gst64off[20];
+ // Offset to first archive member.
+ char fl_fstmoff[20];
+ // Offset to last archive member.
+ char fl_lstmoff[20];
+ // Offset to first member on free list.
+ char fl_freeoff[20];
+};
+
+// The header of an entry in an archive. This is all readable text,
+// padded with spaces where necesary.
+
+struct Archive_header
+{
+ // The entry name.
+ char ar_name[16];
+ // The file modification time.
+ char ar_date[12];
+ // The user's UID in decimal.
+ char ar_uid[6];
+ // The user's GID in decimal.
+ char ar_gid[6];
+ // The file mode in octal.
+ char ar_mode[8];
+ // The file size in decimal.
+ char ar_size[10];
+ // The final magic code.
+ char ar_fmag[2];
+};
+
+// The header of an entry in an AIX big archive.
+// This is followed by ar_namlen bytes + 2 bytes for arfmag.
+
+struct Archive_big_header
+{
+ // The file size in decimal.
+ char ar_size[20];
+ // The next member offset in decimal.
+ char ar_nxtmem[20];
+ // The previous member offset in decimal.
+ char ar_prvmem[20];
+ // The file modification time in decimal.
+ char ar_date[12];
+ // The user's UID in decimal.
+ char ar_uid[12];
+ // The user's GID in decimal.
+ char ar_gid[12];
+ // The file mode in octal.
+ char ar_mode[12];
+ // The file name length in decimal.
+ char ar_namlen[4];
+};
+
+// The functions in this file extract Go export data from an archive.
+
+static const int archive_magic_len = 8;
+
+// return true if bytes, which are from the start of the file, are an
+// archive magic number.
+
+bool
+a68_is_archive_magic(const char* bytes)
+{
+ return (memcmp(bytes, armag, archive_magic_len) == 0
+ || memcmp(bytes, armagt, archive_magic_len) == 0
+ || memcmp(bytes, armagb, archive_magic_len) == 0);
+}
+
+// An object used to read an archive file.
+
+class Archive_file
+{
+ public:
+ Archive_file(const std::string& filename, int fd, location_t location)
+ : filename_(filename), fd_(fd), filesize_(-1), first_member_offset_(0),
+ extended_names_(), is_thin_archive_(false), is_big_archive_(false),
+ location_(location), nested_archives_()
+ { }
+
+ // Initialize.
+ bool
+ initialize();
+
+ // Return the file name.
+ const std::string&
+ filename() const
+ { return this->filename_; }
+
+ // Get the file size.
+ off_t
+ filesize() const
+ { return this->filesize_; }
+
+ // Return the offset of the first member.
+ off_t
+ first_member_offset() const
+ { return this->first_member_offset_; }
+
+ // Return whether this is a thin archive.
+ bool
+ is_thin_archive() const
+ { return this->is_thin_archive_; }
+
+ // Return whether this is a big archive.
+ bool
+ is_big_archive() const
+ { return this->is_big_archive_; }
+
+ // Return the location of the import statement.
+ location_t
+ location() const
+ { return this->location_; }
+
+ // Read bytes.
+ bool
+ read(off_t offset, off_t size, char*);
+
+ // Parse a decimal in readable text.
+ bool
+ parse_decimal(const char* str, off_t size, long* res) const;
+
+ // Read the archive header at OFF, setting *PNAME, *SIZE,
+ // *NESTED_OFF and *NEXT_OFF.
+ bool
+ read_header(off_t off, std::string* pname, off_t* size, off_t* nested_off,
+ off_t* next_off);
+
+ // Interpret the header of HDR, the header of the archive member at
+ // file offset OFF. Return whether it succeeded. Set *SIZE to the
+ // size of the member. Set *PNAME to the name of the member. Set
+ // *NESTED_OFF to the offset in a nested archive.
+ bool
+ interpret_header(const Archive_header* hdr, off_t off,
+ std::string* pname, off_t* size, off_t* nested_off) const;
+
+ // Get the file and offset for an archive member.
+ bool
+ get_file_and_offset(off_t off, const std::string& hdrname,
+ off_t nested_off, int* memfd, off_t* memoff,
+ std::string* memname);
+
+ private:
+ // Initialize a big archive (AIX)
+ bool
+ initialize_big_archive();
+
+ // Initialize a normal archive
+ bool
+ initialize_archive();
+
+ // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
+ bool
+ read_big_archive_header(off_t off, std::string* pname,
+ off_t* size, off_t* next_off);
+
+ // Read the normal archive header at OFF, setting *PNAME, *SIZE,
+ // *NESTED_OFF and *NEXT_OFF.
+ bool
+ read_archive_header(off_t off, std::string* pname, off_t* size,
+ off_t* nested_off, off_t* next_off);
+
+ // For keeping track of open nested archives in a thin archive file.
+ typedef std::map<std::string, Archive_file*> Nested_archive_table;
+
+ // The name of the file.
+ std::string filename_;
+ // The file descriptor.
+ int fd_;
+ // The file size;
+ off_t filesize_;
+ // The first member offset;
+ off_t first_member_offset_;
+ // The extended name table.
+ std::string extended_names_;
+ // Whether this is a thin archive.
+ bool is_thin_archive_;
+ // Whether this is a big archive.
+ bool is_big_archive_;
+ // The location of the import statements.
+ location_t location_;
+ // Table of nested archives.
+ Nested_archive_table nested_archives_;
+};
+
+bool
+Archive_file::initialize()
+{
+ struct stat st;
+ if (fstat(this->fd_, &st) < 0)
+ {
+ a68_error (NO_NODE, "Z: doing stat", this->filename_.c_str());
+ return false;
+ }
+ this->filesize_ = st.st_size;
+
+ char buf[sizeof(armagt)];
+ if (::lseek(this->fd_, 0, SEEK_SET) < 0
+ || ::read(this->fd_, buf, sizeof(armagt)) != sizeof(armagt))
+ {
+ a68_error (NO_NODE, "Z: reading from archive", this->filename_.c_str());
+ return false;
+ }
+ if (memcmp(buf, armagt, sizeof(armagt)) == 0)
+ this->is_thin_archive_ = true;
+ else if (memcmp(buf, armagb, sizeof(armagb)) == 0)
+ this->is_big_archive_ = true;
+
+ if (this->is_big_archive_)
+ return this->initialize_big_archive();
+ else
+ return this->initialize_archive();
+}
+
+// Initialize a big archive (AIX).
+
+bool
+Archive_file::initialize_big_archive()
+{
+ Archive_fl_header flhdr;
+
+ // Read the fixed length header.
+ if (::lseek(this->fd_, 0, SEEK_SET) < 0
+ || ::read(this->fd_, &flhdr, sizeof(flhdr)) != sizeof(flhdr))
+ {
+ a68_error (NO_NODE, "Z: could not read archive header",
+ this->filename_.c_str());
+ return false;
+ }
+
+ // Parse offset of the first member.
+ long off;
+ if (!this->parse_decimal(flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff), &off))
+ {
+ char* buf = new char[sizeof(flhdr.fl_fstmoff) + 1];
+ memcpy(buf, flhdr.fl_fstmoff, sizeof(flhdr.fl_fstmoff));
+ a68_error (NO_NODE,
+ ("Z: malformed first member offset in archive header"
+ " (expected decimal, got Z)"),
+ this->filename_.c_str(), buf);
+ delete[] buf;
+ return false;
+ }
+ if (off == 0) // Empty archive.
+ this->first_member_offset_ = this->filesize_;
+ else
+ this->first_member_offset_ = off;
+ return true;
+}
+
+// Initialize a normal archive.
+
+bool
+Archive_file::initialize_archive()
+{
+ this->first_member_offset_ = sizeof(armag);
+ if (this->first_member_offset_ == this->filesize_)
+ {
+ // Empty archive.
+ return true;
+ }
+
+ // Look for the extended name table.
+ std::string filename;
+ off_t size;
+ off_t next_off;
+ if (!this->read_header(this->first_member_offset_, &filename,
+ &size, NULL, &next_off))
+ return false;
+ if (filename.empty())
+ {
+ // We found the symbol table.
+ if (!this->read_header(next_off, &filename, &size, NULL, NULL))
+ filename.clear();
+ }
+ if (filename == "/")
+ {
+ char* rdbuf = new char[size];
+ if (::read(this->fd_, rdbuf, size) != size)
+ {
+ a68_error (NO_NODE, "Z: could not read extended names",
+ filename.c_str());
+ delete[] rdbuf;
+ return false;
+ }
+ this->extended_names_.assign(rdbuf, size);
+ delete[] rdbuf;
+ }
+
+ return true;
+}
+
+// Read bytes from the file.
+
+bool
+Archive_file::read(off_t offset, off_t size, char* buf)
+{
+ if (::lseek(this->fd_, offset, SEEK_SET) < 0
+ || ::read(this->fd_, buf, size) != size)
+ {
+ a68_error (NO_NODE, "Z: reading from archive", this->filename_.c_str());
+ return false;
+ }
+ return true;
+}
+
+// Parse a decimal in readable text.
+
+bool
+Archive_file::parse_decimal(const char* str, off_t size, long* res) const
+{
+ char* buf = new char[size + 1];
+ memcpy(buf, str, size);
+ char* ps = buf + size;
+ while (ps > buf && ps[-1] == ' ')
+ --ps;
+ *ps = '\0';
+
+ errno = 0;
+ char* end;
+ *res = strtol(buf, &end, 10);
+ if (*end != '\0'
+ || *res < 0
+ || (*res == LONG_MAX && errno == ERANGE))
+ {
+ delete[] buf;
+ return false;
+ }
+ delete[] buf;
+ return true;
+}
+
+// Read the header at OFF. Set *PNAME to the name, *SIZE to the size,
+// *NESTED_OFF to the nested offset, and *NEXT_OFF to the next member offset.
+
+bool
+Archive_file::read_header(off_t off, std::string* pname, off_t* size,
+ off_t* nested_off, off_t* next_off)
+{
+ if (::lseek(this->fd_, off, SEEK_SET) < 0)
+ {
+ a68_error (NO_NODE, "Z: seeking in archive", this->filename_.c_str());
+ return false;
+ }
+ if (this->is_big_archive_)
+ return this->read_big_archive_header(off, pname, size, next_off);
+ else
+ return this->read_archive_header(off, pname, size, nested_off, next_off);
+}
+
+// Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
+
+bool
+Archive_file::read_big_archive_header(off_t off, std::string* pname,
+ off_t* size, off_t* next_off)
+{
+ Archive_big_header hdr;
+ ssize_t got;
+
+ got = ::read(this->fd_, &hdr, sizeof hdr);
+ if (got != sizeof hdr)
+ {
+ if (got < 0)
+ a68_error (NO_NODE, "Z: reading from archive", this->filename_.c_str());
+ else if (got > 0)
+ a68_error (NO_NODE, "Z short entry header at L",
+ this->filename_.c_str(), static_cast<long>(off));
+ else
+ a68_error (NO_NODE, "Z: unexpected EOF at L",
+ this->filename_.c_str(), static_cast<long>(off));
+ }
+
+ long local_size;
+ if (!this->parse_decimal(hdr.ar_size, sizeof(hdr.ar_size), &local_size))
+ {
+ char* buf = new char[sizeof(hdr.ar_size) + 1];
+ memcpy(buf, hdr.ar_size, sizeof(hdr.ar_size));
+ a68_error (NO_NODE,
+ ("Z: malformed size in entry header at L"
+ " (expected decimal, got %s)"),
+ this->filename_.c_str(), static_cast<long>(off), buf);
+ delete[] buf;
+ return false;
+ }
+ *size = local_size;
+
+ long namlen;
+ if (!this->parse_decimal(hdr.ar_namlen, sizeof(hdr.ar_namlen), &namlen))
+ {
+ char* buf = new char[sizeof(hdr.ar_namlen) + 1];
+ memcpy(buf, hdr.ar_namlen, sizeof(hdr.ar_namlen));
+ a68_error (NO_NODE,
+ ("Z: malformed name length in entry header at L"
+ " (expected decimal, got %s)"),
+ this->filename_.c_str(), static_cast<long>(off), buf);
+ delete[] buf;
+ return false;
+ }
+ // Read member name following member header.
+ char* rdbuf = new char[namlen];
+ got = ::read(this->fd_, rdbuf, namlen);
+ if (got != namlen)
+ {
+ a68_error (NO_NODE,
+ "Z: malformed member name in entry header at L",
+ this->filename_.c_str(), static_cast<long>(off));
+ delete[] rdbuf;
+ return false;
+ }
+ pname->assign(rdbuf, namlen);
+ delete[] rdbuf;
+
+ long local_next_off;
+ if (!this->parse_decimal(hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem), &local_next_off))
+ {
+ char* buf = new char[sizeof(hdr.ar_nxtmem) + 1];
+ memcpy(buf, hdr.ar_nxtmem, sizeof(hdr.ar_nxtmem));
+ a68_error (NO_NODE,
+ ("Z: malformed next member offset in entry header at L"
+ " (expected decimal, got %s)"),
+ this->filename_.c_str(), static_cast<long>(off), buf);
+ delete[] buf;
+ return false;
+ }
+ if (next_off != NULL)
+ {
+ if (local_next_off == 0) // Last member.
+ *next_off = this->filesize_;
+ else
+ *next_off = local_next_off;
+ }
+ return true;
+}
+
+// Read the normal archive header at OFF, setting *PNAME, *SIZE,
+// *NESTED_OFF and *NEXT_OFF.
+
+bool
+Archive_file::read_archive_header(off_t off, std::string* pname, off_t* size,
+ off_t* nested_off, off_t* next_off)
+{
+ Archive_header hdr;
+ ssize_t got = ::read(this->fd_, &hdr, sizeof hdr);
+ if (got != sizeof hdr)
+ {
+ if (got < 0)
+ a68_error (NO_NODE, "Z: reading from archive", this->filename_.c_str());
+ else if (got > 0)
+ a68_error (NO_NODE, "Z: short archive header at L",
+ this->filename_.c_str(), static_cast<long>(off));
+ else
+ a68_error (NO_NODE, "Z: unexpected EOF at L",
+ this->filename_.c_str(), static_cast<long>(off));
+ }
+ off_t local_nested_off;
+ if (!this->interpret_header(&hdr, off, pname, size, &local_nested_off))
+ return false;
+ if (nested_off != NULL)
+ *nested_off = local_nested_off;
+
+ off_t local_next_off;
+ local_next_off = off + sizeof(Archive_header);
+ if (!this->is_thin_archive_ || pname->empty() || *pname == "/")
+ local_next_off += *size;
+ if ((local_next_off & 1) != 0)
+ ++local_next_off;
+ if (local_next_off > this->filesize_) // Last member.
+ local_next_off = this->filesize_;
+ if (next_off != NULL)
+ *next_off = local_next_off;
+ return true;
+}
+
+// Interpret the header of HDR, the header of the archive member at
+// file offset OFF.
+
+bool
+Archive_file::interpret_header(const Archive_header* hdr, off_t off,
+ std::string* pname, off_t* size,
+ off_t* nested_off) const
+{
+ if (memcmp(hdr->ar_fmag, arfmag, sizeof arfmag) != 0)
+ {
+ a68_error (NO_NODE, "Z: malformed archive header at L",
+ this->filename_.c_str(), static_cast<unsigned long>(off));
+ return false;
+ }
+
+ long local_size;
+ if (!this->parse_decimal(hdr->ar_size, sizeof hdr->ar_size, &local_size))
+ {
+ a68_error (NO_NODE, "Z: malformed archive header size at L",
+ this->filename_.c_str(), static_cast<unsigned long>(off));
+ return false;
+ }
+ *size = local_size;
+
+ *nested_off = 0;
+ if (hdr->ar_name[0] != '/')
+ {
+ const char* name_end = strchr(hdr->ar_name, '/');
+ if (name_end == NULL
+ || name_end - hdr->ar_name >= static_cast<int>(sizeof hdr->ar_name))
+ {
+ a68_error (NO_NODE,
+ "Z: malformed archive header name at L",
+ this->filename_.c_str(), static_cast<unsigned long>(off));
+ return false;
+ }
+ pname->assign(hdr->ar_name, name_end - hdr->ar_name);
+ }
+ else if (hdr->ar_name[1] == ' ')
+ {
+ // This is the symbol table.
+ pname->clear();
+ }
+ else if (hdr->ar_name[1] == 'S' && hdr->ar_name[2] == 'Y'
+ && hdr->ar_name[3] == 'M' && hdr->ar_name[4] == '6'
+ && hdr->ar_name[5] == '4' && hdr->ar_name[6] == '/'
+ && hdr->ar_name[7] == ' '
+ )
+ {
+ // 64-bit symbol table.
+ pname->clear();
+ }
+ else if (hdr->ar_name[1] == '/')
+ {
+ // This is the extended name table.
+ pname->assign(1, '/');
+ }
+ else
+ {
+ char* end;
+ errno = 0;
+ long x = strtol(hdr->ar_name + 1, &end, 10);
+ long y = 0;
+ if (*end == ':')
+ y = strtol(end + 1, &end, 10);
+ if (*end != ' '
+ || x < 0
+ || (x == LONG_MAX && errno == ERANGE)
+ || static_cast<size_t>(x) >= this->extended_names_.size())
+ {
+ a68_error (NO_NODE, "Z: bad extended name index at L",
+ this->filename_.c_str(), static_cast<unsigned long>(off));
+ return false;
+ }
+
+ const char* name = this->extended_names_.data() + x;
+ const char* name_end = strchr(name, '\n');
+ if (static_cast<size_t>(name_end - name) > this->extended_names_.size()
+ || name_end[-1] != '/')
+ {
+ a68_error (NO_NODE,
+ "Z: bad extended name entry at header L",
+ this->filename_.c_str(), static_cast<unsigned long>(off));
+ return false;
+ }
+ pname->assign(name, name_end - 1 - name);
+ *nested_off = y;
+ }
+
+ return true;
+}
+
+// Get the file and offset for an archive member.
+
+bool
+Archive_file::get_file_and_offset(off_t off, const std::string& hdrname,
+ off_t nested_off, int* memfd, off_t* memoff,
+ std::string* memname)
+{
+ if (this->is_big_archive_)
+ {
+ *memfd = this->fd_;
+ *memoff = (off + sizeof(Archive_big_header) + hdrname.length()
+ + sizeof(arfmag));
+ if ((*memoff & 1) != 0)
+ ++*memoff;
+ *memname = this->filename_ + '(' + hdrname + ')';
+ return true;
+ }
+ else if (!this->is_thin_archive_)
+ {
+ *memfd = this->fd_;
+ *memoff = off + sizeof(Archive_header);
+ *memname = this->filename_ + '(' + hdrname + ')';
+ return true;
+ }
+
+ std::string filename = hdrname;
+ if (!IS_ABSOLUTE_PATH(filename.c_str()))
+ {
+ const char* archive_path = this->filename_.c_str();
+ const char* basename = lbasename(archive_path);
+ if (basename > archive_path)
+ filename.replace(0, 0,
+ this->filename_.substr(0, basename - archive_path));
+ }
+
+ if (nested_off > 0)
+ {
+ // This is a member of a nested archive.
+ Archive_file* nfile;
+ Nested_archive_table::const_iterator p =
+ this->nested_archives_.find(filename);
+ if (p != this->nested_archives_.end())
+ nfile = p->second;
+ else
+ {
+ int nfd = open(filename.c_str(), O_RDONLY | O_BINARY);
+ if (nfd < 0)
+ {
+ a68_error (NO_NODE, "Z: cannot open nested archive Z",
+ this->filename_.c_str(), filename.c_str());
+ return false;
+ }
+ nfile = new Archive_file(filename, nfd, this->location_);
+ if (!nfile->initialize())
+ {
+ delete nfile;
+ return false;
+ }
+ this->nested_archives_[filename] = nfile;
+ }
+
+ std::string nname;
+ off_t nsize;
+ off_t nnested_off;
+ if (!nfile->read_header(nested_off, &nname, &nsize, &nnested_off, NULL))
+ return false;
+ return nfile->get_file_and_offset(nested_off, nname, nnested_off,
+ memfd, memoff, memname);
+ }
+
+ // An external member of a thin archive.
+ *memfd = open(filename.c_str(), O_RDONLY | O_BINARY);
+ if (*memfd < 0)
+ {
+ a68_error (NO_NODE, "Z: opening archive", filename.c_str());
+ return false;
+ }
+ *memoff = 0;
+ *memname = filename;
+ return true;
+}
+
+// An archive member iterator. This is more-or-less copied from gold.
+
+class Archive_iterator
+{
+ public:
+ // The header of an archive member. This is what this iterator
+ // points to.
+ struct Header
+ {
+ // The name of the member.
+ std::string name;
+ // The file offset of the member.
+ off_t off;
+ // The file offset of a nested archive member.
+ off_t nested_off;
+ // The size of the member.
+ off_t size;
+ };
+
+ Archive_iterator(Archive_file* afile, off_t off)
+ : afile_(afile), off_(off)
+ { this->read_next_header(); }
+
+ const Header&
+ operator*() const
+ { return this->header_; }
+
+ const Header*
+ operator->() const
+ { return &this->header_; }
+
+ Archive_iterator&
+ operator++()
+ {
+ if (this->off_ == this->afile_->filesize())
+ return *this;
+ this->off_ = this->next_off_;
+ this->read_next_header();
+ return *this;
+ }
+
+ Archive_iterator
+ operator++(int)
+ {
+ Archive_iterator ret = *this;
+ ++*this;
+ return ret;
+ }
+
+ bool
+ operator==(const Archive_iterator& p) const
+ { return this->off_ == p->off; }
+
+ bool
+ operator!=(const Archive_iterator& p) const
+ { return this->off_ != p->off; }
+
+ private:
+ void
+ read_next_header();
+
+ // The underlying archive file.
+ Archive_file* afile_;
+ // The current offset in the file.
+ off_t off_;
+ // The offset of the next member.
+ off_t next_off_;
+ // The current archive header.
+ Header header_;
+};
+
+// Read the next archive header.
+
+void
+Archive_iterator::read_next_header()
+{
+ off_t filesize = this->afile_->filesize();
+ while (true)
+ {
+ if (this->off_ == filesize)
+ {
+ this->header_.off = filesize;
+ return;
+ }
+
+ if (!this->afile_->read_header(this->off_, &this->header_.name,
+ &this->header_.size,
+ &this->header_.nested_off,
+ &this->next_off_))
+ {
+ this->header_.off = filesize;
+ this->off_ = filesize;
+ return;
+ }
+ this->header_.off = this->off_;
+
+ // Skip special members.
+ if (!this->header_.name.empty() && this->header_.name != "/")
+ return;
+
+ this->off_ = this->next_off_;
+ }
+}
+
+// Initial iterator.
+
+Archive_iterator
+archive_begin(Archive_file* afile)
+{
+ return Archive_iterator(afile, afile->first_member_offset());
+}
+
+// Final iterator.
+
+Archive_iterator
+archive_end(Archive_file* afile)
+{
+ return Archive_iterator(afile, afile->filesize());
+}
+
+/* Get a68 imports from an archive. We walk through the archive and read
+ imports from each member. */
+
+char *
+a68_find_archive_export_data (const char *filename, int fd,
+ size_t *size)
+{
+ char *ret = NULL;
+ size_t ret_size = 0;
+
+ Archive_file afile(filename, fd, UNKNOWN_LOCATION);
+ if (!afile.initialize())
+ return NULL;
+
+
+ Archive_iterator pend = archive_end(&afile);
+ for (Archive_iterator p = archive_begin(&afile); p != pend; p++)
+ {
+ int member_fd;
+ off_t member_off;
+ std::string member_name;
+ if (!afile.get_file_and_offset(p->off, p->name, p->nested_off,
+ &member_fd, &member_off, &member_name))
+ {
+ *size = 0;
+ return NULL;
+ }
+
+ size_t exports_size;
+ char *exports = a68_find_object_export_data (member_name,
+ member_fd,
+ member_off,
+ &exports_size);
+ if (exports != NULL)
+ {
+ if (ret == NULL)
+ {
+ ret = exports;
+ ret_size = exports_size;
+ }
+ else
+ {
+ ret = (char *) xrealloc (ret, ret_size + exports_size);
+ memcpy (ret + ret_size, exports, exports_size);
+ ret_size += exports_size;
+ free (exports);
+ }
+ }
+ }
+
+ *size = ret_size;
+ return ret;
+}