//===- ELFFileFormat.cpp --------------------------------------------------===//
//
//                     The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/LD/ELFFileFormat.h>
#include <mcld/Object/ObjectBuilder.h>
#include <mcld/Target/GNULDBackend.h>

#include <llvm/Support/ELF.h>

using namespace mcld;

ELFFileFormat::ELFFileFormat()
  : f_pNULLSection(NULL),
    f_pGOT(NULL),
    f_pPLT(NULL),
    f_pRelDyn(NULL),
    f_pRelPlt(NULL),
    f_pRelaDyn(NULL),
    f_pRelaPlt(NULL),
    f_pComment(NULL),
    f_pData1(NULL),
    f_pDebug(NULL),
    f_pDynamic(NULL),
    f_pDynStrTab(NULL),
    f_pDynSymTab(NULL),
    f_pFini(NULL),
    f_pFiniArray(NULL),
    f_pHashTab(NULL),
    f_pInit(NULL),
    f_pInitArray(NULL),
    f_pInterp(NULL),
    f_pLine(NULL),
    f_pNote(NULL),
    f_pPreInitArray(NULL),
    f_pROData1(NULL),
    f_pShStrTab(NULL),
    f_pStrTab(NULL),
    f_pSymTab(NULL),
    f_pTBSS(NULL),
    f_pTData(NULL),
    f_pCtors(NULL),
    f_pDataRelRo(NULL),
    f_pDtors(NULL),
    f_pEhFrame(NULL),
    f_pEhFrameHdr(NULL),
    f_pGCCExceptTable(NULL),
    f_pGNUVersion(NULL),
    f_pGNUVersionD(NULL),
    f_pGNUVersionR(NULL),
    f_pGOTPLT(NULL),
    f_pJCR(NULL),
    f_pNoteABITag(NULL),
    f_pStab(NULL),
    f_pStabStr(NULL),
    f_pStack(NULL),
    f_pStackNote(NULL),
    f_pDataRelRoLocal(NULL),
    f_pGNUHashTab(NULL) {

}

void ELFFileFormat::initStdSections(ObjectBuilder& pBuilder, unsigned int pBitClass)
{
  f_pTextSection     = pBuilder.CreateSection(".text",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR,
                                              0x1);
  f_pNULLSection     = pBuilder.CreateSection("",
                                              LDFileFormat::Null,
                                              llvm::ELF::SHT_NULL,
                                              0x0);
  f_pReadOnlySection = pBuilder.CreateSection(".rodata",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC,
                                              0x1);

  f_pBSSSection      = pBuilder.CreateSection(".bss",
                                              LDFileFormat::BSS,
                                              llvm::ELF::SHT_NOBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pComment         = pBuilder.CreateSection(".comment",
                                              LDFileFormat::MetaData,
                                              llvm::ELF::SHT_PROGBITS,
                                              0x0,
                                              0x1);
  f_pDataSection     = pBuilder.CreateSection(".data",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pData1           = pBuilder.CreateSection(".data1",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pDebug           = pBuilder.CreateSection(".debug",
                                              LDFileFormat::Debug,
                                              llvm::ELF::SHT_PROGBITS,
                                              0x0,
                                              0x1);
  f_pInit            = pBuilder.CreateSection(".init",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR,
                                              0x1);
  f_pInitArray       = pBuilder.CreateSection(".init_array",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_INIT_ARRAY,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pFini            = pBuilder.CreateSection(".fini",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR,
                                              0x1);
  f_pFiniArray       = pBuilder.CreateSection(".fini_array",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_FINI_ARRAY,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pLine            = pBuilder.CreateSection(".line",
                                              LDFileFormat::Debug,
                                              llvm::ELF::SHT_PROGBITS,
                                              0x0,
                                              0x1);
  f_pPreInitArray    = pBuilder.CreateSection(".preinit_array",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PREINIT_ARRAY,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  // the definition of SHF_XXX attributes of rodata in Linux Standard Base
  // conflicts with System V standard. We follow System V standard.
  f_pROData1         = pBuilder.CreateSection(".rodata1",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC,
                                              0x1);
  f_pShStrTab        = pBuilder.CreateSection(".shstrtab",
                                              LDFileFormat::NamePool,
                                              llvm::ELF::SHT_STRTAB,
                                              0x0,
                                              0x1);
  // In ELF Spec Book I, p1-16. If symbol table and string table are in 
  // loadable segments, set the attribute to SHF_ALLOC bit. But in the
  // real world, this bit always turn off.
  f_pSymTab          = pBuilder.CreateSection(".symtab",
                                              LDFileFormat::NamePool,
                                              llvm::ELF::SHT_SYMTAB,
                                              0x0,
                                              pBitClass / 8);

  f_pStrTab          = pBuilder.CreateSection(".strtab",
                                              LDFileFormat::NamePool,
                                              llvm::ELF::SHT_STRTAB,
                                              0x0,
                                              0x1);
  f_pTBSS            = pBuilder.CreateSection(".tbss",
                                              LDFileFormat::BSS,
                                              llvm::ELF::SHT_NOBITS,
                                              llvm::ELF::SHF_ALLOC |
                                              llvm::ELF::SHF_WRITE |
                                              llvm::ELF::SHF_TLS,
                                              0x1);
  f_pTData           = pBuilder.CreateSection(".tdata",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC |
                                              llvm::ELF::SHF_WRITE |
                                              llvm::ELF::SHF_TLS,
                                              0x1);

  /// @ref 10.3.1.2, ISO/IEC 23360, Part 1:2010(E), p. 24.
  f_pCtors           = pBuilder.CreateSection(".ctors",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pDataRelRo       = pBuilder.CreateSection(".data.rel.ro",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pDtors           = pBuilder.CreateSection(".dtors",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pEhFrame         = pBuilder.CreateSection(".eh_frame",
                                              LDFileFormat::EhFrame,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC,
                                              0x4);
  f_pGCCExceptTable  = pBuilder.CreateSection(".gcc_except_table",
                                              LDFileFormat::GCCExceptTable,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC,
                                              0x4);
  f_pGNUVersion      = pBuilder.CreateSection(".gnu.version",
                                              LDFileFormat::Version,
                                              llvm::ELF::SHT_GNU_versym,
                                              llvm::ELF::SHF_ALLOC,
                                              0x1);
  f_pGNUVersionD     = pBuilder.CreateSection(".gnu.version_d",
                                              LDFileFormat::Version,
                                              llvm::ELF::SHT_GNU_verdef,
                                              llvm::ELF::SHF_ALLOC,
                                              0x1);
  f_pGNUVersionR     = pBuilder.CreateSection(".gnu.version_r",
                                              LDFileFormat::Version,
                                              llvm::ELF::SHT_GNU_verneed,
                                              llvm::ELF::SHF_ALLOC,
                                              0x1);
  f_pJCR             = pBuilder.CreateSection(".jcr",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  f_pStab            = pBuilder.CreateSection(".stab",
                                              LDFileFormat::Debug,
                                              llvm::ELF::SHT_PROGBITS,
                                              0x0,
                                              0x1);
  f_pStabStr         = pBuilder.CreateSection(".stabstr",
                                              LDFileFormat::Debug,
                                              llvm::ELF::SHT_STRTAB,
                                              0x0,
                                              0x1);
  f_pStackNote       = pBuilder.CreateSection(".note.GNU-stack",
                                              LDFileFormat::StackNote,
                                              llvm::ELF::SHT_PROGBITS,
                                              0x0,
                                              0x1);

  /// @ref GCC convention, see http://www.airs.com/blog/archives/189
  f_pDataRelRoLocal  = pBuilder.CreateSection(".data.rel.ro.local",
                                              LDFileFormat::Regular,
                                              llvm::ELF::SHT_PROGBITS,
                                              llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE,
                                              0x1);
  /// Initialize format dependent sections. (sections for executable and shared
  /// objects)
  initObjectFormat(pBuilder, pBitClass);
}