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.
1048 lines
36 KiB
1048 lines
36 KiB
//===- 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);
|
|
}
|