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