//===- MipsLDBackend.cpp --------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Mips.h"
#include "MipsGNUInfo.h"
#include "MipsELFDynamic.h"
#include "MipsLA25Stub.h"
#include "MipsLDBackend.h"
#include "MipsRelocator.h"
#include "mcld/IRBuilder.h"
#include "mcld/LinkerConfig.h"
#include "mcld/Module.h"
#include "mcld/Fragment/AlignFragment.h"
#include "mcld/Fragment/FillFragment.h"
#include "mcld/LD/BranchIslandFactory.h"
#include "mcld/LD/LDContext.h"
#include "mcld/LD/StubFactory.h"
#include "mcld/LD/ELFFileFormat.h"
#include "mcld/LD/ELFSegment.h"
#include "mcld/LD/ELFSegmentFactory.h"
#include "mcld/MC/Attribute.h"
#include "mcld/Object/ObjectBuilder.h"
#include "mcld/Support/MemoryRegion.h"
#include "mcld/Support/MemoryArea.h"
#include "mcld/Support/MsgHandling.h"
#include "mcld/Support/TargetRegistry.h"
#include "mcld/Target/OutputRelocSection.h"
#include <llvm/ADT/Triple.h>
#include <llvm/Object/ELFTypes.h>
#include <llvm/Support/Casting.h>
#include <llvm/Support/ELF.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/MipsABIFlags.h>
#include <vector>
namespace mcld {
//===----------------------------------------------------------------------===//
// MipsGNULDBackend
//===----------------------------------------------------------------------===//
MipsGNULDBackend::MipsGNULDBackend(const LinkerConfig& pConfig,
MipsGNUInfo* pInfo)
: GNULDBackend(pConfig, pInfo),
m_pRelocator(NULL),
m_pGOT(NULL),
m_pPLT(NULL),
m_pGOTPLT(NULL),
m_pInfo(*pInfo),
m_pRelPlt(NULL),
m_pRelDyn(NULL),
m_pDynamic(NULL),
m_pAbiFlags(NULL),
m_pGOTSymbol(NULL),
m_pPLTSymbol(NULL),
m_pGpDispSymbol(NULL) {
}
MipsGNULDBackend::~MipsGNULDBackend() {
delete m_pRelocator;
delete m_pPLT;
delete m_pRelPlt;
delete m_pRelDyn;
delete m_pDynamic;
}
bool MipsGNULDBackend::needsLA25Stub(Relocation::Type pType,
const mcld::ResolveInfo* pSym) {
if (config().isCodeIndep())
return false;
if (llvm::ELF::R_MIPS_26 != pType)
return false;
if (pSym->isLocal())
return false;
return true;
}
void MipsGNULDBackend::addNonPICBranchSym(ResolveInfo* rsym) {
m_HasNonPICBranchSyms.insert(rsym);
}
bool MipsGNULDBackend::hasNonPICBranch(const ResolveInfo* rsym) const {
return m_HasNonPICBranchSyms.count(rsym);
}
void MipsGNULDBackend::initTargetSections(Module& pModule,
ObjectBuilder& pBuilder) {
if (LinkerConfig::Object == config().codeGenType())
return;
ELFFileFormat* file_format = getOutputFormat();
// initialize .rel.plt
LDSection& relplt = file_format->getRelPlt();
m_pRelPlt = new OutputRelocSection(pModule, relplt);
// initialize .rel.dyn
LDSection& reldyn = file_format->getRelDyn();
m_pRelDyn = new OutputRelocSection(pModule, reldyn);
// initialize .sdata
m_psdata = pBuilder.CreateSection(
".sdata", LDFileFormat::Target, llvm::ELF::SHT_PROGBITS,
llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE | llvm::ELF::SHF_MIPS_GPREL,
4);
// initialize .MIPS.abiflags
m_pAbiFlags = pBuilder.CreateSection(".MIPS.abiflags", LDFileFormat::Target,
llvm::ELF::SHT_MIPS_ABIFLAGS,
llvm::ELF::SHF_ALLOC, 4);
}
void MipsGNULDBackend::initTargetSymbols(IRBuilder& pBuilder, Module& pModule) {
// Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the
// same name in input
m_pGOTSymbol = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
"_GLOBAL_OFFSET_TABLE_",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Null(), // FragRef
ResolveInfo::Hidden);
// Define the symbol _PROCEDURE_LINKAGE_TABLE_ if there is a symbol with the
// same name in input
m_pPLTSymbol = pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
"_PROCEDURE_LINKAGE_TABLE_",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Null(), // FragRef
ResolveInfo::Hidden);
m_pGpDispSymbol =
pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Resolve>(
"_gp_disp",
ResolveInfo::Section,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
FragmentRef::Null(), // FragRef
ResolveInfo::Default);
pBuilder.AddSymbol<IRBuilder::AsReferred, IRBuilder::Unresolve>(
"_gp",
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
FragmentRef::Null(), // FragRef
ResolveInfo::Default);
}
const Relocator* MipsGNULDBackend::getRelocator() const {
assert(m_pRelocator != NULL);
return m_pRelocator;
}
Relocator* MipsGNULDBackend::getRelocator() {
assert(m_pRelocator != NULL);
return m_pRelocator;
}
void MipsGNULDBackend::doPreLayout(IRBuilder& pBuilder) {
// initialize .dynamic data
if (!config().isCodeStatic() && m_pDynamic == NULL)
m_pDynamic = new MipsELFDynamic(*this, config());
if (m_pAbiInfo.hasValue())
m_pAbiFlags->setSize(m_pAbiInfo->size());
// 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->finalizeScanning(*m_pRelDyn);
m_pGOT->finalizeSectionSize();
defineGOTSymbol(pBuilder);
}
if (m_pGOTPLT->hasGOT1()) {
m_pGOTPLT->finalizeSectionSize();
defineGOTPLTSymbol(pBuilder);
}
if (m_pPLT->hasPLT1())
m_pPLT->finalizeSectionSize();
ELFFileFormat* file_format = getOutputFormat();
// 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());
}
// 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());
}
}
}
void MipsGNULDBackend::doPostLayout(Module& pModule, IRBuilder& pBuilder) {
const ELFFileFormat* format = getOutputFormat();
if (format->hasGOTPLT()) {
assert(m_pGOTPLT != NULL && "doPostLayout failed, m_pGOTPLT is NULL!");
m_pGOTPLT->applyAllGOTPLT(m_pPLT->addr());
}
if (format->hasPLT()) {
assert(m_pPLT != NULL && "doPostLayout failed, m_pPLT is NULL!");
m_pPLT->applyAllPLT(*m_pGOTPLT);
}
m_pInfo.setABIVersion(m_pPLT && m_pPLT->hasPLT1() ? 1 : 0);
}
/// dynamic - the dynamic section of the target machine.
/// Use co-variant return type to return its own dynamic section.
MipsELFDynamic& MipsGNULDBackend::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 MipsELFDynamic& MipsGNULDBackend::dynamic() const {
assert(m_pDynamic != NULL);
return *m_pDynamic;
}
uint64_t MipsGNULDBackend::emitSectionData(const LDSection& pSection,
MemoryRegion& pRegion) const {
assert(pRegion.size() && "Size of MemoryRegion is zero!");
const ELFFileFormat* file_format = getOutputFormat();
if (file_format->hasGOT() && (&pSection == &(file_format->getGOT()))) {
return m_pGOT->emit(pRegion);
}
if (file_format->hasPLT() && (&pSection == &(file_format->getPLT()))) {
return m_pPLT->emit(pRegion);
}
if (file_format->hasGOTPLT() && (&pSection == &(file_format->getGOTPLT()))) {
return m_pGOTPLT->emit(pRegion);
}
if (&pSection == m_pAbiFlags && m_pAbiInfo.hasValue())
return MipsAbiFlags::emit(*m_pAbiInfo, pRegion);
if (&pSection == m_psdata && m_psdata->hasSectionData()) {
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;
}
return pRegion.size();
}
fatal(diag::unrecognized_output_sectoin) << pSection.name()
<< "mclinker@googlegroups.com";
return 0;
}
bool MipsGNULDBackend::hasEntryInStrTab(const LDSymbol& pSym) const {
return ResolveInfo::Section != pSym.type() || m_pGpDispSymbol == &pSym;
}
namespace {
struct DynsymGOTCompare {
const MipsGOT& m_pGOT;
explicit DynsymGOTCompare(const MipsGOT& pGOT) : m_pGOT(pGOT) {}
bool operator()(const LDSymbol* X, const LDSymbol* Y) const {
return m_pGOT.dynSymOrderCompare(X, Y);
}
};
} // anonymous namespace
void MipsGNULDBackend::orderSymbolTable(Module& pModule) {
if (config().options().hasGNUHash()) {
// The MIPS ABI and .gnu.hash require .dynsym to be sorted
// in different ways. The MIPS ABI requires a mapping between
// the GOT and the symbol table. At the same time .gnu.hash
// needs symbols to be grouped by hash code.
llvm::errs() << ".gnu.hash is incompatible with the MIPS ABI\n";
}
Module::SymbolTable& symbols = pModule.getSymbolTable();
std::stable_sort(
symbols.dynamicBegin(), symbols.dynamicEnd(), DynsymGOTCompare(*m_pGOT));
}
} // namespace mcld
namespace llvm {
namespace ELF {
// SHT_MIPS_OPTIONS section's block descriptor.
struct Elf_Options {
unsigned char kind; // Determines interpretation of variable
// part of descriptor. See ODK_xxx enumeration.
unsigned char size; // Byte size of descriptor, including this header.
Elf64_Half section; // Section header index of section affected,
// or 0 for global options.
Elf64_Word info; // Kind-specific information.
};
// Content of ODK_REGINFO block in SHT_MIPS_OPTIONS section on 32 bit ABI.
struct Elf32_RegInfo {
Elf32_Word ri_gprmask; // Mask of general purpose registers used.
Elf32_Word ri_cprmask[4]; // Mask of co-processor registers used.
Elf32_Addr ri_gp_value; // GP register value for this object file.
};
// Content of ODK_REGINFO block in SHT_MIPS_OPTIONS section on 64 bit ABI.
struct Elf64_RegInfo {
Elf32_Word ri_gprmask; // Mask of general purpose registers used.
Elf32_Word ri_pad; // Padding.
Elf32_Word ri_cprmask[4]; // Mask of co-processor registers used.
Elf64_Addr ri_gp_value; // GP register value for this object file.
};
} // namespace ELF
} // namespace llvm
namespace mcld {
static const char* ArchName(uint64_t flagBits) {
switch (flagBits) {
case llvm::ELF::EF_MIPS_ARCH_1:
return "mips1";
case llvm::ELF::EF_MIPS_ARCH_2:
return "mips2";
case llvm::ELF::EF_MIPS_ARCH_3:
return "mips3";
case llvm::ELF::EF_MIPS_ARCH_4:
return "mips4";
case llvm::ELF::EF_MIPS_ARCH_5:
return "mips5";
case llvm::ELF::EF_MIPS_ARCH_32:
return "mips32";
case llvm::ELF::EF_MIPS_ARCH_64:
return "mips64";
case llvm::ELF::EF_MIPS_ARCH_32R2:
return "mips32r2";
case llvm::ELF::EF_MIPS_ARCH_64R2:
return "mips64r2";
case llvm::ELF::EF_MIPS_ARCH_32R6:
return "mips32r6";
case llvm::ELF::EF_MIPS_ARCH_64R6:
return "mips64r6";
default:
return "Unknown Arch";
}
}
void MipsGNULDBackend::mergeFlags(Input& pInput, const char* ELF_hdr) {
bool isTarget64Bit = config().targets().triple().isArch64Bit();
bool isInput64Bit = ELF_hdr[llvm::ELF::EI_CLASS] == llvm::ELF::ELFCLASS64;
if (isTarget64Bit != isInput64Bit) {
fatal(diag::error_Mips_incompatible_class)
<< (isTarget64Bit ? "ELFCLASS64" : "ELFCLASS32")
<< (isInput64Bit ? "ELFCLASS64" : "ELFCLASS32") << pInput.name();
return;
}
m_ElfFlagsMap[&pInput] =
isInput64Bit ?
reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(ELF_hdr)->e_flags :
reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(ELF_hdr)->e_flags;
}
bool MipsGNULDBackend::readSection(Input& pInput, SectionData& pSD) {
if ((pSD.getSection().flag() & llvm::ELF::SHF_MIPS_GPREL) ||
(pSD.getSection().type() == llvm::ELF::SHT_MIPS_ABIFLAGS)) {
uint64_t offset = pInput.fileOffset() + pSD.getSection().offset();
uint64_t size = pSD.getSection().size();
Fragment* frag = IRBuilder::CreateRegion(pInput, offset, size);
ObjectBuilder::AppendFragment(*frag, pSD);
return true;
}
if (pSD.getSection().type() == llvm::ELF::SHT_MIPS_OPTIONS) {
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) {
const llvm::ELF::Elf_Options* optb =
reinterpret_cast<const llvm::ELF::Elf_Options*>(region.begin());
const llvm::ELF::Elf_Options* opte =
reinterpret_cast<const llvm::ELF::Elf_Options*>(region.begin() +
size);
for (const llvm::ELF::Elf_Options* opt = optb; opt < opte;
opt += opt->size) {
switch (opt->kind) {
default:
// Nothing to do.
break;
case llvm::ELF::ODK_REGINFO:
if (config().targets().triple().isArch32Bit()) {
const llvm::ELF::Elf32_RegInfo* reg =
reinterpret_cast<const llvm::ELF::Elf32_RegInfo*>(opt + 1);
m_GP0Map[&pInput] = reg->ri_gp_value;
} else {
const llvm::ELF::Elf64_RegInfo* reg =
reinterpret_cast<const llvm::ELF::Elf64_RegInfo*>(opt + 1);
m_GP0Map[&pInput] = reg->ri_gp_value;
}
break;
}
}
}
return true;
}
return GNULDBackend::readSection(pInput, pSD);
}
MipsGOT& MipsGNULDBackend::getGOT() {
assert(m_pGOT != NULL);
return *m_pGOT;
}
const MipsGOT& MipsGNULDBackend::getGOT() const {
assert(m_pGOT != NULL);
return *m_pGOT;
}
MipsPLT& MipsGNULDBackend::getPLT() {
assert(m_pPLT != NULL);
return *m_pPLT;
}
const MipsPLT& MipsGNULDBackend::getPLT() const {
assert(m_pPLT != NULL);
return *m_pPLT;
}
MipsGOTPLT& MipsGNULDBackend::getGOTPLT() {
assert(m_pGOTPLT != NULL);
return *m_pGOTPLT;
}
const MipsGOTPLT& MipsGNULDBackend::getGOTPLT() const {
assert(m_pGOTPLT != NULL);
return *m_pGOTPLT;
}
OutputRelocSection& MipsGNULDBackend::getRelPLT() {
assert(m_pRelPlt != NULL);
return *m_pRelPlt;
}
const OutputRelocSection& MipsGNULDBackend::getRelPLT() const {
assert(m_pRelPlt != NULL);
return *m_pRelPlt;
}
OutputRelocSection& MipsGNULDBackend::getRelDyn() {
assert(m_pRelDyn != NULL);
return *m_pRelDyn;
}
const OutputRelocSection& MipsGNULDBackend::getRelDyn() const {
assert(m_pRelDyn != NULL);
return *m_pRelDyn;
}
unsigned int MipsGNULDBackend::getTargetSectionOrder(
const LDSection& pSectHdr) const {
const ELFFileFormat* file_format = getOutputFormat();
if (file_format->hasGOT() && (&pSectHdr == &file_format->getGOT()))
return SHO_DATA;
if (file_format->hasGOTPLT() && (&pSectHdr == &file_format->getGOTPLT()))
return SHO_DATA;
if (file_format->hasPLT() && (&pSectHdr == &file_format->getPLT()))
return SHO_PLT;
if (&pSectHdr == m_psdata)
return SHO_SMALL_DATA;
if (&pSectHdr == m_pAbiFlags)
return SHO_RO_NOTE;
return SHO_UNDEFINED;
}
/// finalizeSymbol - finalize the symbol value
bool MipsGNULDBackend::finalizeTargetSymbols() {
if (m_pGpDispSymbol != NULL)
m_pGpDispSymbol->setValue(m_pGOT->getGPDispAddress());
return true;
}
/// allocateCommonSymbols - allocate common symbols in the corresponding
/// sections. This is called at pre-layout stage.
/// FIXME: Mips needs to allocate small common symbol
bool MipsGNULDBackend::allocateCommonSymbols(Module& pModule) {
SymbolCategory& symbol_list = pModule.getSymbolTable();
if (symbol_list.emptyCommons() && symbol_list.emptyFiles() &&
symbol_list.emptyLocals() && symbol_list.emptyLocalDyns())
return true;
SymbolCategory::iterator com_sym, com_end;
// FIXME: If the order of common symbols is defined, then sort common symbols
// std::sort(com_sym, com_end, some kind of order);
// get corresponding BSS LDSection
ELFFileFormat* file_format = getOutputFormat();
LDSection& bss_sect = file_format->getBSS();
LDSection& tbss_sect = file_format->getTBSS();
// get or create corresponding BSS SectionData
SectionData* bss_sect_data = NULL;
if (bss_sect.hasSectionData())
bss_sect_data = bss_sect.getSectionData();
else
bss_sect_data = IRBuilder::CreateSectionData(bss_sect);
SectionData* tbss_sect_data = NULL;
if (tbss_sect.hasSectionData())
tbss_sect_data = tbss_sect.getSectionData();
else
tbss_sect_data = IRBuilder::CreateSectionData(tbss_sect);
// remember original BSS size
uint64_t bss_offset = bss_sect.size();
uint64_t tbss_offset = tbss_sect.size();
// allocate all local common symbols
com_end = symbol_list.localEnd();
for (com_sym = symbol_list.localBegin(); com_sym != com_end; ++com_sym) {
if (ResolveInfo::Common == (*com_sym)->desc()) {
// We have to reset the description of the symbol here. When doing
// incremental linking, the output relocatable object may have common
// symbols. Therefore, we can not treat common symbols as normal symbols
// when emitting the regular name pools. We must change the symbols'
// description here.
(*com_sym)->resolveInfo()->setDesc(ResolveInfo::Define);
Fragment* frag = new FillFragment(0x0, 1, (*com_sym)->size());
if (ResolveInfo::ThreadLocal == (*com_sym)->type()) {
// allocate TLS common symbol in tbss section
tbss_offset += ObjectBuilder::AppendFragment(
*frag, *tbss_sect_data, (*com_sym)->value());
ObjectBuilder::UpdateSectionAlign(tbss_sect, (*com_sym)->value());
(*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0));
} else {
// FIXME: how to identify small and large common symbols?
bss_offset += ObjectBuilder::AppendFragment(
*frag, *bss_sect_data, (*com_sym)->value());
ObjectBuilder::UpdateSectionAlign(bss_sect, (*com_sym)->value());
(*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0));
}
}
}
// allocate all global common symbols
com_end = symbol_list.commonEnd();
for (com_sym = symbol_list.commonBegin(); com_sym != com_end; ++com_sym) {
// We have to reset the description of the symbol here. When doing
// incremental linking, the output relocatable object may have common
// symbols. Therefore, we can not treat common symbols as normal symbols
// when emitting the regular name pools. We must change the symbols'
// description here.
(*com_sym)->resolveInfo()->setDesc(ResolveInfo::Define);
Fragment* frag = new FillFragment(0x0, 1, (*com_sym)->size());
if (ResolveInfo::ThreadLocal == (*com_sym)->type()) {
// allocate TLS common symbol in tbss section
tbss_offset += ObjectBuilder::AppendFragment(
*frag, *tbss_sect_data, (*com_sym)->value());
ObjectBuilder::UpdateSectionAlign(tbss_sect, (*com_sym)->value());
(*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0));
} else {
// FIXME: how to identify small and large common symbols?
bss_offset += ObjectBuilder::AppendFragment(
*frag, *bss_sect_data, (*com_sym)->value());
ObjectBuilder::UpdateSectionAlign(bss_sect, (*com_sym)->value());
(*com_sym)->setFragmentRef(FragmentRef::Create(*frag, 0));
}
}
bss_sect.setSize(bss_offset);
tbss_sect.setSize(tbss_offset);
symbol_list.changeCommonsToGlobal();
return true;
}
uint64_t MipsGNULDBackend::getTPOffset(const Input& pInput) const {
return m_TpOffsetMap.lookup(&pInput);
}
uint64_t MipsGNULDBackend::getDTPOffset(const Input& pInput) const {
return m_DtpOffsetMap.lookup(&pInput);
}
uint64_t MipsGNULDBackend::getGP0(const Input& pInput) const {
return m_GP0Map.lookup(&pInput);
}
void MipsGNULDBackend::defineGOTSymbol(IRBuilder& pBuilder) {
// If we do not reserve any GOT entries, we do not need to re-define GOT
// symbol.
if (!m_pGOT->hasGOT1())
return;
// define symbol _GLOBAL_OFFSET_TABLE_
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);
}
}
void MipsGNULDBackend::defineGOTPLTSymbol(IRBuilder& pBuilder) {
// define symbol _PROCEDURE_LINKAGE_TABLE_
if (m_pPLTSymbol != NULL) {
pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Unresolve>(
"_PROCEDURE_LINKAGE_TABLE_",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Create(*(m_pPLT->begin()), 0x0),
ResolveInfo::Hidden);
} else {
m_pPLTSymbol = pBuilder.AddSymbol<IRBuilder::Force, IRBuilder::Resolve>(
"_PROCEDURE_LINKAGE_TABLE_",
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
FragmentRef::Create(*(m_pPLT->begin()), 0x0),
ResolveInfo::Hidden);
}
}
/// doCreateProgramHdrs - backend can implement this function to create the
/// target-dependent segments
void MipsGNULDBackend::doCreateProgramHdrs(Module& pModule) {
if (!m_pAbiFlags || m_pAbiFlags->size() == 0)
return;
// create PT_MIPS_ABIFLAGS segment
ELFSegmentFactory::iterator sit =
elfSegmentTable().find(llvm::ELF::PT_INTERP, 0x0, 0x0);
if (sit == elfSegmentTable().end())
sit = elfSegmentTable().find(llvm::ELF::PT_PHDR, 0x0, 0x0);
if (sit == elfSegmentTable().end())
sit = elfSegmentTable().begin();
else
++sit;
ELFSegment* abiSeg = elfSegmentTable().insert(sit,
llvm::ELF::PT_MIPS_ABIFLAGS,
llvm::ELF::PF_R);
abiSeg->setAlign(8);
abiSeg->append(m_pAbiFlags);
}
bool MipsGNULDBackend::relaxRelocation(IRBuilder& pBuilder, Relocation& pRel) {
uint64_t sym_value = 0x0;
LDSymbol* symbol = pRel.symInfo()->outSymbol();
if (symbol->hasFragRef()) {
uint64_t value = symbol->fragRef()->getOutputOffset();
uint64_t addr = symbol->fragRef()->frag()->getParent()->getSection().addr();
sym_value = addr + value;
}
Stub* stub = getStubFactory()->create(
pRel, sym_value, pBuilder, *getBRIslandFactory());
if (stub == NULL)
return false;
assert(stub->symInfo() != NULL);
// reset the branch target of the reloc to this stub instead
pRel.setSymInfo(stub->symInfo());
// increase the size of .symtab and .strtab
LDSection& symtab = getOutputFormat()->getSymTab();
LDSection& strtab = getOutputFormat()->getStrTab();
symtab.setSize(symtab.size() + sizeof(llvm::ELF::Elf32_Sym));
strtab.setSize(strtab.size() + stub->symInfo()->nameSize() + 1);
return true;
}
bool MipsGNULDBackend::doRelax(Module& pModule,
IRBuilder& pBuilder,
bool& pFinished) {
assert(getStubFactory() != NULL && getBRIslandFactory() != NULL);
bool isRelaxed = false;
for (Module::obj_iterator input = pModule.obj_begin();
input != pModule.obj_end();
++input) {
LDContext* context = (*input)->context();
for (LDContext::sect_iterator rs = context->relocSectBegin();
rs != context->relocSectEnd();
++rs) {
LDSection* sec = *rs;
if (LDFileFormat::Ignore == sec->kind() || !sec->hasRelocData())
continue;
for (RelocData::iterator reloc = sec->getRelocData()->begin();
reloc != sec->getRelocData()->end();
++reloc) {
if (llvm::ELF::R_MIPS_26 != reloc->type())
continue;
if (relaxRelocation(pBuilder, *llvm::cast<Relocation>(reloc)))
isRelaxed = true;
}
}
}
// find the first fragment w/ invalid offset due to stub insertion
std::vector<Fragment*> invalid_frags;
pFinished = true;
for (BranchIslandFactory::iterator ii = getBRIslandFactory()->begin(),
ie = getBRIslandFactory()->end();
ii != ie;
++ii) {
BranchIsland& island = *ii;
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;
}
bool MipsGNULDBackend::initTargetStubs() {
if (getStubFactory() == NULL)
return false;
getStubFactory()->addPrototype(new MipsLA25Stub(*this));
return true;
}
bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf32_Rel& pRel,
Relocation::Type& pType,
uint32_t& pSymIdx,
uint32_t& pOffset) const {
return GNULDBackend::readRelocation(pRel, pType, pSymIdx, pOffset);
}
bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf32_Rela& pRel,
Relocation::Type& pType,
uint32_t& pSymIdx,
uint32_t& pOffset,
int32_t& pAddend) const {
return GNULDBackend::readRelocation(pRel, pType, pSymIdx, pOffset, pAddend);
}
bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf64_Rel& pRel,
Relocation::Type& pType,
uint32_t& pSymIdx,
uint64_t& pOffset) const {
uint64_t r_info = 0x0;
if (llvm::sys::IsLittleEndianHost) {
pOffset = pRel.r_offset;
r_info = pRel.r_info;
} else {
pOffset = mcld::bswap64(pRel.r_offset);
r_info = mcld::bswap64(pRel.r_info);
}
// MIPS 64 little endian (we do not support big endian now)
// has a "special" encoding of r_info relocation
// field. Instead of one 64 bit little endian number, it is a little
// endian 32 bit number followed by a 32 bit big endian number.
pType = mcld::bswap32(r_info >> 32);
pSymIdx = r_info & 0xffffffff;
return true;
}
bool MipsGNULDBackend::readRelocation(const llvm::ELF::Elf64_Rela& pRel,
Relocation::Type& pType,
uint32_t& pSymIdx,
uint64_t& pOffset,
int64_t& pAddend) const {
uint64_t r_info = 0x0;
if (llvm::sys::IsLittleEndianHost) {
pOffset = pRel.r_offset;
r_info = pRel.r_info;
pAddend = pRel.r_addend;
} else {
pOffset = mcld::bswap64(pRel.r_offset);
r_info = mcld::bswap64(pRel.r_info);
pAddend = mcld::bswap64(pRel.r_addend);
}
pType = mcld::bswap32(r_info >> 32);
pSymIdx = r_info & 0xffffffff;
return true;
}
void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf32_Rel& pRel,
Relocation::Type pType,
uint32_t pSymIdx,
uint32_t pOffset) const {
GNULDBackend::emitRelocation(pRel, pType, pSymIdx, pOffset);
}
void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf32_Rela& pRel,
Relocation::Type pType,
uint32_t pSymIdx,
uint32_t pOffset,
int32_t pAddend) const {
GNULDBackend::emitRelocation(pRel, pType, pSymIdx, pOffset, pAddend);
}
void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf64_Rel& pRel,
Relocation::Type pType,
uint32_t pSymIdx,
uint64_t pOffset) const {
uint64_t r_info = mcld::bswap32(pType);
r_info <<= 32;
r_info |= pSymIdx;
pRel.r_info = r_info;
pRel.r_offset = pOffset;
}
void MipsGNULDBackend::emitRelocation(llvm::ELF::Elf64_Rela& pRel,
Relocation::Type pType,
uint32_t pSymIdx,
uint64_t pOffset,
int64_t pAddend) const {
uint64_t r_info = mcld::bswap32(pType);
r_info <<= 32;
r_info |= pSymIdx;
pRel.r_info = r_info;
pRel.r_offset = pOffset;
pRel.r_addend = pAddend;
}
namespace {
struct ISATreeEdge {
unsigned child;
unsigned parent;
};
}
static ISATreeEdge isaTree[] = {
// MIPS32R6 and MIPS64R6 are not compatible with other extensions
// MIPS64 extensions.
{llvm::ELF::EF_MIPS_ARCH_64R2, llvm::ELF::EF_MIPS_ARCH_64},
// MIPS V extensions.
{llvm::ELF::EF_MIPS_ARCH_64, llvm::ELF::EF_MIPS_ARCH_5},
// MIPS IV extensions.
{llvm::ELF::EF_MIPS_ARCH_5, llvm::ELF::EF_MIPS_ARCH_4},
// MIPS III extensions.
{llvm::ELF::EF_MIPS_ARCH_4, llvm::ELF::EF_MIPS_ARCH_3},
// MIPS32 extensions.
{llvm::ELF::EF_MIPS_ARCH_32R2, llvm::ELF::EF_MIPS_ARCH_32},
// MIPS II extensions.
{llvm::ELF::EF_MIPS_ARCH_3, llvm::ELF::EF_MIPS_ARCH_2},
{llvm::ELF::EF_MIPS_ARCH_32, llvm::ELF::EF_MIPS_ARCH_2},
// MIPS I extensions.
{llvm::ELF::EF_MIPS_ARCH_2, llvm::ELF::EF_MIPS_ARCH_1},
};
static bool isIsaMatched(uint32_t base, uint32_t ext) {
if (base == ext)
return true;
if (base == llvm::ELF::EF_MIPS_ARCH_32 &&
isIsaMatched(llvm::ELF::EF_MIPS_ARCH_64, ext))
return true;
if (base == llvm::ELF::EF_MIPS_ARCH_32R2 &&
isIsaMatched(llvm::ELF::EF_MIPS_ARCH_64R2, ext))
return true;
for (const auto &edge : isaTree) {
if (ext == edge.child) {
ext = edge.parent;
if (ext == base)
return true;
}
}
return false;
}
static bool getAbiFlags(const Input& pInput, uint64_t elfFlags, bool& hasFlags,
MipsAbiFlags& pFlags) {
MipsAbiFlags pElfFlags = {};
if (!MipsAbiFlags::fillByElfFlags(pInput, elfFlags, pElfFlags))
return false;
const LDContext* ctx = pInput.context();
for (auto it = ctx->sectBegin(), ie = ctx->sectEnd(); it != ie; ++it)
if ((*it)->type() == llvm::ELF::SHT_MIPS_ABIFLAGS) {
if (!MipsAbiFlags::fillBySection(pInput, **it, pFlags))
return false;
if (!MipsAbiFlags::isCompatible(pInput, pElfFlags, pFlags))
return false;
hasFlags = true;
return true;
}
pFlags = pElfFlags;
return true;
}
static const char* getNanName(uint64_t flags) {
return flags & llvm::ELF::EF_MIPS_NAN2008 ? "2008" : "legacy";
}
static bool mergeElfFlags(const Input& pInput, uint64_t& oldElfFlags,
uint64_t newElfFlags) {
// PIC code is inherently CPIC and may not set CPIC flag explicitly.
// Ensure that this flag will exist in the linked file.
if (newElfFlags & llvm::ELF::EF_MIPS_PIC)
newElfFlags |= llvm::ELF::EF_MIPS_CPIC;
if (newElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE_M16) {
error(diag::error_Mips_m16_unsupported) << pInput.name();
return false;
}
if (!oldElfFlags) {
oldElfFlags = newElfFlags;
return true;
}
uint64_t newPic =
newElfFlags & (llvm::ELF::EF_MIPS_PIC | llvm::ELF::EF_MIPS_CPIC);
uint64_t oldPic =
oldElfFlags & (llvm::ELF::EF_MIPS_PIC | llvm::ELF::EF_MIPS_CPIC);
// Check PIC / CPIC flags compatibility.
if ((newPic != 0) != (oldPic != 0))
warning(diag::warn_Mips_abicalls_linking) << pInput.name();
if (!(newPic & llvm::ELF::EF_MIPS_PIC))
oldElfFlags &= ~llvm::ELF::EF_MIPS_PIC;
if (newPic)
oldElfFlags |= llvm::ELF::EF_MIPS_CPIC;
// Check ISA compatibility.
uint64_t newArch = newElfFlags & llvm::ELF::EF_MIPS_ARCH;
uint64_t oldArch = oldElfFlags & llvm::ELF::EF_MIPS_ARCH;
if (!isIsaMatched(newArch, oldArch)) {
if (!isIsaMatched(oldArch, newArch)) {
error(diag::error_Mips_inconsistent_arch)
<< ArchName(oldArch) << ArchName(newArch) << pInput.name();
return false;
}
oldElfFlags &= ~llvm::ELF::EF_MIPS_ARCH;
oldElfFlags |= newArch;
}
// Check ABI compatibility.
uint32_t newAbi = newElfFlags & llvm::ELF::EF_MIPS_ABI;
uint32_t oldAbi = oldElfFlags & llvm::ELF::EF_MIPS_ABI;
if (newAbi != oldAbi && newAbi && oldAbi) {
error(diag::error_Mips_inconsistent_abi) << pInput.name();
return false;
}
// Check -mnan flags compatibility.
if ((newElfFlags & llvm::ELF::EF_MIPS_NAN2008) !=
(oldElfFlags & llvm::ELF::EF_MIPS_NAN2008)) {
// Linking -mnan=2008 and -mnan=legacy modules
error(diag::error_Mips_inconsistent_mnan)
<< getNanName(oldElfFlags) << getNanName(newElfFlags) << pInput.name();
return false;
}
// Check ASE compatibility.
uint64_t newAse = newElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE;
uint64_t oldAse = oldElfFlags & llvm::ELF::EF_MIPS_ARCH_ASE;
if (newAse != oldAse)
oldElfFlags |= newAse;
// Check FP64 compatibility.
if ((newElfFlags & llvm::ELF::EF_MIPS_FP64) !=
(oldElfFlags & llvm::ELF::EF_MIPS_FP64)) {
// Linking -mnan=2008 and -mnan=legacy modules
error(diag::error_Mips_inconsistent_fp64) << pInput.name();
return false;
}
oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_NOREORDER;
oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_MICROMIPS;
oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_NAN2008;
oldElfFlags |= newElfFlags & llvm::ELF::EF_MIPS_32BITMODE;
return true;
}
void MipsGNULDBackend::saveTPOffset(const Input& pInput) {
const LDContext* ctx = pInput.context();
for (auto it = ctx->sectBegin(), ie = ctx->sectEnd(); it != ie; ++it) {
LDSection* sect = *it;
if (sect->flag() & llvm::ELF::SHF_TLS) {
m_TpOffsetMap[&pInput] = sect->addr() + 0x7000;
m_DtpOffsetMap[&pInput] = sect->addr() + 0x8000;
break;
}
}
}
void MipsGNULDBackend::preMergeSections(Module& pModule) {
uint64_t elfFlags = 0;
bool hasAbiFlags = false;
MipsAbiFlags abiFlags = {};
for (const Input *input : pModule.getObjectList()) {
if (input->type() != Input::Object)
continue;
uint64_t newElfFlags = m_ElfFlagsMap[input];
MipsAbiFlags newAbiFlags = {};
if (!getAbiFlags(*input, newElfFlags, hasAbiFlags, newAbiFlags))
continue;
if (!mergeElfFlags(*input, elfFlags, newElfFlags))
continue;
if (!MipsAbiFlags::merge(*input, abiFlags, newAbiFlags))
continue;
saveTPOffset(*input);
}
m_pInfo.setElfFlags(elfFlags);
if (hasAbiFlags)
m_pAbiInfo = abiFlags;
}
bool MipsGNULDBackend::mergeSection(Module& pModule, const Input& pInput,
LDSection& pSection) {
if (pSection.flag() & llvm::ELF::SHF_MIPS_GPREL) {
SectionData* sd = NULL;
if (!m_psdata->hasSectionData()) {
sd = IRBuilder::CreateSectionData(*m_psdata);
m_psdata->setSectionData(sd);
}
sd = m_psdata->getSectionData();
moveSectionData(*pSection.getSectionData(), *sd);
} else if (pSection.type() == llvm::ELF::SHT_MIPS_ABIFLAGS) {
// Nothing to do because we handle all .MIPS.abiflags sections
// in the preMergeSections method.
} else {
ObjectBuilder builder(pModule);
builder.MergeSection(pInput, pSection);
}
return true;
}
void MipsGNULDBackend::moveSectionData(SectionData& pFrom, SectionData& pTo) {
assert(&pFrom != &pTo && "Cannot move section data to itself!");
uint64_t offset = pTo.getSection().size();
AlignFragment* align = NULL;
if (pFrom.getSection().align() > 1) {
// if the align constraint is larger than 1, append an alignment
unsigned int alignment = pFrom.getSection().align();
align = new AlignFragment(/*alignment*/ alignment,
/*the filled value*/ 0x0,
/*the size of filled value*/ 1u,
/*max bytes to emit*/ alignment - 1);
align->setOffset(offset);
align->setParent(&pTo);
pTo.getFragmentList().push_back(align);
offset += align->size();
}
// move fragments from pFrom to pTO
SectionData::FragmentListType& from_list = pFrom.getFragmentList();
SectionData::FragmentListType& to_list = pTo.getFragmentList();
SectionData::FragmentListType::iterator frag, fragEnd = from_list.end();
for (frag = from_list.begin(); frag != fragEnd; ++frag) {
frag->setParent(&pTo);
frag->setOffset(offset);
offset += frag->size();
}
to_list.splice(to_list.end(), from_list);
// set up pTo's header
pTo.getSection().setSize(offset);
}
//===----------------------------------------------------------------------===//
// Mips32GNULDBackend
//===----------------------------------------------------------------------===//
Mips32GNULDBackend::Mips32GNULDBackend(const LinkerConfig& pConfig,
MipsGNUInfo* pInfo)
: MipsGNULDBackend(pConfig, pInfo) {
}
bool Mips32GNULDBackend::initRelocator() {
if (m_pRelocator == NULL)
m_pRelocator = new Mips32Relocator(*this, config());
return true;
}
void Mips32GNULDBackend::initTargetSections(Module& pModule,
ObjectBuilder& pBuilder) {
MipsGNULDBackend::initTargetSections(pModule, pBuilder);
if (LinkerConfig::Object == config().codeGenType())
return;
ELFFileFormat* fileFormat = getOutputFormat();
// initialize .got
LDSection& got = fileFormat->getGOT();
m_pGOT = new Mips32GOT(got);
// initialize .got.plt
LDSection& gotplt = fileFormat->getGOTPLT();
m_pGOTPLT = new MipsGOTPLT(gotplt);
// initialize .plt
LDSection& plt = fileFormat->getPLT();
m_pPLT = new MipsPLT(plt);
}
size_t Mips32GNULDBackend::getRelEntrySize() {
return 8;
}
size_t Mips32GNULDBackend::getRelaEntrySize() {
return 12;
}
//===----------------------------------------------------------------------===//
// Mips64GNULDBackend
//===----------------------------------------------------------------------===//
Mips64GNULDBackend::Mips64GNULDBackend(const LinkerConfig& pConfig,
MipsGNUInfo* pInfo)
: MipsGNULDBackend(pConfig, pInfo) {
}
bool Mips64GNULDBackend::initRelocator() {
if (m_pRelocator == NULL)
m_pRelocator = new Mips64Relocator(*this, config());
return true;
}
void Mips64GNULDBackend::initTargetSections(Module& pModule,
ObjectBuilder& pBuilder) {
MipsGNULDBackend::initTargetSections(pModule, pBuilder);
if (LinkerConfig::Object == config().codeGenType())
return;
ELFFileFormat* fileFormat = getOutputFormat();
// initialize .got
LDSection& got = fileFormat->getGOT();
m_pGOT = new Mips64GOT(got);
// initialize .got.plt
LDSection& gotplt = fileFormat->getGOTPLT();
m_pGOTPLT = new MipsGOTPLT(gotplt);
// initialize .plt
LDSection& plt = fileFormat->getPLT();
m_pPLT = new MipsPLT(plt);
}
size_t Mips64GNULDBackend::getRelEntrySize() {
return 16;
}
size_t Mips64GNULDBackend::getRelaEntrySize() {
return 24;
}
//===----------------------------------------------------------------------===//
/// createMipsLDBackend - the help funtion to create corresponding MipsLDBackend
///
static TargetLDBackend* createMipsLDBackend(const LinkerConfig& pConfig) {
const llvm::Triple& triple = pConfig.targets().triple();
if (triple.isOSDarwin()) {
assert(0 && "MachO linker is not supported yet");
}
if (triple.isOSWindows()) {
assert(0 && "COFF linker is not supported yet");
}
llvm::Triple::ArchType arch = triple.getArch();
if (llvm::Triple::mips64el == arch)
return new Mips64GNULDBackend(pConfig, new MipsGNUInfo(triple));
assert(arch == llvm::Triple::mipsel);
return new Mips32GNULDBackend(pConfig, new MipsGNUInfo(triple));
}
} // namespace mcld
//===----------------------------------------------------------------------===//
// Force static initialization.
//===----------------------------------------------------------------------===//
extern "C" void MCLDInitializeMipsLDBackend() {
mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheMipselTarget,
mcld::createMipsLDBackend);
mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheMips64elTarget,
mcld::createMipsLDBackend);
}