You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

625 lines
21 KiB

//===- 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 "AArch64CA53Erratum835769Stub.h"
#include "AArch64CA53Erratum843419Stub.h"
#include "AArch64CA53Erratum843419Stub2.h"
#include "AArch64ELFDynamic.h"
#include "AArch64GNUInfo.h"
#include "AArch64InsnHelpers.h"
#include "AArch64LDBackend.h"
#include "AArch64LongBranchStub.h"
#include "AArch64Relocator.h"
#include "mcld/IRBuilder.h"
#include "mcld/LinkerConfig.h"
#include "mcld/Fragment/AlignFragment.h"
#include "mcld/Fragment/FillFragment.h"
#include "mcld/Fragment/NullFragment.h"
#include "mcld/Fragment/RegionFragment.h"
#include "mcld/Fragment/Stub.h"
#include "mcld/LD/BranchIslandFactory.h"
#include "mcld/LD/ELFFileFormat.h"
#include "mcld/LD/ELFSegment.h"
#include "mcld/LD/ELFSegmentFactory.h"
#include "mcld/LD/LDContext.h"
#include "mcld/LD/StubFactory.h"
#include "mcld/Support/MemoryRegion.h"
#include "mcld/Support/MemoryArea.h"
#include "mcld/Support/MsgHandling.h"
#include "mcld/Support/TargetRegistry.h"
#include "mcld/Target/ELFAttribute.h"
#include "mcld/Target/GNUInfo.h"
#include "mcld/Object/ObjectBuilder.h"
#include <llvm/ADT/Triple.h>
#include <llvm/ADT/Twine.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/ELF.h>
#include <cstring>
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_pDynamic(NULL),
m_pGOTSymbol(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) {
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);
}
}
bool AArch64GNULDBackend::initRelocator() {
if (m_pRelocator == NULL) {
m_pRelocator = new AArch64Relocator(*this, config());
}
return true;
}
const Relocator* AArch64GNULDBackend::getRelocator() const {
assert(m_pRelocator != NULL);
return m_pRelocator;
}
Relocator* AArch64GNULDBackend::getRelocator() {
assert(m_pRelocator != NULL);
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() && m_pDynamic == NULL)
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() ||
m_pGOTSymbol != NULL) {
m_pGOT->finalizeSectionSize();
defineGOTSymbol(pBuilder);
}
} else {
// when building shared object, the GOTPLT section is must
if (LinkerConfig::DynObj == config().codeGenType() ||
m_pGOTPLT->hasGOT1() || m_pGOTSymbol != NULL) {
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(m_pPLT != NULL);
m_pPLT->applyPLT0();
m_pPLT->applyPLT1();
}
// apply GOTPLT
if ((config().options().hasNow() && file_format->hasGOT()) ||
file_format->hasGOTPLT()) {
assert(m_pGOTPLT != NULL);
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(m_pDynamic != NULL);
return *m_pDynamic;
}
const AArch64ELFDynamic& AArch64GNULDBackend::dynamic() const {
assert(m_pDynamic != NULL);
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;
}
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;
}
void AArch64GNULDBackend::scanErrata(Module& pModule,
IRBuilder& pBuilder,
size_t& num_new_stubs,
size_t& stubs_strlen) {
// TODO: Implement AArch64 ErrataStubFactory to create the specific erratum
// stub and simplify the logics.
for (Module::iterator sect = pModule.begin(), sectEnd = pModule.end();
sect != sectEnd; ++sect) {
if (((*sect)->kind() == LDFileFormat::TEXT) && (*sect)->hasSectionData()) {
SectionData* sd = (*sect)->getSectionData();
for (SectionData::iterator it = sd->begin(), ie = sd->end(); it != ie;
++it) {
Fragment* frag = llvm::dyn_cast<RegionFragment>(it);
if (frag != NULL) {
FragmentRef* frag_ref = FragmentRef::Create(*frag, 0);
for (unsigned offset = 0; offset < frag->size();
offset += AArch64InsnHelpers::InsnSize) {
Stub* stub = getStubFactory()->create(*frag_ref,
pBuilder,
*getBRIslandFactory());
if (stub != NULL) {
// A stub symbol should be local
assert(stub->symInfo() != NULL && stub->symInfo()->isLocal());
const AArch64CA53ErratumStub* erratum_stub =
reinterpret_cast<const AArch64CA53ErratumStub*>(stub);
assert(erratum_stub != NULL);
// Rewrite the erratum instruction as a branch to the stub.
uint64_t offset = frag_ref->offset() +
erratum_stub->getErratumInsnOffset();
Relocation* reloc =
Relocation::Create(llvm::ELF::R_AARCH64_JUMP26,
*(FragmentRef::Create(*frag, offset)),
/* pAddend */0);
reloc->setSymInfo(stub->symInfo());
reloc->target() = AArch64InsnHelpers::buildBranchInsn();
addExtraRelocation(reloc);
++num_new_stubs;
stubs_strlen += stub->symInfo()->nameSize() + 1;
}
frag_ref->assign(*frag, offset + AArch64InsnHelpers::InsnSize);
} // for each INSN
}
} // for each FRAGMENT
}
} // for each TEXT section
}
bool AArch64GNULDBackend::doRelax(Module& pModule,
IRBuilder& pBuilder,
bool& pFinished) {
assert(getStubFactory() != NULL && getBRIslandFactory() != NULL);
// Number of new stubs added
size_t num_new_stubs = 0;
// String lengh to hold new stub symbols
size_t stubs_strlen = 0;
if (config().targets().fixCA53Erratum835769() ||
config().targets().fixCA53Erratum843419()) {
scanErrata(pModule, pBuilder, num_new_stubs, stubs_strlen);
}
ELFFileFormat* file_format = getOutputFormat();
// check branch relocs and create the related stubs if needed
Module::obj_iterator input, inEnd = pModule.obj_end();
for (input = pModule.obj_begin(); input != inEnd; ++input) {
LDContext::sect_iterator rs, rsEnd = (*input)->context()->relocSectEnd();
for (rs = (*input)->context()->relocSectBegin(); rs != rsEnd; ++rs) {
if (LDFileFormat::Ignore == (*rs)->kind() || !(*rs)->hasRelocData())
continue;
RelocData::iterator reloc, rEnd = (*rs)->getRelocData()->end();
for (reloc = (*rs)->getRelocData()->begin(); reloc != rEnd; ++reloc) {
Relocation* relocation = llvm::cast<Relocation>(reloc);
switch (relocation->type()) {
case llvm::ELF::R_AARCH64_CALL26:
case llvm::ELF::R_AARCH64_JUMP26: {
// calculate the possible symbol value
uint64_t sym_value = 0x0;
LDSymbol* symbol = relocation->symInfo()->outSymbol();
if (symbol->hasFragRef()) {
uint64_t value = symbol->fragRef()->getOutputOffset();
uint64_t addr =
symbol->fragRef()->frag()->getParent()->getSection().addr();
sym_value = addr + value;
}
if ((relocation->symInfo()->reserved() &
AArch64Relocator::ReservePLT) != 0x0) {
// FIXME: we need to find out the address of the specific plt
// entry
assert(file_format->hasPLT());
sym_value = file_format->getPLT().addr();
}
Stub* stub = getStubFactory()->create(*relocation, // relocation
sym_value, // symbol value
pBuilder,
*getBRIslandFactory());
if (stub != NULL) {
// a stub symbol should be local
assert(stub->symInfo() != NULL && stub->symInfo()->isLocal());
// reset the branch target of the reloc to this stub instead
relocation->setSymInfo(stub->symInfo());
++num_new_stubs;
stubs_strlen += stub->symInfo()->nameSize() + 1;
}
break;
}
default: {
break;
}
} // end of switch
} // for all relocations
} // for all relocation section
} // for all inputs
// Find the first fragment w/ invalid offset due to stub insertion.
std::vector<Fragment*> invalid_frags;
pFinished = true;
for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
island_end = getBRIslandFactory()->end();
island != island_end;
++island) {
if ((*island).size() > stubGroupSize()) {
error(diag::err_no_space_to_place_stubs) << stubGroupSize();
return false;
}
if ((*island).numOfStubs() == 0) {
continue;
}
Fragment* exit = &*(*island).end();
if (exit == &*(*island).begin()->getParent()->end()) {
continue;
}
if (((*island).offset() + (*island).size()) > exit->getOffset()) {
if (invalid_frags.empty() ||
(invalid_frags.back()->getParent() != (*island).getParent())) {
invalid_frags.push_back(exit);
pFinished = false;
}
continue;
}
}
// Reset the offset of invalid fragments.
for (auto it = invalid_frags.begin(), ie = invalid_frags.end(); it != ie;
++it) {
Fragment* invalid = *it;
while (invalid != NULL) {
invalid->setOffset(invalid->getPrevNode()->getOffset() +
invalid->getPrevNode()->size());
invalid = invalid->getNextNode();
}
}
// Fix up the size of .symtab, .strtab, and TEXT sections
if (num_new_stubs == 0) {
return false;
} else {
switch (config().options().getStripSymbolMode()) {
case GeneralOptions::StripSymbolMode::StripAllSymbols:
case GeneralOptions::StripSymbolMode::StripLocals:
break;
default: {
LDSection& symtab = file_format->getSymTab();
LDSection& strtab = file_format->getStrTab();
symtab.setSize(symtab.size() +
sizeof(llvm::ELF::Elf64_Sym) * num_new_stubs);
symtab.setInfo(symtab.getInfo() + num_new_stubs);
strtab.setSize(strtab.size() + stubs_strlen);
}
} // switch (config().options().getStripSymbolMode())
SectionData* prev = NULL;
for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
island_end = getBRIslandFactory()->end();
island != island_end;
++island) {
SectionData* sd = (*island).begin()->getParent();
if ((*island).numOfStubs() != 0) {
if (sd != prev) {
sd->getSection().setSize(sd->back().getOffset() + sd->back().size());
}
}
prev = sd;
}
return true;
} // if (num_new_stubs == 0)
}
bool AArch64GNULDBackend::initTargetStubs() {
StubFactory* factory = getStubFactory();
if (factory != NULL) {
factory->addPrototype(new AArch64LongBranchStub(config().isCodeIndep()));
if (config().targets().fixCA53Erratum835769()) {
factory->addPrototype(new AArch64CA53Erratum835769Stub());
}
if (config().targets().fixCA53Erratum843419()) {
factory->addPrototype(new AArch64CA53Erratum843419Stub());
factory->addPrototype(new AArch64CA53Erratum843419Stub2());
}
return true;
}
return false;
}
void AArch64GNULDBackend::doCreateProgramHdrs(Module& pModule) {
}
bool AArch64GNULDBackend::finalizeTargetSymbols() {
return true;
}
bool AArch64GNULDBackend::mergeSection(Module& pModule,
const Input& pInput,
LDSection& pSection) {
return true;
}
bool AArch64GNULDBackend::readSection(Input& pInput, SectionData& pSD) {
return true;
}
AArch64GOT& AArch64GNULDBackend::getGOT() {
assert(m_pGOT != NULL && "GOT section not exist");
return *m_pGOT;
}
const AArch64GOT& AArch64GNULDBackend::getGOT() const {
assert(m_pGOT != NULL && "GOT section not exist");
return *m_pGOT;
}
AArch64GOT& AArch64GNULDBackend::getGOTPLT() {
assert(m_pGOTPLT != NULL && "GOTPLT section not exist");
return *m_pGOTPLT;
}
const AArch64GOT& AArch64GNULDBackend::getGOTPLT() const {
assert(m_pGOTPLT != NULL && "GOTPLT section not exist");
return *m_pGOTPLT;
}
AArch64PLT& AArch64GNULDBackend::getPLT() {
assert(m_pPLT != NULL && "PLT section not exist");
return *m_pPLT;
}
const AArch64PLT& AArch64GNULDBackend::getPLT() const {
assert(m_pPLT != NULL && "PLT section not exist");
return *m_pPLT;
}
OutputRelocSection& AArch64GNULDBackend::getRelaDyn() {
assert(m_pRelaDyn != NULL && ".rela.dyn section not exist");
return *m_pRelaDyn;
}
const OutputRelocSection& AArch64GNULDBackend::getRelaDyn() const {
assert(m_pRelaDyn != NULL && ".rela.dyn section not exist");
return *m_pRelaDyn;
}
OutputRelocSection& AArch64GNULDBackend::getRelaPLT() {
assert(m_pRelaPLT != NULL && ".rela.plt section not exist");
return *m_pRelaPLT;
}
const OutputRelocSection& AArch64GNULDBackend::getRelaPLT() const {
assert(m_pRelaPLT != NULL && ".rela.plt section not exist");
return *m_pRelaPLT;
}
//===----------------------------------------------------------------------===//
// 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 mcld
//===----------------------------------------------------------------------===//
// Force static initialization.
//===----------------------------------------------------------------------===//
extern "C" void MCLDInitializeAArch64LDBackend() {
// Register the linker backend
mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheAArch64Target,
mcld::createAArch64LDBackend);
}