//===- XCOFFReader.cpp ----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "XCOFFReader.h"

namespace llvm {
namespace objcopy {
namespace xcoff {

using namespace object;

Error XCOFFReader::readSections(Object &Obj) const {
  ArrayRef<XCOFFSectionHeader32> Sections = XCOFFObj.sections32();
  for (const XCOFFSectionHeader32 &Sec : Sections) {
    Section ReadSec;
    // Section header.
    ReadSec.SectionHeader = Sec;
    DataRefImpl SectionDRI;
    SectionDRI.p = reinterpret_cast<uintptr_t>(&Sec);

    // Section data.
    if (Sec.SectionSize) {
      Expected<ArrayRef<uint8_t>> ContentsRef =
          XCOFFObj.getSectionContents(SectionDRI);
      if (!ContentsRef)
        return ContentsRef.takeError();
      ReadSec.Contents = ContentsRef.get();
    }

    // Relocations.
    if (Sec.NumberOfRelocations) {
      auto Relocations =
          XCOFFObj.relocations<XCOFFSectionHeader32, XCOFFRelocation32>(Sec);
      if (!Relocations)
        return Relocations.takeError();
      llvm::append_range(ReadSec.Relocations, Relocations.get());
    }

    Obj.Sections.push_back(std::move(ReadSec));
  }
  return Error::success();
}

Error XCOFFReader::readSymbols(Object &Obj) const {
  std::vector<Symbol> Symbols;
  Symbols.reserve(XCOFFObj.getNumberOfSymbolTableEntries());
  for (SymbolRef Sym : XCOFFObj.symbols()) {
    Symbol ReadSym;
    DataRefImpl SymbolDRI = Sym.getRawDataRefImpl();
    XCOFFSymbolRef SymbolEntRef = XCOFFObj.toSymbolRef(SymbolDRI);
    ReadSym.Sym = *SymbolEntRef.getSymbol32();
    // Auxiliary entries.
    if (SymbolEntRef.getNumberOfAuxEntries()) {
      const char *Start = reinterpret_cast<const char *>(
          SymbolDRI.p + XCOFF::SymbolTableEntrySize);
      Expected<StringRef> RawAuxEntriesOrError = XCOFFObj.getRawData(
          Start,
          XCOFF::SymbolTableEntrySize * SymbolEntRef.getNumberOfAuxEntries(),
          StringRef("symbol"));
      if (!RawAuxEntriesOrError)
        return RawAuxEntriesOrError.takeError();
      ReadSym.AuxSymbolEntries = RawAuxEntriesOrError.get();
    }
    Obj.Symbols.push_back(std::move(ReadSym));
  }
  return Error::success();
}

Expected<std::unique_ptr<Object>> XCOFFReader::create() const {
  auto Obj = std::make_unique<Object>();
  // Only 32-bit supported now.
  if (XCOFFObj.is64Bit())
    return createStringError(object_error::invalid_file_type,
                             "64-bit XCOFF is not supported yet");
  // Read the file header.
  Obj->FileHeader = *XCOFFObj.fileHeader32();
  // Read the optional header.
  if (XCOFFObj.getOptionalHeaderSize())
    Obj->OptionalFileHeader = *XCOFFObj.auxiliaryHeader32();
  // Read each section.
  Obj->Sections.reserve(XCOFFObj.getNumberOfSections());
  if (Error E = readSections(*Obj))
    return std::move(E);
  // Read each symbol.
  Obj->Symbols.reserve(XCOFFObj.getRawNumberOfSymbolTableEntries32());
  if (Error E = readSymbols(*Obj))
    return std::move(E);
  // String table.
  Obj->StringTable = XCOFFObj.getStringTable();
  return std::move(Obj);
}

} // end namespace xcoff
} // end namespace objcopy
} // end namespace llvm
