//===- subzero/src/IceELFSection.cpp - Representation of ELF sections -----===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Defines how ELF sections are represented.
///
//===----------------------------------------------------------------------===//

#include "IceELFSection.h"

#include "IceDefs.h"
#include "IceELFStreamer.h"
#include "llvm/Support/MathExtras.h"

using namespace llvm::ELF;

namespace Ice {

// Text sections.

void ELFTextSection::appendData(ELFStreamer &Str,
                                const llvm::StringRef MoreData) {
  Str.writeBytes(MoreData);
  Header.sh_size += MoreData.size();
}

// Data sections.

void ELFDataSection::appendData(ELFStreamer &Str,
                                const llvm::StringRef MoreData) {
  Str.writeBytes(MoreData);
  Header.sh_size += MoreData.size();
}

void ELFDataSection::appendZeros(ELFStreamer &Str, SizeT NumBytes) {
  Str.writeZeroPadding(NumBytes);
  Header.sh_size += NumBytes;
}

void ELFDataSection::appendRelocationOffset(ELFStreamer &Str, bool IsRela,
                                            RelocOffsetT RelocOffset) {
  const SizeT RelocAddrSize = typeWidthInBytes(getPointerType());
  if (IsRela) {
    appendZeros(Str, RelocAddrSize);
    return;
  }
  assert(RelocAddrSize == 4 && " writeLE32 assumes RelocAddrSize is 4");
  Str.writeLE32(RelocOffset);
  Header.sh_size += RelocAddrSize;
}

void ELFDataSection::padToAlignment(ELFStreamer &Str, Elf64_Xword Align) {
  assert(llvm::isPowerOf2_32(Align));
  Elf64_Xword AlignDiff = Utils::OffsetToAlignment(Header.sh_size, Align);
  if (AlignDiff == 0)
    return;
  if (Header.sh_type != llvm::ELF::SHT_NOBITS)
    Str.writeZeroPadding(AlignDiff);
  Header.sh_size += AlignDiff;
}

// Relocation sections.

void ELFRelocationSection::addRelocations(RelocOffsetT BaseOff,
                                          const FixupRefList &FixupRefs,
                                          ELFSymbolTableSection *SymTab) {
  for (const AssemblerFixup *FR : FixupRefs) {
    Fixups.push_back(*FR);
    AssemblerFixup &F = Fixups.back();
    F.set_position(BaseOff + F.position());
    assert(!F.valueIsSymbol());
    if (!F.isNullSymbol()) {
      // Do an early lookup in the symbol table.  If the symbol is found,
      // replace the Constant in the symbol with the ELFSym, and calculate the
      // final value of the addend.  As such, a local label allocated from the
      // Assembler arena will be converted to a symbol before the Assembler
      // arena goes away.
      if (const ELFSym *Sym = SymTab->findSymbol(F.symbol())) {
        F.set_addend(F.offset());
        F.set_value(Sym);
      }
    }
  }
}

size_t ELFRelocationSection::getSectionDataSize() const {
  return Fixups.size() * Header.sh_entsize;
}

// Symbol tables.

void ELFSymbolTableSection::createNullSymbol(ELFSection *NullSection,
                                             GlobalContext *Ctx) {
  // The first entry in the symbol table should be a NULL entry, so make sure
  // the map is still empty.
  assert(LocalSymbols.empty());
  // Explicitly set the null symbol name to the empty string, so that
  // GlobalString::operator<() orders the null string first.
  NullSymbolName = GlobalString::createWithString(Ctx, "");
  createDefinedSym(NullSymbolName, STT_NOTYPE, STB_LOCAL, NullSection, 0, 0);
  NullSymbol = findSymbol(NullSymbolName);
}

void ELFSymbolTableSection::createDefinedSym(GlobalString Name, uint8_t Type,
                                             uint8_t Binding,
                                             ELFSection *Section,
                                             RelocOffsetT Offset, SizeT Size) {
  ELFSym NewSymbol = ELFSym();
  NewSymbol.Sym.setBindingAndType(Binding, Type);
  NewSymbol.Sym.st_value = Offset;
  NewSymbol.Sym.st_size = Size;
  NewSymbol.Section = Section;
  NewSymbol.Number = ELFSym::UnknownNumber;
  bool Unique;
  if (Binding == STB_LOCAL)
    Unique = LocalSymbols.insert(std::make_pair(Name, NewSymbol)).second;
  else
    Unique = GlobalSymbols.insert(std::make_pair(Name, NewSymbol)).second;
  assert(Unique);
  (void)Unique;
}

void ELFSymbolTableSection::noteUndefinedSym(GlobalString Name,
                                             ELFSection *NullSection) {
  ELFSym NewSymbol = ELFSym();
  NewSymbol.Sym.setBindingAndType(STB_GLOBAL, STT_NOTYPE);
  NewSymbol.Section = NullSection;
  NewSymbol.Number = ELFSym::UnknownNumber;
  bool Unique = GlobalSymbols.insert(std::make_pair(Name, NewSymbol)).second;
  if (!Unique) {
    std::string Buffer;
    llvm::raw_string_ostream StrBuf(Buffer);
    StrBuf << "Symbol external and defined: " << Name;
    llvm::report_fatal_error(StrBuf.str());
  }
  (void)Unique;
}

const ELFSym *ELFSymbolTableSection::findSymbol(GlobalString Name) const {
  auto I = LocalSymbols.find(Name);
  if (I != LocalSymbols.end())
    return &I->second;
  I = GlobalSymbols.find(Name);
  if (I != GlobalSymbols.end())
    return &I->second;
  return nullptr;
}

void ELFSymbolTableSection::updateIndices(const ELFStringTableSection *StrTab) {
  SizeT SymNumber = 0;
  for (auto &KeyValue : LocalSymbols) {
    GlobalString Name = KeyValue.first;
    ELFSection *Section = KeyValue.second.Section;
    Elf64_Sym &SymInfo = KeyValue.second.Sym;
    if (Name != NullSymbolName && Name.hasStdString())
      SymInfo.st_name = StrTab->getIndex(Name.toString());
    SymInfo.st_shndx = Section->getNumber();
    KeyValue.second.setNumber(SymNumber++);
  }
  for (auto &KeyValue : GlobalSymbols) {
    const std::string &Name = KeyValue.first.toString();
    ELFSection *Section = KeyValue.second.Section;
    Elf64_Sym &SymInfo = KeyValue.second.Sym;
    if (!Name.empty())
      SymInfo.st_name = StrTab->getIndex(Name);
    SymInfo.st_shndx = Section->getNumber();
    KeyValue.second.setNumber(SymNumber++);
  }
}

void ELFSymbolTableSection::writeData(ELFStreamer &Str, bool IsELF64) {
  if (IsELF64) {
    writeSymbolMap<true>(Str, LocalSymbols);
    writeSymbolMap<true>(Str, GlobalSymbols);
  } else {
    writeSymbolMap<false>(Str, LocalSymbols);
    writeSymbolMap<false>(Str, GlobalSymbols);
  }
}

// String tables.

void ELFStringTableSection::add(const std::string &Str) {
  assert(!isLaidOut());
  assert(!Str.empty());
  StringToIndexMap.insert(std::make_pair(Str, UnknownIndex));
}

size_t ELFStringTableSection::getIndex(const std::string &Str) const {
  assert(isLaidOut());
  StringToIndexType::const_iterator It = StringToIndexMap.find(Str);
  if (It == StringToIndexMap.end()) {
    llvm::report_fatal_error("String index not found: " + Str);
    return UnknownIndex;
  }
  return It->second;
}

bool ELFStringTableSection::SuffixComparator::
operator()(const std::string &StrA, const std::string &StrB) const {
  size_t LenA = StrA.size();
  size_t LenB = StrB.size();
  size_t CommonLen = std::min(LenA, LenB);
  // If there is a difference in the common suffix, use that diff to sort.
  for (size_t i = 0; i < CommonLen; ++i) {
    char a = StrA[LenA - i - 1];
    char b = StrB[LenB - i - 1];
    if (a != b)
      return a > b;
  }
  // If the common suffixes are completely equal, let the longer one come
  // first, so that it can be laid out first and its characters shared.
  return LenA > LenB;
}

void ELFStringTableSection::doLayout() {
  assert(!isLaidOut());
  llvm::StringRef Prev;

  // String table starts with 0 byte.
  StringData.push_back(0);

  for (auto &StringIndex : StringToIndexMap) {
    assert(StringIndex.second == UnknownIndex);
    llvm::StringRef Cur = llvm::StringRef(StringIndex.first);
    if (Prev.endswith(Cur)) {
      // Prev is already in the StringData, and Cur is shorter than Prev based
      // on the sort.
      StringIndex.second = StringData.size() - Cur.size() - 1;
      continue;
    }
    StringIndex.second = StringData.size();
    std::copy(Cur.begin(), Cur.end(), back_inserter(StringData));
    StringData.push_back(0);
    Prev = Cur;
  }
}

} // end of namespace Ice
