blob: 844c8ba9d12e6d3d6c4b3c46fb46d446558027b0 [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 "ARMELFDynamic.h"
#include "ARMLDBackend.h"
#include "ARMRelocator.h"
#include "ARMToARMStub.h"
#include "ARMToTHMStub.h"
#include "THMToTHMStub.h"
#include "THMToARMStub.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/FragmentLinker.h>
#include <mcld/Support/MemoryRegion.h>
#include <mcld/Support/MemoryArea.h>
#include <mcld/Support/MsgHandling.h>
#include <mcld/Support/TargetRegistry.h>
#include <mcld/Fragment/Stub.h>
#include <mcld/LD/BranchIslandFactory.h>
#include <mcld/LD/StubFactory.h>
#include <mcld/Object/ObjectBuilder.h>
#include <mcld/Fragment/NullFragment.h>
#include <mcld/LD/LDContext.h>
#include <mcld/Target/GNUInfo.h>
using namespace mcld;
//===----------------------------------------------------------------------===//
// 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_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;
}
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);
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(FragmentLinker& pLinker)
{
// Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the
// same name in input
m_pGOTSymbol = pLinker.defineSymbol<FragmentLinker::AsRefered, FragmentLinker::Resolve>(
"_GLOBAL_OFFSET_TABLE_",
false,
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Null(), // FragRef
ResolveInfo::Hidden);
FragmentRef* exidx_start = NULL;
FragmentRef* exidx_end = NULL;
ResolveInfo::Desc desc = ResolveInfo::Undefined;
if (NULL != m_pEXIDX && 0x0 != m_pEXIDX->size()) {
exidx_start = FragmentRef::Create(m_pEXIDX->getSectionData()->front(), 0x0);
exidx_end = FragmentRef::Create(m_pEXIDX->getSectionData()->back(), 0x0);
desc = ResolveInfo::Define;
}
else {
exidx_start = FragmentRef::Null();
exidx_end = FragmentRef::Null();
}
m_pEXIDXStart =
pLinker.defineSymbol<FragmentLinker::Force,
FragmentLinker::Resolve>("__exidx_start",
false,
ResolveInfo::NoType,
desc, // ResolveInfo::Desc
ResolveInfo::Global,
0x0, // size
0x0, // value
exidx_start, // FragRef
ResolveInfo::Hidden);
m_pEXIDXEnd =
pLinker.defineSymbol<FragmentLinker::Force,
FragmentLinker::Resolve>("__exidx_end",
false,
ResolveInfo::NoType,
desc, //ResolveInfo::Desc
ResolveInfo::Global,
0x0, // size
0x0, // value
exidx_end, // FragRef
ResolveInfo::Hidden);
}
bool ARMGNULDBackend::initRelocator(const FragmentLinker& pLinker)
{
if (NULL == m_pRelocator) {
m_pRelocator = new ARMRelocator(*this);
m_pRelocator->setFragmentLinker(pLinker);
}
return true;
}
Relocator* ARMGNULDBackend::getRelocator()
{
assert(NULL != m_pRelocator);
return m_pRelocator;
}
void ARMGNULDBackend::doPreLayout(FragmentLinker& pLinker)
{
// 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() ||
NULL != m_pGOTSymbol) {
m_pGOT->finalizeSectionSize();
defineGOTSymbol(pLinker);
}
// set .plt size
if (m_pPLT->hasPLT1())
m_pPLT->finalizeSectionSize();
ELFFileFormat* file_format = getOutputFormat();
// set .rel.dyn size
if (!m_pRelDyn->empty())
file_format->getRelDyn().setSize(
m_pRelDyn->numOfRelocs() * getRelEntrySize());
// set .rel.plt size
if (!m_pRelPLT->empty())
file_format->getRelPlt().setSize(
m_pRelPLT->numOfRelocs() * getRelEntrySize());
}
}
void ARMGNULDBackend::doPostLayout(Module& pModule,
FragmentLinker& pLinker)
{
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(NULL != m_pPLT);
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(NULL != m_pGOT);
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()
{
if (NULL == m_pDynamic)
m_pDynamic = new ARMELFDynamic(*this, config());
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( NULL != m_pDynamic);
return *m_pDynamic;
}
void ARMGNULDBackend::defineGOTSymbol(FragmentLinker& pLinker)
{
// define symbol _GLOBAL_OFFSET_TABLE_ when .got create
if (m_pGOTSymbol != NULL) {
pLinker.defineSymbol<FragmentLinker::Force, FragmentLinker::Unresolve>(
"_GLOBAL_OFFSET_TABLE_",
false,
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Create(*(m_pGOT->begin()), 0x0),
ResolveInfo::Hidden);
}
else {
m_pGOTSymbol = pLinker.defineSymbol<FragmentLinker::Force, FragmentLinker::Resolve>(
"_GLOBAL_OFFSET_TABLE_",
false,
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Create(*(m_pGOT->begin()), 0x0),
ResolveInfo::Hidden);
}
}
void ARMGNULDBackend::addCopyReloc(ResolveInfo& pSym)
{
Relocation& rel_entry = *m_pRelDyn->consumeEntry();
rel_entry.setType(llvm::ELF::R_ARM_COPY);
assert(pSym.outSymbol()->hasFragRef());
rel_entry.targetRef().assign(*pSym.outSymbol()->fragRef());
rel_entry.setSymInfo(&pSym);
}
/// defineSymbolForCopyReloc
/// For a symbol needing copy relocation, define a copy symbol in the BSS
/// section and all other reference to this symbol should refer to this
/// copy.
/// This is executed at scan relocation stage.
LDSymbol&
ARMGNULDBackend::defineSymbolforCopyReloc(FragmentLinker& pLinker,
const ResolveInfo& pSym)
{
// get or create corresponding BSS LDSection
LDSection* bss_sect_hdr = NULL;
ELFFileFormat* file_format = getOutputFormat();
if (ResolveInfo::ThreadLocal == pSym.type())
bss_sect_hdr = &file_format->getTBSS();
else
bss_sect_hdr = &file_format->getBSS();
// get or create corresponding BSS SectionData
SectionData* bss_data = NULL;
if (bss_sect_hdr->hasSectionData())
bss_data = bss_sect_hdr->getSectionData();
else
bss_data = IRBuilder::CreateSectionData(*bss_sect_hdr);
// Determine the alignment by the symbol value
// FIXME: here we use the largest alignment
uint32_t addralign = config().targets().bitclass() / 8;
// allocate space in BSS for the copy symbol
Fragment* frag = new FillFragment(0x0, 1, pSym.size());
uint64_t size = ObjectBuilder::AppendFragment(*frag,
*bss_data,
addralign);
bss_sect_hdr->setSize(bss_sect_hdr->size() + size);
// change symbol binding to Global if it's a weak symbol
ResolveInfo::Binding binding = (ResolveInfo::Binding)pSym.binding();
if (binding == ResolveInfo::Weak)
binding = ResolveInfo::Global;
// Define the copy symbol in the bss section and resolve it
LDSymbol* cpy_sym = pLinker.defineSymbol<FragmentLinker::Force, FragmentLinker::Resolve>(
pSym.name(),
false,
(ResolveInfo::Type)pSym.type(),
ResolveInfo::Define,
binding,
pSym.size(), // size
0x0, // value
FragmentRef::Create(*frag, 0x0),
(ResolveInfo::Visibility)pSym.other());
return *cpy_sym;
}
/// checkValidReloc - When we attempt to generate a dynamic relocation for
/// ouput file, check if the relocation is supported by dynamic linker.
void ARMGNULDBackend::checkValidReloc(Relocation& pReloc,
const FragmentLinker& pLinker) const
{
// If not PIC object, no relocation type is invalid
if (!pLinker.isOutputPIC())
return;
switch(pReloc.type()) {
case llvm::ELF::R_ARM_RELATIVE:
case llvm::ELF::R_ARM_COPY:
case llvm::ELF::R_ARM_GLOB_DAT:
case llvm::ELF::R_ARM_JUMP_SLOT:
case llvm::ELF::R_ARM_ABS32:
case llvm::ELF::R_ARM_ABS32_NOI:
case llvm::ELF::R_ARM_PC24:
case llvm::ELF::R_ARM_TLS_DTPMOD32:
case llvm::ELF::R_ARM_TLS_DTPOFF32:
case llvm::ELF::R_ARM_TLS_TPOFF32:
break;
default:
error(diag::non_pic_relocation) << (int)pReloc.type()
<< pReloc.symInfo()->name();
break;
}
}
void ARMGNULDBackend::scanLocalReloc(Relocation& pReloc,
FragmentLinker& pLinker)
{
// rsym - The relocation target symbol
ResolveInfo* rsym = pReloc.symInfo();
switch(pReloc.type()){
// Set R_ARM_TARGET1 to R_ARM_ABS32
// Ref: GNU gold 1.11 arm.cc, line 9892
// FIXME: R_ARM_TARGET1 should be set by option --target1-rel
// or --target1-rel
case llvm::ELF::R_ARM_TARGET1:
pReloc.setType(llvm::ELF::R_ARM_ABS32);
case llvm::ELF::R_ARM_ABS32:
case llvm::ELF::R_ARM_ABS32_NOI: {
// If buiding PIC object (shared library or PIC executable),
// a dynamic relocations with RELATIVE type to this location is needed.
// Reserve an entry in .rel.dyn
if (pLinker.isOutputPIC()) {
m_pRelDyn->reserveEntry();
// set Rel bit
rsym->setReserved(rsym->reserved() | ReserveRel);
}
return;
}
case llvm::ELF::R_ARM_ABS16:
case llvm::ELF::R_ARM_ABS12:
case llvm::ELF::R_ARM_THM_ABS5:
case llvm::ELF::R_ARM_ABS8:
case llvm::ELF::R_ARM_BASE_ABS:
case llvm::ELF::R_ARM_MOVW_ABS_NC:
case llvm::ELF::R_ARM_MOVT_ABS:
case llvm::ELF::R_ARM_THM_MOVW_ABS_NC:
case llvm::ELF::R_ARM_THM_MOVT_ABS: {
// PIC code should not contain these kinds of relocation
if (pLinker.isOutputPIC()) {
error(diag::non_pic_relocation) << (int)pReloc.type()
<< pReloc.symInfo()->name();
}
return;
}
case llvm::ELF::R_ARM_GOTOFF32:
case llvm::ELF::R_ARM_GOTOFF12: {
// FIXME: A GOT section is needed
return;
}
// Set R_ARM_TARGET2 to R_ARM_GOT_PREL
// Ref: GNU gold 1.11 arm.cc, line 9892
// FIXME: R_ARM_TARGET2 should be set by option --target2
case llvm::ELF::R_ARM_TARGET2:
pReloc.setType(llvm::ELF::R_ARM_GOT_PREL);
case llvm::ELF::R_ARM_GOT_BREL:
case llvm::ELF::R_ARM_GOT_PREL: {
// A GOT entry is needed for these relocation type.
// return if we already create GOT for this symbol
if (rsym->reserved() & (ReserveGOT | GOTRel))
return;
m_pGOT->reserveGOT();
// If building PIC object, a dynamic relocation with
// type RELATIVE is needed to relocate this GOT entry.
// Reserve an entry in .rel.dyn
if (pLinker.isOutputPIC()) {
// create .rel.dyn section if not exist
m_pRelDyn->reserveEntry();
// set GOTRel bit
rsym->setReserved(rsym->reserved() | 0x4u);
return;
}
// set GOT bit
rsym->setReserved(rsym->reserved() | 0x2u);
return;
}
case llvm::ELF::R_ARM_BASE_PREL: {
// FIXME: Currently we only support R_ARM_BASE_PREL against
// symbol _GLOBAL_OFFSET_TABLE_
if (rsym != m_pGOTSymbol->resolveInfo())
fatal(diag::base_relocation) << (int)pReloc.type() << rsym->name()
<< "mclinker@googlegroups.com";
return;
}
case llvm::ELF::R_ARM_COPY:
case llvm::ELF::R_ARM_GLOB_DAT:
case llvm::ELF::R_ARM_JUMP_SLOT:
case llvm::ELF::R_ARM_RELATIVE: {
// These are relocation type for dynamic linker, shold not
// appear in object file.
fatal(diag::dynamic_relocation) << (int)pReloc.type();
break;
}
default: {
break;
}
} // end switch
}
void ARMGNULDBackend::scanGlobalReloc(Relocation& pReloc,
FragmentLinker& pLinker)
{
// rsym - The relocation target symbol
ResolveInfo* rsym = pReloc.symInfo();
switch(pReloc.type()) {
// Set R_ARM_TARGET1 to R_ARM_ABS32
// Ref: GNU gold 1.11 arm.cc, line 9892
// FIXME: R_ARM_TARGET1 should be set by option --target1-rel
// or --target1-rel
case llvm::ELF::R_ARM_TARGET1:
pReloc.setType(llvm::ELF::R_ARM_ABS32);
case llvm::ELF::R_ARM_ABS32:
case llvm::ELF::R_ARM_ABS16:
case llvm::ELF::R_ARM_ABS12:
case llvm::ELF::R_ARM_THM_ABS5:
case llvm::ELF::R_ARM_ABS8:
case llvm::ELF::R_ARM_BASE_ABS:
case llvm::ELF::R_ARM_MOVW_ABS_NC:
case llvm::ELF::R_ARM_MOVT_ABS:
case llvm::ELF::R_ARM_THM_MOVW_ABS_NC:
case llvm::ELF::R_ARM_THM_MOVT_ABS:
case llvm::ELF::R_ARM_ABS32_NOI: {
// Absolute relocation type, symbol may needs PLT entry or
// dynamic relocation entry
if (symbolNeedsPLT(pLinker, *rsym)) {
// create plt for this symbol if it does not have one
if (!(rsym->reserved() & ReservePLT)){
// Symbol needs PLT entry, we need to reserve a PLT entry
// and the corresponding GOT and dynamic relocation entry
// in .got and .rel.plt. (GOT entry will be reserved simultaneously
// when calling ARMPLT->reserveEntry())
m_pPLT->reserveEntry();
m_pRelPLT->reserveEntry();
// set PLT bit
rsym->setReserved(rsym->reserved() | ReservePLT);
}
}
if (symbolNeedsDynRel(
pLinker, *rsym, (rsym->reserved() & ReservePLT), true)) {
// symbol needs dynamic relocation entry, reserve an entry in .rel.dyn
m_pRelDyn->reserveEntry();
if (symbolNeedsCopyReloc(pLinker, pReloc, *rsym)) {
LDSymbol& cpy_sym = defineSymbolforCopyReloc(pLinker, *rsym);
addCopyReloc(*cpy_sym.resolveInfo());
}
else {
checkValidReloc(pReloc, pLinker);
// set Rel bit
rsym->setReserved(rsym->reserved() | ReserveRel);
}
}
return;
}
case llvm::ELF::R_ARM_GOTOFF32:
case llvm::ELF::R_ARM_GOTOFF12: {
// FIXME: A GOT section is needed
return;
}
case llvm::ELF::R_ARM_BASE_PREL:
case llvm::ELF::R_ARM_THM_MOVW_BREL_NC:
case llvm::ELF::R_ARM_THM_MOVW_BREL:
case llvm::ELF::R_ARM_THM_MOVT_BREL:
// FIXME: Currently we only support these relocations against
// symbol _GLOBAL_OFFSET_TABLE_
if (rsym != m_pGOTSymbol->resolveInfo()) {
fatal(diag::base_relocation) << (int)pReloc.type() << rsym->name()
<< "mclinker@googlegroups.com";
}
case llvm::ELF::R_ARM_REL32:
case llvm::ELF::R_ARM_LDR_PC_G0:
case llvm::ELF::R_ARM_SBREL32:
case llvm::ELF::R_ARM_THM_PC8:
case llvm::ELF::R_ARM_MOVW_PREL_NC:
case llvm::ELF::R_ARM_MOVT_PREL:
case llvm::ELF::R_ARM_THM_MOVW_PREL_NC:
case llvm::ELF::R_ARM_THM_MOVT_PREL:
case llvm::ELF::R_ARM_THM_ALU_PREL_11_0:
case llvm::ELF::R_ARM_THM_PC12:
case llvm::ELF::R_ARM_REL32_NOI:
case llvm::ELF::R_ARM_ALU_PC_G0_NC:
case llvm::ELF::R_ARM_ALU_PC_G0:
case llvm::ELF::R_ARM_ALU_PC_G1_NC:
case llvm::ELF::R_ARM_ALU_PC_G1:
case llvm::ELF::R_ARM_ALU_PC_G2:
case llvm::ELF::R_ARM_LDR_PC_G1:
case llvm::ELF::R_ARM_LDR_PC_G2:
case llvm::ELF::R_ARM_LDRS_PC_G0:
case llvm::ELF::R_ARM_LDRS_PC_G1:
case llvm::ELF::R_ARM_LDRS_PC_G2:
case llvm::ELF::R_ARM_LDC_PC_G0:
case llvm::ELF::R_ARM_LDC_PC_G1:
case llvm::ELF::R_ARM_LDC_PC_G2:
case llvm::ELF::R_ARM_ALU_SB_G0_NC:
case llvm::ELF::R_ARM_ALU_SB_G0:
case llvm::ELF::R_ARM_ALU_SB_G1_NC:
case llvm::ELF::R_ARM_ALU_SB_G1:
case llvm::ELF::R_ARM_ALU_SB_G2:
case llvm::ELF::R_ARM_LDR_SB_G0:
case llvm::ELF::R_ARM_LDR_SB_G1:
case llvm::ELF::R_ARM_LDR_SB_G2:
case llvm::ELF::R_ARM_LDRS_SB_G0:
case llvm::ELF::R_ARM_LDRS_SB_G1:
case llvm::ELF::R_ARM_LDRS_SB_G2:
case llvm::ELF::R_ARM_LDC_SB_G0:
case llvm::ELF::R_ARM_LDC_SB_G1:
case llvm::ELF::R_ARM_LDC_SB_G2:
case llvm::ELF::R_ARM_MOVW_BREL_NC:
case llvm::ELF::R_ARM_MOVT_BREL:
case llvm::ELF::R_ARM_MOVW_BREL: {
// Relative addressing relocation, may needs dynamic relocation
if (symbolNeedsDynRel(
pLinker, *rsym, (rsym->reserved() & ReservePLT), false)) {
// symbol needs dynamic relocation entry, reserve an entry in .rel.dyn
m_pRelDyn->reserveEntry();
if (symbolNeedsCopyReloc(pLinker, pReloc, *rsym)) {
LDSymbol& cpy_sym = defineSymbolforCopyReloc(pLinker, *rsym);
addCopyReloc(*cpy_sym.resolveInfo());
}
else {
checkValidReloc(pReloc, pLinker);
// set Rel bit
rsym->setReserved(rsym->reserved() | ReserveRel);
}
}
return;
}
case llvm::ELF::R_ARM_THM_CALL:
case llvm::ELF::R_ARM_PLT32:
case llvm::ELF::R_ARM_CALL:
case llvm::ELF::R_ARM_JUMP24:
case llvm::ELF::R_ARM_THM_JUMP24:
case llvm::ELF::R_ARM_SBREL31:
case llvm::ELF::R_ARM_PREL31:
case llvm::ELF::R_ARM_THM_JUMP19:
case llvm::ELF::R_ARM_THM_JUMP6:
case llvm::ELF::R_ARM_THM_JUMP11:
case llvm::ELF::R_ARM_THM_JUMP8: {
// These are branch relocation (except PREL31)
// A PLT entry is needed when building shared library
// return if we already create plt for this symbol
if (rsym->reserved() & ReservePLT)
return;
// if the symbol's value can be decided at link time, then no need plt
if (symbolFinalValueIsKnown(pLinker, *rsym))
return;
// if symbol is defined in the ouput file and it's not
// preemptible, no need plt
if (rsym->isDefine() && !rsym->isDyn() &&
!isSymbolPreemptible(*rsym)) {
return;
}
// Symbol needs PLT entry, we need to reserve a PLT entry
// and the corresponding GOT and dynamic relocation entry
// in .got and .rel.plt. (GOT entry will be reserved simultaneously
// when calling ARMPLT->reserveEntry())
m_pPLT->reserveEntry();
m_pRelPLT->reserveEntry();
// set PLT bit
rsym->setReserved(rsym->reserved() | ReservePLT);
return;
}
// Set R_ARM_TARGET2 to R_ARM_GOT_PREL
// Ref: GNU gold 1.11 arm.cc, line 9892
// FIXME: R_ARM_TARGET2 should be set by option --target2
case llvm::ELF::R_ARM_TARGET2:
pReloc.setType(llvm::ELF::R_ARM_GOT_PREL);
case llvm::ELF::R_ARM_GOT_BREL:
case llvm::ELF::R_ARM_GOT_ABS:
case llvm::ELF::R_ARM_GOT_PREL: {
// Symbol needs GOT entry, reserve entry in .got
// return if we already create GOT for this symbol
if (rsym->reserved() & (ReserveGOT | GOTRel))
return;
m_pGOT->reserveGOT();
// if the symbol cannot be fully resolved at link time, then we need a
// dynamic relocation
if (!symbolFinalValueIsKnown(pLinker, *rsym)) {
m_pRelDyn->reserveEntry();
// set GOTRel bit
rsym->setReserved(rsym->reserved() | GOTRel);
return;
}
// set GOT bit
rsym->setReserved(rsym->reserved() | ReserveGOT);
return;
}
case llvm::ELF::R_ARM_COPY:
case llvm::ELF::R_ARM_GLOB_DAT:
case llvm::ELF::R_ARM_JUMP_SLOT:
case llvm::ELF::R_ARM_RELATIVE: {
// These are relocation type for dynamic linker, shold not
// appear in object file.
fatal(diag::dynamic_relocation) << (int)pReloc.type();
break;
}
default: {
break;
}
} // end switch
}
void ARMGNULDBackend::scanRelocation(Relocation& pReloc,
FragmentLinker& pLinker,
Module& pModule,
const LDSection& pSection)
{
// rsym - The relocation target symbol
ResolveInfo* rsym = pReloc.symInfo();
assert(NULL != rsym && "ResolveInfo of relocation not set while scanRelocation");
pReloc.updateAddend();
if (0 == (pSection.flag() & llvm::ELF::SHF_ALLOC))
return;
// Scan relocation type to determine if an GOT/PLT/Dynamic Relocation
// entries should be created.
// FIXME: Below judgements concern nothing about TLS related relocation
// rsym is local
if (rsym->isLocal())
scanLocalReloc(pReloc, pLinker);
// rsym is external
else
scanGlobalReloc(pReloc, pLinker);
// check if we shoule issue undefined reference for the relocation target
// symbol
if (rsym->isUndef() && !rsym->isDyn() && !rsym->isWeak() && !rsym->isNull())
fatal(diag::undefined_reference) << rsym->name();
if ((rsym->reserved() & ReserveRel) != 0x0) {
// set hasTextRelSection if needed
checkAndSetHasTextRel(pSection);
}
}
uint64_t ARMGNULDBackend::emitSectionData(const LDSection& pSection,
MemoryRegion& pRegion) const
{
assert(pRegion.size() && "Size of MemoryRegion is zero!");
const ELFFileFormat* file_format = getOutputFormat();
if (&pSection == m_pAttributes ||
&pSection == m_pEXIDX ||
&pSection == m_pEXTAB) {
// 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.start();
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 (0 == fill_frag.getValueSize()) {
// 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 uint8_t* start = region_frag.getRegion().start();
memcpy(out_offset, start, size);
break;
}
case Fragment::Alignment: {
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();
} // end if
if (&pSection == &(file_format->getPLT())) {
assert(NULL != m_pPLT && "emitSectionData failed, m_pPLT is NULL!");
uint64_t result = m_pPLT->emit(pRegion);
return result;
}
if (&pSection == &(file_format->getGOT())) {
assert(NULL != m_pGOT && "emitSectionData failed, m_pGOT is NULL!");
uint64_t result = m_pGOT->emit(pRegion);
return result;
}
fatal(diag::unrecognized_output_sectoin)
<< pSection.name()
<< "mclinker@googlegroups.com";
return 0x0;
}
/// finalizeSymbol - finalize the symbol value
bool ARMGNULDBackend::finalizeTargetSymbols(FragmentLinker& pLinker)
{
if (NULL != m_pEXIDXStart) {
if (NULL != m_pEXIDX && 0x0 != m_pEXIDX->size())
m_pEXIDXStart->setValue(m_pEXIDX->addr());
}
if (NULL != m_pEXIDXEnd) {
if (NULL != m_pEXIDX && 0x0 != m_pEXIDX->size())
m_pEXIDXEnd->setValue(m_pEXIDX->addr() + m_pEXIDX->size());
}
return true;
}
bool ARMGNULDBackend::mergeSection(Module& pModule, LDSection& pSection)
{
switch (pSection.type()) {
case llvm::ELF::SHT_ARM_ATTRIBUTES: {
// FIXME: (Luba)
// Handle ARM attributes in the right way.
// In current milestone, FragmentLinker goes through the shortcut.
// It reads input's ARM attributes and copies the first ARM attributes
// into the output file. The correct way is merge these sections, not
// just copy.
if (0 != m_pAttributes->size())
return true;
// First time we meet a ARM attributes section.
SectionData* sd = IRBuilder::CreateSectionData(*m_pAttributes);
ObjectBuilder::MoveSectionData(*pSection.getSectionData(), *sd);
return true;
}
default: {
ObjectBuilder builder(config(), pModule);
return builder.MergeSection(pSection);
}
} // end of switch
return true;
}
bool ARMGNULDBackend::readSection(Input& pInput, SectionData& pSD)
{
Fragment* frag = NULL;
uint32_t offset = pInput.fileOffset() + pSD.getSection().offset();
uint32_t size = pSD.getSection().size();
MemoryRegion* region = pInput.memArea()->request(offset, size);
if (NULL == region) {
// 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(NULL != m_pGOT && "GOT section not exist");
return *m_pGOT;
}
const ARMGOT& ARMGNULDBackend::getGOT() const
{
assert(NULL != m_pGOT && "GOT section not exist");
return *m_pGOT;
}
ARMPLT& ARMGNULDBackend::getPLT()
{
assert(NULL != m_pPLT && "PLT section not exist");
return *m_pPLT;
}
const ARMPLT& ARMGNULDBackend::getPLT() const
{
assert(NULL != m_pPLT && "PLT section not exist");
return *m_pPLT;
}
OutputRelocSection& ARMGNULDBackend::getRelDyn()
{
assert(NULL != m_pRelDyn && ".rel.dyn section not exist");
return *m_pRelDyn;
}
const OutputRelocSection& ARMGNULDBackend::getRelDyn() const
{
assert(NULL != m_pRelDyn && ".rel.dyn section not exist");
return *m_pRelDyn;
}
OutputRelocSection& ARMGNULDBackend::getRelPLT()
{
assert(NULL != m_pRelPLT && ".rel.plt section not exist");
return *m_pRelPLT;
}
const OutputRelocSection& ARMGNULDBackend::getRelPLT() const
{
assert(NULL != m_pRelPLT && ".rel.plt section not exist");
return *m_pRelPLT;
}
unsigned int
ARMGNULDBackend::getTargetSectionOrder(const LDSection& pSectHdr) const
{
const ELFFileFormat* file_format = getOutputFormat();
if (&pSectHdr == &file_format->getGOT()) {
if (config().options().hasNow())
return SHO_RELRO_LAST;
return SHO_DATA;
}
if (&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;
}
/// doRelax
bool ARMGNULDBackend::doRelax(Module& pModule, FragmentLinker& pLinker, bool& pFinished)
{
assert(NULL != getStubFactory() && NULL != getBRIslandFactory());
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_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:
case llvm::ELF::R_ARM_V4BX: {
// 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()->isGlobal() &&
(relocation->symInfo()->reserved() & 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
pLinker,
*getBRIslandFactory());
if (NULL != stub) {
assert(NULL != stub->symInfo());
// increase the size of .symtab and .strtab
LDSection& symtab = file_format->getSymTab();
LDSection& strtab = file_format->getStrTab();
if (config().targets().is32Bits())
symtab.setSize(symtab.size() + sizeof(llvm::ELF::Elf32_Sym));
else
symtab.setSize(symtab.size() + sizeof(llvm::ELF::Elf64_Sym));
strtab.setSize(strtab.size() + stub->symInfo()->nameSize() + 1);
isRelaxed = true;
}
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
Fragment* invalid = NULL;
pFinished = true;
for (BranchIslandFactory::iterator island = getBRIslandFactory()->begin(),
island_end = getBRIslandFactory()->end(); island != island_end; ++island) {
if ((*island).end() == file_format->getText().getSectionData()->end())
break;
Fragment* exit = (*island).end();
if (((*island).offset() + (*island).size()) > exit->getOffset()) {
invalid = exit;
pFinished = false;
break;
}
}
// reset the offset of invalid fragments
while (NULL != invalid) {
invalid->setOffset(invalid->getPrevNode()->getOffset() +
invalid->getPrevNode()->size());
invalid = invalid->getNextNode();
}
// reset the size of .text
if (isRelaxed) {
file_format->getText().setSize(
file_format->getText().getSectionData()->back().getOffset() +
file_format->getText().getSectionData()->back().size());
}
return isRelaxed;
}
/// initTargetStubs
bool ARMGNULDBackend::initTargetStubs(FragmentLinker& pLinker)
{
if (NULL != getStubFactory()) {
getStubFactory()->addPrototype(new ARMToARMStub(pLinker.isOutputPIC()));
getStubFactory()->addPrototype(new ARMToTHMStub(pLinker.isOutputPIC()));
getStubFactory()->addPrototype(new THMToTHMStub(pLinker.isOutputPIC()));
getStubFactory()->addPrototype(new THMToARMStub(pLinker.isOutputPIC()));
return true;
}
return false;
}
/// doCreateProgramHdrs - backend can implement this function to create the
/// target-dependent segments
void ARMGNULDBackend::doCreateProgramHdrs(Module& pModule,
const FragmentLinker& pLinker)
{
if (NULL != m_pEXIDX && 0x0 != m_pEXIDX->size()) {
// make PT_ARM_EXIDX
ELFSegment* exidx_seg = elfSegmentTable().produce(llvm::ELF::PT_ARM_EXIDX,
llvm::ELF::PF_R);
exidx_seg->addSection(m_pEXIDX);
}
}
namespace mcld {
//===----------------------------------------------------------------------===//
/// createARMLDBackend - the help funtion to create corresponding ARMLDBackend
///
TargetLDBackend* createARMLDBackend(const llvm::Target& pTarget,
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 of mcld
//===----------------------------------------------------------------------===//
// Force static initialization.
//===----------------------------------------------------------------------===//
extern "C" void MCLDInitializeARMLDBackend() {
// Register the linker backend
mcld::TargetRegistry::RegisterTargetLDBackend(TheARMTarget, createARMLDBackend);
mcld::TargetRegistry::RegisterTargetLDBackend(TheThumbTarget, createARMLDBackend);
}