//===- subzero/src/IceELFObjectWriter.cpp - ELF object file writer --------===//
//
//                        The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Defines the writer for ELF relocatable object files.
///
//===----------------------------------------------------------------------===//

#include "IceELFObjectWriter.h"

#include "IceAssembler.h"
#include "IceDefs.h"
#include "IceELFSection.h"
#include "IceELFStreamer.h"
#include "IceGlobalContext.h"
#include "IceGlobalInits.h"
#include "IceInst.h"
#include "IceOperand.h"
#include "llvm/Support/MathExtras.h"

using namespace llvm::ELF;

namespace Ice {

namespace {

struct {
  bool IsELF64;
  uint16_t ELFMachine;
  uint32_t ELFFlags;
} ELFTargetInfo[] = {
#define X(tag, str, is_elf64, e_machine, e_flags)                              \
  { is_elf64, e_machine, e_flags }                                             \
  ,
    TARGETARCH_TABLE
#undef X
};

bool isELF64(TargetArch Arch) {
  if (Arch < TargetArch_NUM)
    return ELFTargetInfo[Arch].IsELF64;
  llvm_unreachable("Invalid target arch for isELF64");
  return false;
}

uint16_t getELFMachine(TargetArch Arch) {
  if (Arch < TargetArch_NUM)
    return ELFTargetInfo[Arch].ELFMachine;
  llvm_unreachable("Invalid target arch for getELFMachine");
  return EM_NONE;
}

uint32_t getELFFlags(TargetArch Arch) {
  if (Arch < TargetArch_NUM)
    return ELFTargetInfo[Arch].ELFFlags;
  llvm_unreachable("Invalid target arch for getELFFlags");
  return 0;
}

} // end of anonymous namespace

ELFObjectWriter::ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out)
    : Ctx(Ctx), Str(Out), ELF64(isELF64(Ctx.getFlags().getTargetArch())) {
  // Create the special bookkeeping sections now.
  const IceString NullSectionName("");
  NullSection = new (Ctx.allocate<ELFSection>())
      ELFSection(NullSectionName, SHT_NULL, 0, 0, 0);

  const IceString ShStrTabName(".shstrtab");
  ShStrTab = new (Ctx.allocate<ELFStringTableSection>())
      ELFStringTableSection(ShStrTabName, SHT_STRTAB, 0, 1, 0);
  ShStrTab->add(ShStrTabName);

  const IceString SymTabName(".symtab");
  const Elf64_Xword SymTabAlign = ELF64 ? 8 : 4;
  const Elf64_Xword SymTabEntSize =
      ELF64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
  static_assert(sizeof(Elf64_Sym) == 24 && sizeof(Elf32_Sym) == 16,
                "Elf_Sym sizes cannot be derived from sizeof");
  SymTab = createSection<ELFSymbolTableSection>(SymTabName, SHT_SYMTAB, 0,
                                                SymTabAlign, SymTabEntSize);
  SymTab->createNullSymbol(NullSection);

  const IceString StrTabName(".strtab");
  StrTab =
      createSection<ELFStringTableSection>(StrTabName, SHT_STRTAB, 0, 1, 0);
}

template <typename T>
T *ELFObjectWriter::createSection(const IceString &Name, Elf64_Word ShType,
                                  Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
                                  Elf64_Xword ShEntsize) {
  assert(!SectionNumbersAssigned);
  T *NewSection =
      new (Ctx.allocate<T>()) T(Name, ShType, ShFlags, ShAddralign, ShEntsize);
  ShStrTab->add(Name);
  return NewSection;
}

ELFRelocationSection *
ELFObjectWriter::createRelocationSection(const ELFSection *RelatedSection) {
  // Choice of RELA vs REL is actually separate from elf64 vs elf32, but in
  // practice we've only had .rela for elf64 (x86-64). In the future, the two
  // properties may need to be decoupled and the ShEntSize can vary more.
  const Elf64_Word ShType = ELF64 ? SHT_RELA : SHT_REL;
  IceString RelPrefix = ELF64 ? ".rela" : ".rel";
  IceString RelSectionName = RelPrefix + RelatedSection->getName();
  const Elf64_Xword ShAlign = ELF64 ? 8 : 4;
  const Elf64_Xword ShEntSize = ELF64 ? sizeof(Elf64_Rela) : sizeof(Elf32_Rel);
  static_assert(sizeof(Elf64_Rela) == 24 && sizeof(Elf32_Rel) == 8,
                "Elf_Rel/Rela sizes cannot be derived from sizeof");
  constexpr Elf64_Xword ShFlags = 0;
  ELFRelocationSection *RelSection = createSection<ELFRelocationSection>(
      RelSectionName, ShType, ShFlags, ShAlign, ShEntSize);
  RelSection->setRelatedSection(RelatedSection);
  return RelSection;
}

template <typename UserSectionList>
void ELFObjectWriter::assignRelSectionNumInPairs(SizeT &CurSectionNumber,
                                                 UserSectionList &UserSections,
                                                 RelSectionList &RelSections,
                                                 SectionList &AllSections) {
  RelSectionList::iterator RelIt = RelSections.begin();
  RelSectionList::iterator RelE = RelSections.end();
  for (ELFSection *UserSection : UserSections) {
    UserSection->setNumber(CurSectionNumber++);
    UserSection->setNameStrIndex(ShStrTab->getIndex(UserSection->getName()));
    AllSections.push_back(UserSection);
    if (RelIt != RelE) {
      ELFRelocationSection *RelSection = *RelIt;
      if (RelSection->getRelatedSection() == UserSection) {
        RelSection->setInfoNum(UserSection->getNumber());
        RelSection->setNumber(CurSectionNumber++);
        RelSection->setNameStrIndex(ShStrTab->getIndex(RelSection->getName()));
        AllSections.push_back(RelSection);
        ++RelIt;
      }
    }
  }
  // Should finish with UserIt at the same time as RelIt.
  assert(RelIt == RelE);
  return;
}

void ELFObjectWriter::assignRelLinkNum(SizeT SymTabNumber,
                                       RelSectionList &RelSections) {
  for (ELFRelocationSection *S : RelSections) {
    S->setLinkNum(SymTabNumber);
  }
}

void ELFObjectWriter::assignSectionNumbersInfo(SectionList &AllSections) {
  // Go through each section, assigning them section numbers and and fill in
  // the size for sections that aren't incrementally updated.
  assert(!SectionNumbersAssigned);
  SizeT CurSectionNumber = 0;
  NullSection->setNumber(CurSectionNumber++);
  // The rest of the fields are initialized to 0, and stay that way.
  AllSections.push_back(NullSection);

  assignRelSectionNumInPairs<TextSectionList>(CurSectionNumber, TextSections,
                                              RelTextSections, AllSections);
  assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, DataSections,
                                              RelDataSections, AllSections);
  for (ELFSection *BSSSection : BSSSections) {
    BSSSection->setNumber(CurSectionNumber++);
    BSSSection->setNameStrIndex(ShStrTab->getIndex(BSSSection->getName()));
    AllSections.push_back(BSSSection);
  }
  assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, RODataSections,
                                              RelRODataSections, AllSections);

  ShStrTab->setNumber(CurSectionNumber++);
  ShStrTab->setNameStrIndex(ShStrTab->getIndex(ShStrTab->getName()));
  AllSections.push_back(ShStrTab);

  SymTab->setNumber(CurSectionNumber++);
  SymTab->setNameStrIndex(ShStrTab->getIndex(SymTab->getName()));
  AllSections.push_back(SymTab);

  StrTab->setNumber(CurSectionNumber++);
  StrTab->setNameStrIndex(ShStrTab->getIndex(StrTab->getName()));
  AllSections.push_back(StrTab);

  SymTab->setLinkNum(StrTab->getNumber());
  SymTab->setInfoNum(SymTab->getNumLocals());

  assignRelLinkNum(SymTab->getNumber(), RelTextSections);
  assignRelLinkNum(SymTab->getNumber(), RelDataSections);
  assignRelLinkNum(SymTab->getNumber(), RelRODataSections);
  SectionNumbersAssigned = true;
}

Elf64_Off ELFObjectWriter::alignFileOffset(Elf64_Xword Align) {
  Elf64_Off OffsetInFile = Str.tell();
  Elf64_Xword AlignDiff = Utils::OffsetToAlignment(OffsetInFile, Align);
  if (AlignDiff == 0)
    return OffsetInFile;
  Str.writeZeroPadding(AlignDiff);
  OffsetInFile += AlignDiff;
  return OffsetInFile;
}

void ELFObjectWriter::writeFunctionCode(const IceString &FuncName,
                                        bool IsInternal, const Assembler *Asm) {
  assert(!SectionNumbersAssigned);
  ELFTextSection *Section = nullptr;
  ELFRelocationSection *RelSection = nullptr;
  const bool FunctionSections = Ctx.getFlags().getFunctionSections();
  if (TextSections.empty() || FunctionSections) {
    IceString SectionName = ".text";
    if (FunctionSections)
      SectionName += "." + FuncName;
    constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_EXECINSTR;
    const Elf64_Xword ShAlign = 1 << Asm->getBundleAlignLog2Bytes();
    Section = createSection<ELFTextSection>(SectionName, SHT_PROGBITS, ShFlags,
                                            ShAlign, 0);
    Elf64_Off OffsetInFile = alignFileOffset(Section->getSectionAlign());
    Section->setFileOffset(OffsetInFile);
    TextSections.push_back(Section);
    RelSection = createRelocationSection(Section);
    RelTextSections.push_back(RelSection);
  } else {
    Section = TextSections[0];
    RelSection = RelTextSections[0];
  }
  RelocOffsetT OffsetInSection = Section->getCurrentSize();
  // Function symbols are set to 0 size in the symbol table, in contrast to
  // data symbols which have a proper size.
  SizeT SymbolSize = 0;
  Section->appendData(Str, Asm->getBufferView());
  uint8_t SymbolType;
  uint8_t SymbolBinding;
  if (IsInternal && !Ctx.getFlags().getDisableInternal()) {
    SymbolType = STT_NOTYPE;
    SymbolBinding = STB_LOCAL;
  } else {
    SymbolType = STT_FUNC;
    SymbolBinding = STB_GLOBAL;
  }
  SymTab->createDefinedSym(FuncName, SymbolType, SymbolBinding, Section,
                           OffsetInSection, SymbolSize);
  StrTab->add(FuncName);

  // Copy the fixup information from per-function Assembler memory to the
  // object writer's memory, for writing later.
  if (!Asm->fixups().empty()) {
    RelSection->addRelocations(OffsetInSection, Asm->fixups());
  }
}

namespace {

ELFObjectWriter::SectionType
classifyGlobalSection(const VariableDeclaration *Var) {
  if (Var->getIsConstant())
    return ELFObjectWriter::ROData;
  if (Var->hasNonzeroInitializer())
    return ELFObjectWriter::Data;
  return ELFObjectWriter::BSS;
}

// Partition the Vars list by SectionType into VarsBySection. If TranslateOnly
// is non-empty, then only the TranslateOnly variable is kept for emission.
void partitionGlobalsBySection(const VariableDeclarationList &Vars,
                               VariableDeclarationList VarsBySection[],
                               const IceString &TranslateOnly) {
  for (VariableDeclaration *Var : Vars) {
    if (GlobalContext::matchSymbolName(Var->getName(), TranslateOnly)) {
      size_t Section = classifyGlobalSection(Var);
      assert(Section < ELFObjectWriter::NumSectionTypes);
      VarsBySection[Section].push_back(Var);
    }
  }
}

} // end of anonymous namespace

void ELFObjectWriter::writeDataSection(const VariableDeclarationList &Vars,
                                       FixupKind RelocationKind,
                                       const IceString &SectionSuffix) {
  assert(!SectionNumbersAssigned);
  VariableDeclarationList VarsBySection[ELFObjectWriter::NumSectionTypes];
  for (auto &SectionList : VarsBySection)
    SectionList.reserve(Vars.size());
  partitionGlobalsBySection(Vars, VarsBySection,
                            Ctx.getFlags().getTranslateOnly());
  size_t I = 0;
  for (auto &SectionList : VarsBySection) {
    writeDataOfType(static_cast<SectionType>(I++), SectionList, RelocationKind,
                    SectionSuffix);
  }
}

namespace {
IceString MangleSectionName(const char Base[], const IceString &Suffix) {
  if (Suffix.empty())
    return Base;
  return Base + ("." + Suffix);
}
} // end of anonymous namespace

// TODO(jvoung): Handle fdata-sections.
void ELFObjectWriter::writeDataOfType(SectionType ST,
                                      const VariableDeclarationList &Vars,
                                      FixupKind RelocationKind,
                                      const IceString &SectionSuffix) {
  if (Vars.empty())
    return;
  ELFDataSection *Section;
  ELFRelocationSection *RelSection;
  IceString SectionName;
  Elf64_Xword ShAddralign = 1;
  for (VariableDeclaration *Var : Vars) {
    Elf64_Xword Align = Var->getAlignment();
    ShAddralign = std::max(ShAddralign, Align);
  }
  constexpr Elf64_Xword ShEntsize = 0; // non-uniform data element size.
  // Lift this out, so it can be re-used if we do fdata-sections?
  switch (ST) {
  case ROData: {
    const IceString SectionName = MangleSectionName(".rodata", SectionSuffix);
    constexpr Elf64_Xword ShFlags = SHF_ALLOC;
    Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, ShFlags,
                                            ShAddralign, ShEntsize);
    Section->setFileOffset(alignFileOffset(ShAddralign));
    RODataSections.push_back(Section);
    RelSection = createRelocationSection(Section);
    RelRODataSections.push_back(RelSection);
    break;
  }
  case Data: {
    const IceString SectionName = MangleSectionName(".data", SectionSuffix);
    constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_WRITE;
    Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, ShFlags,
                                            ShAddralign, ShEntsize);
    Section->setFileOffset(alignFileOffset(ShAddralign));
    DataSections.push_back(Section);
    RelSection = createRelocationSection(Section);
    RelDataSections.push_back(RelSection);
    break;
  }
  case BSS: {
    const IceString SectionName = MangleSectionName(".bss", SectionSuffix);
    constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_WRITE;
    Section = createSection<ELFDataSection>(SectionName, SHT_NOBITS, ShFlags,
                                            ShAddralign, ShEntsize);
    Section->setFileOffset(alignFileOffset(ShAddralign));
    BSSSections.push_back(Section);
    break;
  }
  case NumSectionTypes:
    llvm::report_fatal_error("Unknown SectionType");
    break;
  }

  constexpr uint8_t SymbolType = STT_OBJECT;
  for (VariableDeclaration *Var : Vars) {
    // If the variable declaration does not have an initializer, its symtab
    // entry will be created separately.
    if (!Var->hasInitializer())
      continue;
    Elf64_Xword Align = Var->getAlignment();
    constexpr Elf64_Xword MinAlign = 1;
    Align = std::max(Align, MinAlign);
    Section->padToAlignment(Str, Align);
    SizeT SymbolSize = Var->getNumBytes();
    bool IsExternal = Var->isExternal() || Ctx.getFlags().getDisableInternal();
    const uint8_t SymbolBinding = IsExternal ? STB_GLOBAL : STB_LOCAL;
    IceString MangledName = Var->mangleName(&Ctx);
    SymTab->createDefinedSym(MangledName, SymbolType, SymbolBinding, Section,
                             Section->getCurrentSize(), SymbolSize);
    StrTab->add(MangledName);
    if (!Var->hasNonzeroInitializer()) {
      assert(ST == BSS || ST == ROData);
      if (ST == ROData)
        Section->appendZeros(Str, SymbolSize);
      else
        Section->setSize(Section->getCurrentSize() + SymbolSize);
    } else {
      assert(ST != BSS);
      for (const std::unique_ptr<VariableDeclaration::Initializer> &Init :
           Var->getInitializers()) {
        switch (Init->getKind()) {
        case VariableDeclaration::Initializer::DataInitializerKind: {
          const auto &Data =
              llvm::cast<VariableDeclaration::DataInitializer>(Init.get())
                  ->getContents();
          Section->appendData(Str, llvm::StringRef(Data.data(), Data.size()));
          break;
        }
        case VariableDeclaration::Initializer::ZeroInitializerKind:
          Section->appendZeros(Str, Init->getNumBytes());
          break;
        case VariableDeclaration::Initializer::RelocInitializerKind: {
          const auto Reloc =
              llvm::cast<VariableDeclaration::RelocInitializer>(Init.get());
          AssemblerFixup NewFixup;
          NewFixup.set_position(Section->getCurrentSize());
          NewFixup.set_kind(RelocationKind);
          constexpr bool SuppressMangling = true;
          NewFixup.set_value(Ctx.getConstantSym(
              Reloc->getOffset(), Reloc->getDeclaration()->mangleName(&Ctx),
              SuppressMangling));
          RelSection->addRelocation(NewFixup);
          Section->appendRelocationOffset(Str, RelSection->isRela(),
                                          Reloc->getOffset());
          break;
        }
        }
      }
    }
  }
}

void ELFObjectWriter::writeInitialELFHeader() {
  assert(!SectionNumbersAssigned);
  constexpr Elf64_Off DummySHOffset = 0;
  constexpr SizeT DummySHStrIndex = 0;
  constexpr SizeT DummyNumSections = 0;
  if (ELF64) {
    writeELFHeaderInternal<true>(DummySHOffset, DummySHStrIndex,
                                 DummyNumSections);
  } else {
    writeELFHeaderInternal<false>(DummySHOffset, DummySHStrIndex,
                                  DummyNumSections);
  }
}

template <bool IsELF64>
void ELFObjectWriter::writeELFHeaderInternal(Elf64_Off SectionHeaderOffset,
                                             SizeT SectHeaderStrIndex,
                                             SizeT NumSections) {
  // Write the e_ident: magic number, class, etc. The e_ident is byte order and
  // ELF class independent.
  Str.writeBytes(llvm::StringRef(ElfMagic, strlen(ElfMagic)));
  Str.write8(IsELF64 ? ELFCLASS64 : ELFCLASS32);
  Str.write8(ELFDATA2LSB);
  Str.write8(EV_CURRENT);
  Str.write8(ELFOSABI_NONE);
  constexpr uint8_t ELF_ABIVersion = 0;
  Str.write8(ELF_ABIVersion);
  Str.writeZeroPadding(EI_NIDENT - EI_PAD);

  // TODO(jvoung): Handle and test > 64K sections. See the generic ABI doc:
  // https://refspecs.linuxbase.org/elf/gabi4+/ch4.eheader.html e_shnum should
  // be 0 and then actual number of sections is stored in the sh_size member of
  // the 0th section.
  assert(NumSections < SHN_LORESERVE);
  assert(SectHeaderStrIndex < SHN_LORESERVE);

  const TargetArch Arch = Ctx.getFlags().getTargetArch();
  // Write the rest of the file header, which does depend on byte order and ELF
  // class.
  Str.writeLE16(ET_REL);                                        // e_type
  Str.writeLE16(getELFMachine(Ctx.getFlags().getTargetArch())); // e_machine
  Str.writeELFWord<IsELF64>(1);                                 // e_version
  // Since this is for a relocatable object, there is no entry point, and no
  // program headers.
  Str.writeAddrOrOffset<IsELF64>(0);                                // e_entry
  Str.writeAddrOrOffset<IsELF64>(0);                                // e_phoff
  Str.writeAddrOrOffset<IsELF64>(SectionHeaderOffset);              // e_shoff
  Str.writeELFWord<IsELF64>(getELFFlags(Arch));                     // e_flags
  Str.writeLE16(IsELF64 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr)); // e_ehsize
  static_assert(sizeof(Elf64_Ehdr) == 64 && sizeof(Elf32_Ehdr) == 52,
                "Elf_Ehdr sizes cannot be derived from sizeof");
  Str.writeLE16(0); // e_phentsize
  Str.writeLE16(0); // e_phnum
  Str.writeLE16(IsELF64 ? sizeof(Elf64_Shdr)
                        : sizeof(Elf32_Shdr)); // e_shentsize
  static_assert(sizeof(Elf64_Shdr) == 64 && sizeof(Elf32_Shdr) == 40,
                "Elf_Shdr sizes cannot be derived from sizeof");
  Str.writeLE16(static_cast<Elf64_Half>(NumSections));        // e_shnum
  Str.writeLE16(static_cast<Elf64_Half>(SectHeaderStrIndex)); // e_shstrndx
}

template <typename ConstType> void ELFObjectWriter::writeConstantPool(Type Ty) {
  ConstantList Pool = Ctx.getConstantPool(Ty);
  if (Pool.empty()) {
    return;
  }
  SizeT Align = typeAlignInBytes(Ty);
  size_t EntSize = typeWidthInBytes(Ty);
  char Buf[20];
  SizeT WriteAmt = std::min(EntSize, llvm::array_lengthof(Buf));
  // Check that we write the full PrimType.
  assert(WriteAmt == EntSize);
  // Assume that writing WriteAmt bytes at a time allows us to avoid aligning
  // between entries.
  assert(WriteAmt % Align == 0);
  constexpr Elf64_Xword ShFlags = SHF_ALLOC | SHF_MERGE;
  std::string SecBuffer;
  llvm::raw_string_ostream SecStrBuf(SecBuffer);
  SecStrBuf << ".rodata.cst" << WriteAmt;
  ELFDataSection *Section = createSection<ELFDataSection>(
      SecStrBuf.str(), SHT_PROGBITS, ShFlags, Align, WriteAmt);
  RODataSections.push_back(Section);
  SizeT OffsetInSection = 0;
  // The symbol table entry doesn't need to know the defined symbol's size
  // since this is in a section with a fixed Entry Size.
  constexpr SizeT SymbolSize = 0;
  Section->setFileOffset(alignFileOffset(Align));

  // If the -reorder-pooled-constant option is set to true, we should shuffle
  // the constants before we emit them.
  if (Ctx.getFlags().shouldReorderPooledConstants() && !Pool.empty()) {
    // Use the constant's kind value as the salt for creating random number
    // generator.
    Operand::OperandKind K = (*Pool.begin())->getKind();
    RandomNumberGenerator RNG(Ctx.getFlags().getRandomSeed(),
                              RPE_PooledConstantReordering, K);
    RandomShuffle(Pool.begin(), Pool.end(),
                  [&RNG](uint64_t N) { return (uint32_t)RNG.next(N); });
  }
  // Write the data.
  for (Constant *C : Pool) {
    if (!C->getShouldBePooled())
      continue;
    auto *Const = llvm::cast<ConstType>(C);
    std::string SymBuffer;
    llvm::raw_string_ostream SymStrBuf(SymBuffer);
    Const->emitPoolLabel(SymStrBuf, &Ctx);
    std::string &SymName = SymStrBuf.str();
    SymTab->createDefinedSym(SymName, STT_NOTYPE, STB_LOCAL, Section,
                             OffsetInSection, SymbolSize);
    StrTab->add(SymName);
    typename ConstType::PrimType Value = Const->getValue();
    memcpy(Buf, &Value, WriteAmt);
    Str.writeBytes(llvm::StringRef(Buf, WriteAmt));
    OffsetInSection += WriteAmt;
  }
  Section->setSize(OffsetInSection);
}

// Instantiate known needed versions of the template, since we are defining the
// function in the .cpp file instead of the .h file. We may need to instantiate
// constant pools for integers as well if we do constant-pooling of large
// integers to remove them from the instruction stream (fewer bytes controlled
// by an attacker).
template void ELFObjectWriter::writeConstantPool<ConstantFloat>(Type Ty);

template void ELFObjectWriter::writeConstantPool<ConstantDouble>(Type Ty);

template void ELFObjectWriter::writeConstantPool<ConstantInteger32>(Type Ty);

void ELFObjectWriter::writeAllRelocationSections() {
  writeRelocationSections(RelTextSections);
  writeRelocationSections(RelDataSections);
  writeRelocationSections(RelRODataSections);
}

void ELFObjectWriter::writeJumpTable(const JumpTableData &JT,
                                     FixupKind RelocationKind) {
  ELFDataSection *Section;
  ELFRelocationSection *RelSection;
  const Elf64_Xword PointerSize = typeWidthInBytes(getPointerType());
  const Elf64_Xword ShAddralign = PointerSize;
  const Elf64_Xword ShEntsize = PointerSize;
  const IceString SectionName =
      MangleSectionName(".rodata", JT.getFunctionName() + "$jumptable");
  Section = createSection<ELFDataSection>(SectionName, SHT_PROGBITS, SHF_ALLOC,
                                          ShAddralign, ShEntsize);
  Section->setFileOffset(alignFileOffset(ShAddralign));
  RODataSections.push_back(Section);
  RelSection = createRelocationSection(Section);
  RelRODataSections.push_back(RelSection);

  constexpr uint8_t SymbolType = STT_OBJECT;
  Section->padToAlignment(Str, PointerSize);
  bool IsExternal = Ctx.getFlags().getDisableInternal();
  const uint8_t SymbolBinding = IsExternal ? STB_GLOBAL : STB_LOCAL;
  IceString JumpTableName =
      InstJumpTable::makeName(JT.getFunctionName(), JT.getId());
  SymTab->createDefinedSym(JumpTableName, SymbolType, SymbolBinding, Section,
                           Section->getCurrentSize(), PointerSize);
  StrTab->add(JumpTableName);

  for (intptr_t TargetOffset : JT.getTargetOffsets()) {
    AssemblerFixup NewFixup;
    NewFixup.set_position(Section->getCurrentSize());
    NewFixup.set_kind(RelocationKind);
    constexpr bool SuppressMangling = true;
    NewFixup.set_value(Ctx.getConstantSym(TargetOffset, JT.getFunctionName(),
                                          SuppressMangling));
    RelSection->addRelocation(NewFixup);
    Section->appendRelocationOffset(Str, RelSection->isRela(), TargetOffset);
  }
}

void ELFObjectWriter::setUndefinedSyms(const ConstantList &UndefSyms) {
  for (const Constant *S : UndefSyms) {
    const auto *Sym = llvm::cast<ConstantRelocatable>(S);
    const IceString &Name = Sym->getName();
    bool BadIntrinsic;
    const Intrinsics::FullIntrinsicInfo *Info =
        Ctx.getIntrinsicsInfo().find(Name, BadIntrinsic);
    if (Info)
      continue;
    // Ignore BadIntrinsic, which is set if the name begins with "llvm." but
    // doesn't match a known intrinsic.  If we want this to turn into an error,
    // we should catch it early on.
    assert(Sym->getOffset() == 0);
    assert(Sym->getSuppressMangling());
    SymTab->noteUndefinedSym(Name, NullSection);
    StrTab->add(Name);
  }
}

void ELFObjectWriter::writeRelocationSections(RelSectionList &RelSections) {
  for (ELFRelocationSection *RelSec : RelSections) {
    Elf64_Off Offset = alignFileOffset(RelSec->getSectionAlign());
    RelSec->setFileOffset(Offset);
    RelSec->setSize(RelSec->getSectionDataSize());
    if (ELF64) {
      RelSec->writeData<true>(Ctx, Str, SymTab);
    } else {
      RelSec->writeData<false>(Ctx, Str, SymTab);
    }
  }
}

void ELFObjectWriter::writeNonUserSections() {
  // Write out the shstrtab now that all sections are known.
  ShStrTab->doLayout();
  ShStrTab->setSize(ShStrTab->getSectionDataSize());
  Elf64_Off ShStrTabOffset = alignFileOffset(ShStrTab->getSectionAlign());
  ShStrTab->setFileOffset(ShStrTabOffset);
  Str.writeBytes(ShStrTab->getSectionData());

  SectionList AllSections;
  assignSectionNumbersInfo(AllSections);

  // Finalize the regular StrTab and fix up references in the SymTab.
  StrTab->doLayout();
  StrTab->setSize(StrTab->getSectionDataSize());

  SymTab->updateIndices(StrTab);

  Elf64_Off SymTabOffset = alignFileOffset(SymTab->getSectionAlign());
  SymTab->setFileOffset(SymTabOffset);
  SymTab->setSize(SymTab->getSectionDataSize());
  SymTab->writeData(Str, ELF64);

  Elf64_Off StrTabOffset = alignFileOffset(StrTab->getSectionAlign());
  StrTab->setFileOffset(StrTabOffset);
  Str.writeBytes(StrTab->getSectionData());

  writeAllRelocationSections();

  // Write out the section headers.
  const size_t ShdrAlign = ELF64 ? 8 : 4;
  Elf64_Off ShOffset = alignFileOffset(ShdrAlign);
  for (const auto S : AllSections) {
    if (ELF64)
      S->writeHeader<true>(Str);
    else
      S->writeHeader<false>(Str);
  }

  // Finally write the updated ELF header w/ the correct number of sections.
  Str.seek(0);
  if (ELF64) {
    writeELFHeaderInternal<true>(ShOffset, ShStrTab->getNumber(),
                                 AllSections.size());
  } else {
    writeELFHeaderInternal<false>(ShOffset, ShStrTab->getNumber(),
                                  AllSections.size());
  }
}

} // end of namespace Ice
