bool GNUArchiveReader::readArchive(const LinkerConfig& pConfig,
                                   Archive& pArchive)
{
  // bypass the empty archive
  if (Archive::MAGIC_LEN == pArchive.getARFile().memArea()->handler()->size())
    return true;

  if (pArchive.getARFile().attribute()->isWholeArchive())
    return includeAllMembers(pConfig, pArchive);

  // if this is the first time read this archive, setup symtab and strtab
  if (pArchive.getSymbolTable().empty()) {
  // read the symtab of the archive
  readSymbolTable(pArchive);

  // read the strtab of the archive
  readStringTable(pArchive);

  // add root archive to ArchiveMemberMap
  pArchive.addArchiveMember(pArchive.getARFile().name(),
                            pArchive.inputs().root(),
                            &InputTree::Downward);
  }

  // include the needed members in the archive and build up the input tree
  bool willSymResolved;
  do {
    willSymResolved = false;
    for (size_t idx = 0; idx < pArchive.numOfSymbols(); ++idx) {
      // bypass if we already decided to include this symbol or not
      if (Archive::Symbol::Unknown != pArchive.getSymbolStatus(idx))
        continue;

      // bypass if another symbol with the same object file offset is included
      if (pArchive.hasObjectMember(pArchive.getObjFileOffset(idx))) {
        pArchive.setSymbolStatus(idx, Archive::Symbol::Include);
        continue;
      }

      // check if we should include this defined symbol
      Archive::Symbol::Status status =
        shouldIncludeSymbol(pArchive.getSymbolName(idx));
      if (Archive::Symbol::Unknown != status)
        pArchive.setSymbolStatus(idx, status);

      if (Archive::Symbol::Include == status) {
        // include the object member from the given offset
        includeMember(pConfig, pArchive, pArchive.getObjFileOffset(idx));
        willSymResolved = true;
      } // end of if
    } // end of for
  } while (willSymResolved);

  return true;
}
/// includeAllMembers - include all object members. This is called if
/// --whole-archive is the attribute for this archive file.
bool GNUArchiveReader::includeAllMembers(const LinkerConfig& pConfig,
                                         Archive& pArchive)
{
  // read the symtab of the archive
  readSymbolTable(pArchive);

  // read the strtab of the archive
  readStringTable(pArchive);

  // add root archive to ArchiveMemberMap
  pArchive.addArchiveMember(pArchive.getARFile().name(),
                            pArchive.inputs().root(),
                            &InputTree::Downward);

  bool isThinAR = isThinArchive(pArchive.getARFile());
  uint32_t begin_offset = pArchive.getARFile().fileOffset() +
                          Archive::MAGIC_LEN +
                          sizeof(Archive::MemberHeader) +
                          pArchive.getSymTabSize();
  if (pArchive.hasStrTable()) {
    if (0x0 != (begin_offset & 1))
      ++begin_offset;
    begin_offset += sizeof(Archive::MemberHeader) +
                    pArchive.getStrTable().size();
  }
  uint32_t end_offset = pArchive.getARFile().memArea()->handler()->size();
  for (uint32_t offset = begin_offset;
       offset < end_offset;
       offset += sizeof(Archive::MemberHeader)) {

    size_t size = includeMember(pConfig, pArchive, offset);

    if (!isThinAR) {
      offset += size;
    }

    if (0x0 != (offset & 1))
      ++offset;
  }
  return true;
}
/// includeMember - include the object member in the given file offset, and
/// return the size of the object
/// @param pConfig - LinkerConfig
/// @param pArchiveRoot - the archive root
/// @param pFileOffset  - file offset of the member header in the archive
size_t GNUArchiveReader::includeMember(const LinkerConfig& pConfig,
                                       Archive& pArchive,
                                       uint32_t pFileOffset)
{
  Input* cur_archive = &(pArchive.getARFile());
  Input* member = NULL;
  uint32_t file_offset = pFileOffset;
  size_t size = 0;
  do {
    uint32_t nested_offset = 0;
    // use the file offset in current archive to find out the member we
    // want to include
    member = readMemberHeader(pArchive,
                              *cur_archive,
                              file_offset,
                              nested_offset,
                              size);
    assert(member != NULL);
    // bypass if we get an archive that is already in the map
    if (Input::Archive == member->type()) {
        cur_archive = member;
        file_offset = nested_offset;
        continue;
    }

    // insert a node into the subtree of current archive.
    Archive::ArchiveMember* parent =
      pArchive.getArchiveMember(cur_archive->name());

    assert(NULL != parent);
    pArchive.inputs().insert(parent->lastPos, *(parent->move), *member);

    // move the iterator to new created node, and also adjust the
    // direction to Afterward for next insertion in this subtree
    parent->move->move(parent->lastPos);
    parent->move = &InputTree::Afterward;
    bool doContinue = false;

    if (m_ELFObjectReader.isMyFormat(*member, doContinue)) {
      member->setType(Input::Object);
      pArchive.addObjectMember(pFileOffset, parent->lastPos);
      m_ELFObjectReader.readHeader(*member);
      m_ELFObjectReader.readSections(*member);
      m_ELFObjectReader.readSymbols(*member);
      m_Module.getObjectList().push_back(member);
    }
    else if (doContinue && isMyFormat(*member, doContinue)) {
      member->setType(Input::Archive);
      // when adding a new archive node, set the iterator to archive
      // itself, and set the direction to Downward
      pArchive.addArchiveMember(member->name(),
                                parent->lastPos,
                                &InputTree::Downward);
      cur_archive = member;
      file_offset = nested_offset;
    }
    else {
      warning(diag::warn_unrecognized_input_file) << member->path()
        << pConfig.targets().triple().str();
    }
  } while (Input::Object != member->type());
  return size;
}