//===- AArch64LDBackend.cpp -----------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "AArch64.h"
#include "AArch64ELFDynamic.h"
#include "AArch64GNUInfo.h"
#include "AArch64LDBackend.h"
#include "AArch64Relocator.h"
#include <cstring>
#include <llvm/ADT/Triple.h>
#include <llvm/ADT/Twine.h>
#include <llvm/Support/ELF.h>
#include <llvm/Support/Casting.h>
#include <mcld/IRBuilder.h>
#include <mcld/LinkerConfig.h>
#include <mcld/Fragment/FillFragment.h>
#include <mcld/Fragment/AlignFragment.h>
#include <mcld/Fragment/RegionFragment.h>
#include <mcld/Fragment/Stub.h>
#include <mcld/Fragment/NullFragment.h>
#include <mcld/Support/MemoryRegion.h>
#include <mcld/Support/MemoryArea.h>
#include <mcld/Support/MsgHandling.h>
#include <mcld/Support/TargetRegistry.h>
#include <mcld/LD/BranchIslandFactory.h>
#include <mcld/LD/StubFactory.h>
#include <mcld/LD/LDContext.h>
#include <mcld/LD/ELFFileFormat.h>
#include <mcld/LD/ELFSegmentFactory.h>
#include <mcld/LD/ELFSegment.h>
#include <mcld/Target/ELFAttribute.h>
#include <mcld/Target/GNUInfo.h>
#include <mcld/Object/ObjectBuilder.h>
using namespace mcld;
//===----------------------------------------------------------------------===//
// AArch64GNULDBackend
//===----------------------------------------------------------------------===//
AArch64GNULDBackend::AArch64GNULDBackend(const LinkerConfig& pConfig,
GNUInfo* pInfo)
: GNULDBackend(pConfig, pInfo),
m_pRelocator(NULL),
m_pGOT(NULL),
m_pGOTPLT(NULL),
m_pPLT(NULL),
m_pRelaDyn(NULL),
m_pRelaPLT(NULL),
// m_pAttrData(NULL),
m_pDynamic(NULL),
m_pGOTSymbol(NULL)
// m_pAttributes(NULL)
{
}
AArch64GNULDBackend::~AArch64GNULDBackend()
{
if (m_pRelocator != NULL)
delete m_pRelocator;
if (m_pGOT == m_pGOTPLT) {
if (m_pGOT != NULL)
delete m_pGOT;
} else {
if (m_pGOT != NULL)
delete m_pGOT;
if (m_pGOTPLT != NULL)
delete m_pGOTPLT;
}
if (m_pPLT != NULL)
delete m_pPLT;
if (m_pRelaDyn != NULL)
delete m_pRelaDyn;
if (m_pRelaPLT != NULL)
delete m_pRelaPLT;
if (m_pDynamic != NULL)
delete m_pDynamic;
}
void AArch64GNULDBackend::initTargetSections(Module& pModule,
ObjectBuilder& pBuilder)
{
// TODO
if (LinkerConfig::Object != config().codeGenType()) {
ELFFileFormat* file_format = getOutputFormat();
// initialize .got
LDSection& got = file_format->getGOT();
m_pGOT = new AArch64GOT(got);
if (config().options().hasNow()) {
// when -z now is given, there will be only one .got section (contains
// both GOTPLT and normal GOT entries), create GOT0 for .got section and
// set m_pGOTPLT to the same .got
m_pGOT->createGOT0();
m_pGOTPLT = m_pGOT;
}
else {
// Otherwise, got should be seperated to two sections, .got and .got.plt
// initialize .got.plt
LDSection& gotplt = file_format->getGOTPLT();
m_pGOTPLT = new AArch64GOT(gotplt);
m_pGOTPLT->createGOT0();
}
// initialize .plt
LDSection& plt = file_format->getPLT();
m_pPLT = new AArch64PLT(plt, *m_pGOTPLT);
// initialize .rela.plt
LDSection& relaplt = file_format->getRelaPlt();
relaplt.setLink(&plt);
m_pRelaPLT = new OutputRelocSection(pModule, relaplt);
// initialize .rela.dyn
LDSection& reladyn = file_format->getRelaDyn();
m_pRelaDyn = new OutputRelocSection(pModule, reladyn);
}
}
void AArch64GNULDBackend::initTargetSymbols(IRBuilder& pBuilder,
Module& pModule)
{
// Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the
// same name in input
if (LinkerConfig::Object != config().codeGenType()) {
m_pGOTSymbol = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
"_GLOBAL_OFFSET_TABLE_",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Null(),
ResolveInfo::Hidden);
}
// TODO
}
bool AArch64GNULDBackend::initRelocator()
{
if (NULL == m_pRelocator) {
m_pRelocator = new AArch64Relocator(*this, config());
}
return true;
}
const Relocator* AArch64GNULDBackend::getRelocator() const
{
assert(NULL != m_pRelocator);
return m_pRelocator;
}
Relocator* AArch64GNULDBackend::getRelocator()
{
assert(NULL != m_pRelocator);
return m_pRelocator;
}
void AArch64GNULDBackend::defineGOTSymbol(IRBuilder& pBuilder)
{
// define symbol _GLOBAL_OFFSET_TABLE_ when .got create
if (m_pGOTSymbol != NULL) {
pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
"_GLOBAL_OFFSET_TABLE_",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Create(*(m_pGOTPLT->begin()), 0x0),
ResolveInfo::Hidden);
}
else {
m_pGOTSymbol = pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>(
"_GLOBAL_OFFSET_TABLE_",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Create(*(m_pGOTPLT->begin()), 0x0),
ResolveInfo::Hidden);
}
}
void AArch64GNULDBackend::doPreLayout(IRBuilder& pBuilder)
{
// initialize .dynamic data
if (!config().isCodeStatic() && NULL == m_pDynamic)
m_pDynamic = new AArch64ELFDynamic(*this, config());
if (LinkerConfig::Object != config().codeGenType()) {
// set .got size
if (config().options().hasNow()) {
// when building shared object, the GOTPLT section is must
if (LinkerConfig::DynObj == config().codeGenType() ||
m_pGOT->hasGOT1() ||
NULL != m_pGOTSymbol) {
m_pGOT->finalizeSectionSize();
defineGOTSymbol(pBuilder);
}
}
else {
// when building shared object, the GOTPLT section is must
if (LinkerConfig::DynObj == config().codeGenType() ||
m_pGOTPLT->hasGOT1() ||
NULL != m_pGOTSymbol) {
m_pGOTPLT->finalizeSectionSize();
defineGOTSymbol(pBuilder);
}
if (m_pGOT->hasGOT1())
m_pGOT->finalizeSectionSize();
}
// set .plt size
if (m_pPLT->hasPLT1())
m_pPLT->finalizeSectionSize();
ELFFileFormat* file_format = getOutputFormat();
// set .rela.dyn size
if (!m_pRelaDyn->empty()) {
assert(!config().isCodeStatic() &&
"static linkage should not result in a dynamic relocation section");
file_format->getRelaDyn().setSize(
m_pRelaDyn->numOfRelocs() * getRelaEntrySize());
}
// set .rela.plt size
if (!m_pRelaPLT->empty()) {
assert(!config().isCodeStatic() &&
"static linkage should not result in a dynamic relocation section");
file_format->getRelaPlt().setSize(
m_pRelaPLT->numOfRelocs() * getRelaEntrySize());
}
}
}
void AArch64GNULDBackend::doPostLayout(Module& pModule, IRBuilder& pBuilder)
{
const ELFFileFormat *file_format = getOutputFormat();
// apply PLT
if (file_format->hasPLT()) {
assert(NULL != m_pPLT);
m_pPLT->applyPLT0();
m_pPLT->applyPLT1();
}
// apply GOTPLT
if ((config().options().hasNow() && file_format->hasGOT()) ||
file_format->hasGOTPLT()) {
assert(NULL != m_pGOTPLT);
if (LinkerConfig::DynObj == config().codeGenType())
m_pGOTPLT->applyGOT0(file_format->getDynamic().addr());
else {
// executable file and object file? should fill with zero.
m_pGOTPLT->applyGOT0(0);
}
}
}
AArch64ELFDynamic& AArch64GNULDBackend::dynamic()
{
assert(NULL != m_pDynamic);
return *m_pDynamic;
}
const AArch64ELFDynamic& AArch64GNULDBackend::dynamic() const
{
assert(NULL != m_pDynamic);
return *m_pDynamic;
}
uint64_t AArch64GNULDBackend::emitSectionData(const LDSection& pSection,
MemoryRegion& pRegion) const
{
assert(pRegion.size() && "Size of MemoryRegion is zero!");
const ELFFileFormat* file_format = getOutputFormat();
if (file_format->hasPLT() && (&pSection == &(file_format->getPLT()))) {
uint64_t result = m_pPLT->emit(pRegion);
return result;
}
if (file_format->hasGOT() && (&pSection == &(file_format->getGOT()))) {
uint64_t result = m_pGOT->emit(pRegion);
return result;
}
if (file_format->hasGOTPLT() && (&pSection == &(file_format->getGOTPLT()))) {
uint64_t result = m_pGOT->emit(pRegion);
return result;
}
// TODO
return pRegion.size();
}
unsigned int
AArch64GNULDBackend::getTargetSectionOrder(const LDSection& pSectHdr) const
{
const ELFFileFormat* file_format = getOutputFormat();
if (file_format->hasGOT() && (&pSectHdr == &file_format->getGOT())) {
if (config().options().hasNow())
return SHO_RELRO;
return SHO_RELRO_LAST;
}
if (file_format->hasGOTPLT() && (&pSectHdr == &file_format->getGOTPLT()))
return SHO_NON_RELRO_FIRST;
if (file_format->hasPLT() && (&pSectHdr == &file_format->getPLT()))
return SHO_PLT;
return SHO_UNDEFINED;
}
bool AArch64GNULDBackend::doRelax(Module& pModule,
IRBuilder& pBuilder,
bool& pFinished)
{
// TODO
return false;
}
bool AArch64GNULDBackend::initTargetStubs()
{
// TODO
return true;
}
void AArch64GNULDBackend::doCreateProgramHdrs(Module& pModule)
{
// TODO
}
bool AArch64GNULDBackend::finalizeTargetSymbols()
{
// TODO
return true;
}
bool AArch64GNULDBackend::mergeSection(Module& pModule,
const Input& pInput,
LDSection& pSection)
{
// TODO
return true;
}
bool AArch64GNULDBackend::readSection(Input& pInput, SectionData& pSD)
{
// TODO
return true;
}
AArch64GOT& AArch64GNULDBackend::getGOT()
{
assert(NULL != m_pGOT && "GOT section not exist");
return *m_pGOT;
}
const AArch64GOT& AArch64GNULDBackend::getGOT() const
{
assert(NULL != m_pGOT && "GOT section not exist");
return *m_pGOT;
}
AArch64GOT& AArch64GNULDBackend::getGOTPLT()
{
assert(NULL != m_pGOTPLT && "GOTPLT section not exist");
return *m_pGOTPLT;
}
const AArch64GOT& AArch64GNULDBackend::getGOTPLT() const
{
assert(NULL != m_pGOTPLT && "GOTPLT section not exist");
return *m_pGOTPLT;
}
AArch64PLT& AArch64GNULDBackend::getPLT()
{
assert(NULL != m_pPLT && "PLT section not exist");
return *m_pPLT;
}
const AArch64PLT& AArch64GNULDBackend::getPLT() const
{
assert(NULL != m_pPLT && "PLT section not exist");
return *m_pPLT;
}
OutputRelocSection& AArch64GNULDBackend::getRelaDyn()
{
assert(NULL != m_pRelaDyn && ".rela.dyn section not exist");
return *m_pRelaDyn;
}
const OutputRelocSection& AArch64GNULDBackend::getRelaDyn() const
{
assert(NULL != m_pRelaDyn && ".rela.dyn section not exist");
return *m_pRelaDyn;
}
OutputRelocSection& AArch64GNULDBackend::getRelaPLT()
{
assert(NULL != m_pRelaPLT && ".rela.plt section not exist");
return *m_pRelaPLT;
}
const OutputRelocSection& AArch64GNULDBackend::getRelaPLT() const
{
assert(NULL != m_pRelaPLT && ".rela.plt section not exist");
return *m_pRelaPLT;
}
namespace mcld {
//===----------------------------------------------------------------------===//
// createAArch64LDBackend - the help funtion to create corresponding
// AArch64LDBackend
//===----------------------------------------------------------------------===//
TargetLDBackend* createAArch64LDBackend(const LinkerConfig& pConfig)
{
if (pConfig.targets().triple().isOSDarwin()) {
assert(0 && "MachO linker is not supported yet");
/**
return new AArch64MachOLDBackend(createAArch64MachOArchiveReader,
createAArch64MachOObjectReader,
createAArch64MachOObjectWriter);
**/
}
if (pConfig.targets().triple().isOSWindows()) {
assert(0 && "COFF linker is not supported yet");
/**
return new AArch64COFFLDBackend(createAArch64COFFArchiveReader,
createAArch64COFFObjectReader,
createAArch64COFFObjectWriter);
**/
}
return new AArch64GNULDBackend(pConfig,
new AArch64GNUInfo(pConfig.targets().triple()));
}
} // namespace of mcld
//===----------------------------------------------------------------------===//
// Force static initialization.
//===----------------------------------------------------------------------===//
extern "C" void MCLDInitializeAArch64LDBackend() {
// Register the linker backend
mcld::TargetRegistry::RegisterTargetLDBackend(TheAArch64Target,
createAArch64LDBackend);
}