blob: b2fbfb2ed2e25e643d2c5dd8786a76b2bcfdb34b [file] [log] [blame]
//===- ARMLDBackend.cpp ---------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ARM.h"
#include "ARMGNUInfo.h"
#include "ARMELFAttributeData.h"
#include "ARMELFDynamic.h"
#include "ARMException.h"
#include "ARMLDBackend.h"
#include "ARMRelocator.h"
#include "ARMToARMStub.h"
#include "ARMToTHMStub.h"
#include "THMToTHMStub.h"
#include "THMToARMStub.h"
#include "mcld/IRBuilder.h"
#include "mcld/LinkerConfig.h"
#include "mcld/ADT/ilist_sort.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/Object/ObjectBuilder.h"
#include "mcld/Support/MemoryArea.h"
#include "mcld/Support/MemoryRegion.h"
#include "mcld/Support/MsgHandling.h"
#include "mcld/Support/TargetRegistry.h"
#include "mcld/Target/ELFAttribute.h"
#include "mcld/Target/GNUInfo.h"
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ADT/Twine.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/ELF.h>
#include <cstring>
#include <vector>
namespace mcld {
/// Fragment data for EXIDX_CANTUNWIND.
static const char g_CantUnwindEntry[8] = {
// Relocation to text section.
0, 0, 0, 0,
// EXIDX_CANTUNWIND (little endian.)
1, 0, 0, 0,
};
/// Helper function to create a local symbol at the end of the fragment.
static mcld::ResolveInfo*
CreateLocalSymbolToFragmentEnd(mcld::Module& pModule, mcld::Fragment& pFrag) {
// Create and add symbol to the name pool.
mcld::ResolveInfo* resolveInfo =
pModule.getNamePool().createSymbol(/* pName */"",
/* pIsDyn */false,
mcld::ResolveInfo::Section,
mcld::ResolveInfo::Define,
mcld::ResolveInfo::Local,
/* pSize */0,
mcld::ResolveInfo::Hidden);
if (resolveInfo == nullptr) {
return nullptr;
}
// Create input symbol.
mcld::LDSymbol* inputSym = mcld::LDSymbol::Create(*resolveInfo);
if (inputSym == nullptr) {
return nullptr;
}
inputSym->setFragmentRef(mcld::FragmentRef::Create(pFrag, pFrag.size()));
inputSym->setValue(/* pValue */0);
// The output symbol is simply an alias to the input symbol.
resolveInfo->setSymPtr(inputSym);
return resolveInfo;
}
/// Comparator to sort .ARM.exidx fragments according to the address of the
/// corresponding .text fragment.
class ExIdxFragmentComparator {
private:
const ARMExData& m_pExData;
public:
explicit ExIdxFragmentComparator(const ARMExData& pExData)
: m_pExData(pExData) {
}
bool operator()(const Fragment& a, const Fragment& b) {
ARMExSectionTuple* tupleA = m_pExData.getTupleByExIdx(&a);
ARMExSectionTuple* tupleB = m_pExData.getTupleByExIdx(&b);
Fragment* textFragA = tupleA->getTextFragment();
Fragment* textFragB = tupleB->getTextFragment();
uint64_t addrA = textFragA->getParent()->getSection().addr() +
textFragA->getOffset();
uint64_t addrB = textFragB->getParent()->getSection().addr() +
textFragB->getOffset();
return (addrA < addrB);
}
};
//===----------------------------------------------------------------------===//
// ARMGNULDBackend
//===----------------------------------------------------------------------===//
ARMGNULDBackend::ARMGNULDBackend(const LinkerConfig& pConfig, GNUInfo* pInfo)
: GNULDBackend(pConfig, pInfo),
m_pRelocator(NULL),
m_pGOT(NULL),
m_pPLT(NULL),
m_pRelDyn(NULL),
m_pRelPLT(NULL),
m_pAttrData(NULL),
m_pDynamic(NULL),
m_pGOTSymbol(NULL),
m_pEXIDXStart(NULL),
m_pEXIDXEnd(NULL),
m_pEXIDX(NULL),
m_pEXTAB(NULL),
m_pAttributes(NULL) {
}
ARMGNULDBackend::~ARMGNULDBackend() {
delete m_pRelocator;
delete m_pGOT;
delete m_pPLT;
delete m_pRelDyn;
delete m_pRelPLT;
delete m_pDynamic;
delete m_pAttrData;
}
void ARMGNULDBackend::initTargetSections(Module& pModule,
ObjectBuilder& pBuilder) {
// FIXME: Currently we set exidx and extab to "Exception" and directly emit
// them from input
m_pEXIDX =
pBuilder.CreateSection(".ARM.exidx",
LDFileFormat::Target,
llvm::ELF::SHT_ARM_EXIDX,
llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_LINK_ORDER,
config().targets().bitclass() / 8);
m_pEXTAB = pBuilder.CreateSection(".ARM.extab",
LDFileFormat::Target,
llvm::ELF::SHT_PROGBITS,
llvm::ELF::SHF_ALLOC,
0x1);
m_pAttributes = pBuilder.CreateSection(".ARM.attributes",
LDFileFormat::Target,
llvm::ELF::SHT_ARM_ATTRIBUTES,
0x0,
0x1);
// initialize "aeabi" attributes subsection
m_pAttrData = new ARMELFAttributeData();
attribute().registerAttributeData(*m_pAttrData);
if (LinkerConfig::Object != config().codeGenType()) {
ELFFileFormat* file_format = getOutputFormat();
// initialize .got
LDSection& got = file_format->getGOT();
m_pGOT = new ARMGOT(got);
// initialize .plt
LDSection& plt = file_format->getPLT();
m_pPLT = new ARMPLT(plt, *m_pGOT);
// initialize .rel.plt
LDSection& relplt = file_format->getRelPlt();
relplt.setLink(&plt);
// create SectionData and ARMRelDynSection
m_pRelPLT = new OutputRelocSection(pModule, relplt);
// initialize .rel.dyn
LDSection& reldyn = file_format->getRelDyn();
m_pRelDyn = new OutputRelocSection(pModule, reldyn);
}
}
void ARMGNULDBackend::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);
}
if (m_pEXIDX != NULL && m_pEXIDX->size() != 0x0) {
FragmentRef* exidx_start =
FragmentRef::Create(m_pEXIDX->getSectionData()->front(), 0x0);
FragmentRef* exidx_end = FragmentRef::Create(
m_pEXIDX->getSectionData()->front(), m_pEXIDX->size());
m_pEXIDXStart =
pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
"__exidx_start",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
exidx_start, // FragRef
ResolveInfo::Default);
m_pEXIDXEnd = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
"__exidx_end",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
exidx_end, // FragRef
ResolveInfo::Default);
// change __exidx_start/_end to local dynamic category
if (m_pEXIDXStart != NULL)
pModule.getSymbolTable().changeToDynamic(*m_pEXIDXStart);
if (m_pEXIDXEnd != NULL)
pModule.getSymbolTable().changeToDynamic(*m_pEXIDXEnd);
} else {
m_pEXIDXStart =
pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
"__exidx_start",
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
FragmentRef::Null(),
ResolveInfo::Default);
m_pEXIDXEnd = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
"__exidx_end",
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
FragmentRef::Null(),
ResolveInfo::Default);
}
}
bool ARMGNULDBackend::initRelocator() {
if (m_pRelocator == NULL) {
m_pRelocator = new ARMRelocator(*this, config());
}
return true;
}
const Relocator* ARMGNULDBackend::getRelocator() const {
assert(m_pRelocator != NULL);
return m_pRelocator;
}
Relocator* ARMGNULDBackend::getRelocator() {
assert(m_pRelocator != NULL);
return m_pRelocator;
}
void ARMGNULDBackend::doPreLayout(IRBuilder& pBuilder) {
// initialize .dynamic data
if (!config().isCodeStatic() && m_pDynamic == NULL)
m_pDynamic = new ARMELFDynamic(*this, config());
// set attribute section size
m_pAttributes->setSize(attribute().sizeOutput());
// set .got size
// when building shared object, the .got section is must
if (LinkerConfig::Object != config().codeGenType()) {
if (LinkerConfig::DynObj == config().codeGenType() || m_pGOT->hasGOT1() ||
m_pGOTSymbol != NULL) {
m_pGOT->finalizeSectionSize();
defineGOTSymbol(pBuilder);
}
// set .plt size
if (m_pPLT->hasPLT1())
m_pPLT->finalizeSectionSize();
ELFFileFormat* file_format = getOutputFormat();
// set .rel.dyn size
if (!m_pRelDyn->empty()) {
assert(
!config().isCodeStatic() &&
"static linkage should not result in a dynamic relocation section");
file_format->getRelDyn().setSize(m_pRelDyn->numOfRelocs() *
getRelEntrySize());
}
// set .rel.plt size
if (!m_pRelPLT->empty()) {
assert(
!config().isCodeStatic() &&
"static linkage should not result in a dynamic relocation section");
file_format->getRelPlt().setSize(m_pRelPLT->numOfRelocs() *
getRelEntrySize());
}
}
}
void ARMGNULDBackend::doPostLayout(Module& pModule, IRBuilder& pBuilder) {
const ELFFileFormat* file_format = getOutputFormat();
// apply PLT
if (file_format->hasPLT()) {
// Since we already have the size of LDSection PLT, m_pPLT should not be
// NULL.
assert(m_pPLT != NULL);
m_pPLT->applyPLT0();
m_pPLT->applyPLT1();
}
// apply GOT
if (file_format->hasGOT()) {
// Since we already have the size of GOT, m_pGOT should not be NULL.
assert(m_pGOT != NULL);
if (LinkerConfig::DynObj == config().codeGenType())
m_pGOT->applyGOT0(file_format->getDynamic().addr());
else {
// executable file and object file? should fill with zero.
m_pGOT->applyGOT0(0);
}
}
}
/// dynamic - the dynamic section of the target machine.
/// Use co-variant return type to return its own dynamic section.
ARMELFDynamic& ARMGNULDBackend::dynamic() {
assert(m_pDynamic != NULL);
return *m_pDynamic;
}
/// dynamic - the dynamic section of the target machine.
/// Use co-variant return type to return its own dynamic section.
const ARMELFDynamic& ARMGNULDBackend::dynamic() const {
assert(m_pDynamic != NULL);
return *m_pDynamic;
}
void ARMGNULDBackend::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_pGOT->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_pGOT->begin()), 0x0),
ResolveInfo::Hidden);
}
}
uint64_t ARMGNULDBackend::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 (&pSection == m_pAttributes) {
return attribute().emit(pRegion);
}
// FIXME: Currently Emitting .ARM.attributes, .ARM.exidx, and .ARM.extab
// directly from the input file.
const SectionData* sect_data = pSection.getSectionData();
SectionData::const_iterator frag_iter, frag_end = sect_data->end();
uint8_t* out_offset = pRegion.begin();
for (frag_iter = sect_data->begin(); frag_iter != frag_end; ++frag_iter) {
size_t size = frag_iter->size();
switch (frag_iter->getKind()) {
case Fragment::Fillment: {
const FillFragment& fill_frag = llvm::cast<FillFragment>(*frag_iter);
if (fill_frag.getValueSize() == 0) {
// virtual fillment, ignore it.
break;
}
memset(out_offset, fill_frag.getValue(), fill_frag.size());
break;
}
case Fragment::Region: {
const RegionFragment& region_frag =
llvm::cast<RegionFragment>(*frag_iter);
const char* start = region_frag.getRegion().begin();
memcpy(out_offset, start, size);
break;
}
case Fragment::Alignment: {
const AlignFragment& align_frag = llvm::cast<AlignFragment>(*frag_iter);
uint64_t count = size / align_frag.getValueSize();
switch (align_frag.getValueSize()) {
case 1u:
std::memset(out_offset, align_frag.getValue(), count);
break;
default:
llvm::report_fatal_error(
"unsupported value size for align fragment emission yet.\n");
break;
} // end switch
break;
}
case Fragment::Null: {
assert(0x0 == size);
break;
}
default:
llvm::report_fatal_error("unsupported fragment type.\n");
break;
} // end switch
out_offset += size;
} // end for
return pRegion.size();
}
/// finalizeSymbol - finalize the symbol value
bool ARMGNULDBackend::finalizeTargetSymbols() {
return true;
}
/// preMergeSections - hooks to be executed before merging sections
void ARMGNULDBackend::preMergeSections(Module& pModule) {
// Since the link relationship between .text and .ARM.exidx will be discarded
// after merging sections, we have to build the exception handling section
// mapping before section merge.
m_pExData = ARMExData::create(pModule);
}
/// postMergeSections - hooks to be executed after merging sections
void ARMGNULDBackend::postMergeSections(Module& pModule) {
if (m_pEXIDX->hasSectionData()) {
// Append the NullFragment so that __exidx_end can be correctly inserted.
NullFragment* null = new NullFragment(m_pEXIDX->getSectionData());
null->setOffset(m_pEXIDX->size());
}
}
bool ARMGNULDBackend::mergeSection(Module& pModule,
const Input& pInput,
LDSection& pSection) {
switch (pSection.type()) {
case llvm::ELF::SHT_ARM_ATTRIBUTES: {
return attribute().merge(pInput, pSection);
}
case llvm::ELF::SHT_ARM_EXIDX: {
assert(pSection.getLink() != NULL);
if ((pSection.getLink()->kind() == LDFileFormat::Ignore) ||
(pSection.getLink()->kind() == LDFileFormat::Folded)) {
// if the target section of the .ARM.exidx is Ignore, then it should be
// ignored as well
pSection.setKind(LDFileFormat::Ignore);
return true;
}
if (!m_pEXIDX->hasSectionData()) {
// Create SectionData for m_pEXIDX.
SectionData* sectData = IRBuilder::CreateSectionData(*m_pEXIDX);
// Initialize the alignment of m_pEXIDX.
const size_t alignExIdx = 4;
m_pEXIDX->setAlign(alignExIdx);
// Insert an AlignFragment to the beginning of m_pEXIDX.
AlignFragment* frag =
new AlignFragment(/*alignment*/alignExIdx,
/*the filled value*/0x0,
/*the size of filled value*/1u,
/*max bytes to emit*/alignExIdx - 1);
frag->setOffset(0);
frag->setParent(sectData);
sectData->getFragmentList().push_back(frag);
m_pEXIDX->setSize(frag->size());
}
// Move RegionFragment from pSection to m_pEXIDX.
uint64_t offset = m_pEXIDX->size();
SectionData::FragmentListType& src =
pSection.getSectionData()->getFragmentList();
SectionData::FragmentListType& dst =
m_pEXIDX->getSectionData()->getFragmentList();
SectionData::FragmentListType::iterator frag = src.begin();
SectionData::FragmentListType::iterator fragEnd = src.end();
while (frag != fragEnd) {
if (frag->getKind() != Fragment::Region) {
++frag;
} else {
frag->setParent(m_pEXIDX->getSectionData());
frag->setOffset(offset);
offset += frag->size();
dst.splice(dst.end(), src, frag++);
}
}
// Update the size of m_pEXIDX.
m_pEXIDX->setSize(offset);
return true;
}
default: {
ObjectBuilder builder(pModule);
builder.MergeSection(pInput, pSection);
return true;
}
} // end of switch
return true;
}
void ARMGNULDBackend::setUpReachedSectionsForGC(
const Module& pModule,
GarbageCollection::SectionReachedListMap& pSectReachedListMap) const {
// traverse all the input relocations to find the relocation sections applying
// .ARM.exidx sections
Module::const_obj_iterator input, inEnd = pModule.obj_end();
for (input = pModule.obj_begin(); input != inEnd; ++input) {
LDContext::const_sect_iterator rs,
rsEnd = (*input)->context()->relocSectEnd();
for (rs = (*input)->context()->relocSectBegin(); rs != rsEnd; ++rs) {
// bypass the discarded relocation section
// 1. its section kind is changed to Ignore. (The target section is a
// discarded group section.)
// 2. it has no reloc data. (All symbols in the input relocs are in the
// discarded group sections)
LDSection* reloc_sect = *rs;
LDSection* apply_sect = reloc_sect->getLink();
if ((LDFileFormat::Ignore == reloc_sect->kind()) ||
(!reloc_sect->hasRelocData()))
continue;
if (llvm::ELF::SHT_ARM_EXIDX == apply_sect->type()) {
// 1. set up the reference according to relocations
bool add_first = false;
GarbageCollection::SectionListTy* reached_sects = NULL;
RelocData::iterator reloc_it, rEnd = reloc_sect->getRelocData()->end();
for (reloc_it = reloc_sect->getRelocData()->begin(); reloc_it != rEnd;
++reloc_it) {
Relocation* reloc = llvm::cast<Relocation>(reloc_it);
ResolveInfo* sym = reloc->symInfo();
// only the target symbols defined in the input fragments can make the
// reference
if (sym == NULL)
continue;
if (!sym->isDefine() || !sym->outSymbol()->hasFragRef())
continue;
// only the target symbols defined in the concerned sections can make
// the reference
const LDSection* target_sect =
&sym->outSymbol()->fragRef()->frag()->getParent()->getSection();
if (target_sect->kind() != LDFileFormat::TEXT &&
target_sect->kind() != LDFileFormat::DATA &&
target_sect->kind() != LDFileFormat::BSS)
continue;
// setup the reached list, if we first add the element to reached list
// of this section, create an entry in ReachedSections map
if (!add_first) {
reached_sects = &pSectReachedListMap.getReachedList(*apply_sect);
add_first = true;
}
reached_sects->insert(target_sect);
}
reached_sects = NULL;
add_first = false;
// 2. set up the reference from XXX to .ARM.exidx.XXX
assert(apply_sect->getLink() != NULL);
pSectReachedListMap.addReference(*apply_sect->getLink(), *apply_sect);
}
}
}
}
bool ARMGNULDBackend::readSection(Input& pInput, SectionData& pSD) {
Fragment* frag = NULL;
uint32_t offset = pInput.fileOffset() + pSD.getSection().offset();
uint32_t size = pSD.getSection().size();
llvm::StringRef region = pInput.memArea()->request(offset, size);
if (region.size() == 0) {
// If the input section's size is zero, we got a NULL region.
// use a virtual fill fragment
frag = new FillFragment(0x0, 0, 0);
} else {
frag = new RegionFragment(region);
}
ObjectBuilder::AppendFragment(*frag, pSD);
return true;
}
ARMGOT& ARMGNULDBackend::getGOT() {
assert(m_pGOT != NULL && "GOT section not exist");
return *m_pGOT;
}
const ARMGOT& ARMGNULDBackend::getGOT() const {
assert(m_pGOT != NULL && "GOT section not exist");
return *m_pGOT;
}
ARMPLT& ARMGNULDBackend::getPLT() {
assert(m_pPLT != NULL && "PLT section not exist");
return *m_pPLT;
}
const ARMPLT& ARMGNULDBackend::getPLT() const {
assert(m_pPLT != NULL && "PLT section not exist");
return *m_pPLT;
}
OutputRelocSection& ARMGNULDBackend::getRelDyn() {
assert(m_pRelDyn != NULL && ".rel.dyn section not exist");
return *m_pRelDyn;
}
const OutputRelocSection& ARMGNULDBackend::getRelDyn() const {
assert(m_pRelDyn != NULL && ".rel.dyn section not exist");
return *m_pRelDyn;
}
OutputRelocSection& ARMGNULDBackend::getRelPLT() {
assert(m_pRelPLT != NULL && ".rel.plt section not exist");
return *m_pRelPLT;
}
const OutputRelocSection& ARMGNULDBackend::getRelPLT() const {
assert(m_pRelPLT != NULL && ".rel.plt section not exist");
return *m_pRelPLT;
}
ARMELFAttributeData& ARMGNULDBackend::getAttributeData() {
assert(m_pAttrData != NULL && ".ARM.attributes section not exist");
return *m_pAttrData;
}
const ARMELFAttributeData& ARMGNULDBackend::getAttributeData() const {
assert(m_pAttrData != NULL && ".ARM.attributes section not exist");
return *m_pAttrData;
}
unsigned int ARMGNULDBackend::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_LAST;
return SHO_DATA;
}
if (file_format->hasPLT() && (&pSectHdr == &file_format->getPLT()))
return SHO_PLT;
if (&pSectHdr == m_pEXIDX || &pSectHdr == m_pEXTAB) {
// put ARM.exidx and ARM.extab in the same order of .eh_frame
return SHO_EXCEPTION;
}
return SHO_UNDEFINED;
}
void ARMGNULDBackend::rewriteARMExIdxSection(Module& pModule) {
if (!m_pEXIDX->hasSectionData()) {
// Return if this is empty section.
return;
}
SectionData* sectData = m_pEXIDX->getSectionData();
SectionData::FragmentListType& list = sectData->getFragmentList();
// Move the first fragment (align fragment) and last fragment (null fragment)
// to temporary list because we would only like to sort the region fragment.
SectionData::FragmentListType tmp;
{
SectionData::iterator first = sectData->begin();
SectionData::iterator last = sectData->end();
--last;
assert(first->getKind() == Fragment::Alignment);
assert(last->getKind() == Fragment::Null);
tmp.splice(tmp.end(), list, first);
tmp.splice(tmp.end(), list, last);
}
// Sort the region fragments in the .ARM.exidx output section.
sort(list, ExIdxFragmentComparator(*m_pExData));
// Fix the coverage of the .ARM.exidx table.
llvm::StringRef cantUnwindRegion(g_CantUnwindEntry,
sizeof(g_CantUnwindEntry));
SectionData::FragmentListType::iterator it = list.begin();
if (it != list.end()) {
Fragment* prevTextFrag = m_pExData->getTupleByExIdx(&*it)->getTextFragment();
uint64_t prevTextEnd = prevTextFrag->getParent()->getSection().addr() +
prevTextFrag->getOffset() +
prevTextFrag->size();
++it;
while (it != list.end()) {
Fragment* currTextFrag =
m_pExData->getTupleByExIdx(&*it)->getTextFragment();
uint64_t currTextBegin = currTextFrag->getParent()->getSection().addr() +
currTextFrag->getOffset();
if (currTextBegin > prevTextEnd) {
// Found a gap. Insert a can't unwind entry.
RegionFragment* frag = new RegionFragment(cantUnwindRegion, nullptr);
frag->setParent(sectData);
list.insert(it, frag);
// Add PREL31 reference to the beginning of the uncovered region.
Relocation* reloc =
Relocation::Create(static_cast<uint32_t>(llvm::ELF::R_ARM_PREL31),
*FragmentRef::Create(*frag, /* pOffset */0),
/* pAddend */0);
reloc->setSymInfo(
CreateLocalSymbolToFragmentEnd(pModule, *prevTextFrag));
addExtraRelocation(reloc);
}
prevTextEnd = currTextBegin + currTextFrag->size();
prevTextFrag = currTextFrag;
++it;
}
// Add a can't unwind entry to terminate .ARM.exidx section.
RegionFragment* frag = new RegionFragment(cantUnwindRegion, nullptr);
frag->setParent(sectData);
list.push_back(frag);
// Add PREL31 reference to the end of the .text section.
Relocation* reloc =
Relocation::Create(static_cast<uint32_t>(llvm::ELF::R_ARM_PREL31),
*FragmentRef::Create(*frag, /* pOffset */0),
/* pAddend */0);
reloc->setSymInfo(CreateLocalSymbolToFragmentEnd(pModule, *prevTextFrag));
addExtraRelocation(reloc);
}
// Add the first and the last fragment back.
list.splice(list.begin(), tmp, tmp.begin());
list.splice(list.end(), tmp, tmp.begin());
// Update the fragment offsets.
uint64_t offset = 0;
for (SectionData::iterator it = sectData->begin(), end = sectData->end();
it != end; ++it) {
it->setOffset(offset);
offset += it->size();
}
// Update the section size.
m_pEXIDX->setSize(offset);
// Rebuild the section header.
setOutputSectionAddress(pModule);
}
/// relax - the relaxation pass
bool ARMGNULDBackend::relax(Module& pModule, IRBuilder& pBuilder) {
if (!GNULDBackend::relax(pModule, pBuilder)) {
return false;
}
rewriteARMExIdxSection(pModule);
return true;
}
/// doRelax
bool ARMGNULDBackend::doRelax(Module& pModule,
IRBuilder& pBuilder,
bool& pFinished) {
assert(getStubFactory() != NULL && getBRIslandFactory() != NULL);
bool isRelaxed = false;
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_ARM_PC24:
case llvm::ELF::R_ARM_CALL:
case llvm::ELF::R_ARM_JUMP24:
case llvm::ELF::R_ARM_PLT32:
case llvm::ELF::R_ARM_THM_CALL:
case llvm::ELF::R_ARM_THM_XPC22:
case llvm::ELF::R_ARM_THM_JUMP24:
case llvm::ELF::R_ARM_THM_JUMP19: {
// 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() &
ARMRelocator::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) {
assert(stub->symInfo() != NULL);
// reset the branch target of the reloc to this stub instead
relocation->setSymInfo(stub->symInfo());
switch (config().options().getStripSymbolMode()) {
case GeneralOptions::StripSymbolMode::StripAllSymbols:
case GeneralOptions::StripSymbolMode::StripLocals:
break;
default: {
// a stub symbol should be local
assert(stub->symInfo() != NULL && stub->symInfo()->isLocal());
LDSection& symtab = file_format->getSymTab();
LDSection& strtab = file_format->getStrTab();
// increase the size of .symtab and .strtab if needed
symtab.setSize(symtab.size() + sizeof(llvm::ELF::Elf32_Sym));
symtab.setInfo(symtab.getInfo() + 1);
strtab.setSize(strtab.size() + stub->symInfo()->nameSize() +
1);
}
} // end of switch
isRelaxed = true;
}
break;
}
case llvm::ELF::R_ARM_V4BX:
/* FIXME: bypass R_ARM_V4BX relocation now */
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();
}
}
// reset the size of section that has stubs inserted.
if (isRelaxed) {
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 isRelaxed;
}
/// initTargetStubs
bool ARMGNULDBackend::initTargetStubs() {
if (getStubFactory() != NULL) {
getStubFactory()->addPrototype(new ARMToARMStub(config().isCodeIndep()));
getStubFactory()->addPrototype(new ARMToTHMStub(config().isCodeIndep()));
getStubFactory()->addPrototype(
new THMToTHMStub(config().isCodeIndep(), m_pAttrData->usingThumb2()));
getStubFactory()->addPrototype(
new THMToARMStub(config().isCodeIndep(), m_pAttrData->usingThumb2()));
return true;
}
return false;
}
/// maxFwdBranchOffset
int64_t ARMGNULDBackend::maxFwdBranchOffset() const {
if (m_pAttrData->usingThumb2()) {
return THM2_MAX_FWD_BRANCH_OFFSET;
} else {
return THM_MAX_FWD_BRANCH_OFFSET;
}
}
/// maxBwdBranchOffset
int64_t ARMGNULDBackend::maxBwdBranchOffset() const {
if (m_pAttrData->usingThumb2()) {
return THM2_MAX_BWD_BRANCH_OFFSET;
} else {
return THM_MAX_BWD_BRANCH_OFFSET;
}
}
/// doCreateProgramHdrs - backend can implement this function to create the
/// target-dependent segments
void ARMGNULDBackend::doCreateProgramHdrs(Module& pModule) {
if (m_pEXIDX != NULL && m_pEXIDX->size() != 0x0) {
// make PT_ARM_EXIDX
ELFSegment* exidx_seg =
elfSegmentTable().produce(llvm::ELF::PT_ARM_EXIDX, llvm::ELF::PF_R);
exidx_seg->append(m_pEXIDX);
}
}
/// mayHaveUnsafeFunctionPointerAccess - check if the section may have unsafe
/// function pointer access
bool ARMGNULDBackend::mayHaveUnsafeFunctionPointerAccess(
const LDSection& pSection) const {
llvm::StringRef name(pSection.name());
return !name.startswith(".ARM.exidx") && !name.startswith(".ARM.extab") &&
GNULDBackend::mayHaveUnsafeFunctionPointerAccess(pSection);
}
//===----------------------------------------------------------------------===//
/// createARMLDBackend - the help funtion to create corresponding ARMLDBackend
///
TargetLDBackend* createARMLDBackend(const LinkerConfig& pConfig) {
if (pConfig.targets().triple().isOSDarwin()) {
assert(0 && "MachO linker is not supported yet");
/**
return new ARMMachOLDBackend(createARMMachOArchiveReader,
createARMMachOObjectReader,
createARMMachOObjectWriter);
**/
}
if (pConfig.targets().triple().isOSWindows()) {
assert(0 && "COFF linker is not supported yet");
/**
return new ARMCOFFLDBackend(createARMCOFFArchiveReader,
createARMCOFFObjectReader,
createARMCOFFObjectWriter);
**/
}
return new ARMGNULDBackend(pConfig,
new ARMGNUInfo(pConfig.targets().triple()));
}
} // namespace mcld
//===----------------------------------------------------------------------===//
// Force static initialization.
//===----------------------------------------------------------------------===//
extern "C" void MCLDInitializeARMLDBackend() {
// Register the linker backend
mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheARMTarget,
mcld::createARMLDBackend);
mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheThumbTarget,
mcld::createARMLDBackend);
}