blob: c7a6b23a1d72654b9ecc134b7e5a32fce18a0576 [file] [log] [blame]
//===- MipsLDBackend.cpp --------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <llvm/ADT/Triple.h>
#include <llvm/Support/ELF.h>
#include <mcld/LD/SectionMap.h>
#include <mcld/MC/MCLDInfo.h>
#include <mcld/MC/MCLinker.h>
#include <mcld/Support/MemoryRegion.h>
#include <mcld/Support/TargetRegistry.h>
#include <mcld/Target/OutputRelocSection.h>
#include "Mips.h"
#include "MipsELFDynamic.h"
#include "MipsLDBackend.h"
#include "MipsRelocationFactory.h"
enum {
// The original o32 abi.
E_MIPS_ABI_O32 = 0x00001000,
// O32 extended to work on 64 bit architectures.
E_MIPS_ABI_O64 = 0x00002000,
// EABI in 32 bit mode.
E_MIPS_ABI_EABI32 = 0x00003000,
// EABI in 64 bit mode.
E_MIPS_ABI_EABI64 = 0x00004000
};
namespace mcld {
MipsGNULDBackend::MipsGNULDBackend()
: m_pRelocFactory(NULL),
m_pGOT(NULL),
m_pRelDyn(NULL),
m_pDynamic(NULL),
m_pGOTSymbol(NULL),
m_pGpDispSymbol(NULL)
{
}
MipsGNULDBackend::~MipsGNULDBackend()
{
if (NULL != m_pRelocFactory)
delete m_pRelocFactory;
if (NULL != m_pGOT)
delete m_pGOT;
if (NULL != m_pRelDyn)
delete m_pRelDyn;
if (NULL != m_pDynamic)
delete m_pDynamic;
}
bool MipsGNULDBackend::initTargetSectionMap(SectionMap& pSectionMap)
{
// Nothing to do because we do not support
// any MIPS specific sections now.
return true;
}
void MipsGNULDBackend::initTargetSections(MCLinker& pLinker)
{
// Nothing to do because we do not support
// any MIPS specific sections now.
}
void MipsGNULDBackend::initTargetSymbols(MCLinker& pLinker)
{
// Define the symbol _GLOBAL_OFFSET_TABLE_ if there is a symbol with the
// same name in input
m_pGOTSymbol = pLinker.defineSymbol<MCLinker::AsRefered, MCLinker::Resolve>(
"_GLOBAL_OFFSET_TABLE_",
false,
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Hidden);
m_pGpDispSymbol = pLinker.defineSymbol<MCLinker::AsRefered, MCLinker::Resolve>(
"_gp_disp",
false,
ResolveInfo::Section,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
if (NULL != m_pGpDispSymbol) {
m_pGpDispSymbol->resolveInfo()->setReserved(ReserveGpDisp);
}
}
bool MipsGNULDBackend::initRelocFactory(const MCLinker& pLinker)
{
if (NULL == m_pRelocFactory) {
m_pRelocFactory = new MipsRelocationFactory(1024, *this);
m_pRelocFactory->setLayout(pLinker.getLayout());
}
return true;
}
RelocationFactory* MipsGNULDBackend::getRelocFactory()
{
assert(NULL != m_pRelocFactory);
return m_pRelocFactory;
}
void MipsGNULDBackend::scanRelocation(Relocation& pReloc,
const LDSymbol& pInputSym,
MCLinker& pLinker,
const MCLDInfo& pLDInfo,
const Output& pOutput)
{
// rsym - The relocation target symbol
ResolveInfo* rsym = pReloc.symInfo();
assert(NULL != rsym && "ResolveInfo of relocation not set while scanRelocation");
// A refernece to symbol _GLOBAL_OFFSET_TABLE_ implies
// that a .got section is needed.
if (NULL == m_pGOT && NULL != m_pGOTSymbol) {
if (rsym == m_pGOTSymbol->resolveInfo()) {
createGOT(pLinker, pOutput);
}
}
if (rsym->isLocal())
scanLocalReloc(pReloc, pInputSym, pLinker, pLDInfo, pOutput);
else
scanGlobalReloc(pReloc, pInputSym, pLinker, pLDInfo, pOutput);
}
uint32_t MipsGNULDBackend::machine() const
{
return llvm::ELF::EM_MIPS;
}
uint8_t MipsGNULDBackend::OSABI() const
{
return llvm::ELF::ELFOSABI_NONE;
}
uint8_t MipsGNULDBackend::ABIVersion() const
{
return 0;
}
uint64_t MipsGNULDBackend::flags() const
{
// TODO: (simon) The correct flag's set depend on command line
// arguments and flags from input .o files.
return llvm::ELF::EF_MIPS_ARCH_32R2 |
llvm::ELF::EF_MIPS_NOREORDER |
llvm::ELF::EF_MIPS_PIC |
llvm::ELF::EF_MIPS_CPIC |
E_MIPS_ABI_O32;
}
bool MipsGNULDBackend::isLittleEndian() const
{
// Now we support little endian (mipsel) target only.
return true;
}
unsigned int MipsGNULDBackend::bitclass() const
{
return 32;
}
void MipsGNULDBackend::doPreLayout(const Output& pOutput,
const MCLDInfo& pInfo,
MCLinker& pLinker)
{
// when building shared object, the .got section is must.
if (pOutput.type() == Output::DynObj && NULL == m_pGOT) {
createGOT(pLinker, pOutput);
}
}
void MipsGNULDBackend::doPostLayout(const Output& pOutput,
const MCLDInfo& pInfo,
MCLinker& pLinker)
{
// emit program headers
if (pOutput.type() == Output::DynObj || pOutput.type() == Output::Exec)
emitProgramHdrs(pLinker.getLDInfo().output());
}
/// dynamic - the dynamic section of the target machine.
/// Use co-variant return type to return its own dynamic section.
MipsELFDynamic& MipsGNULDBackend::dynamic()
{
if (NULL == m_pDynamic)
m_pDynamic = new MipsELFDynamic(*this);
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( NULL != m_pDynamic);
return *m_pDynamic;
}
uint64_t MipsGNULDBackend::emitSectionData(const Output& pOutput,
const LDSection& pSection,
const MCLDInfo& pInfo,
MemoryRegion& pRegion) const
{
assert(pRegion.size() && "Size of MemoryRegion is zero!");
ELFFileFormat* file_format = getOutputFormat(pOutput);
if (&pSection == &(file_format->getGOT())) {
assert(NULL != m_pGOT && "emitSectionData failed, m_pGOT is NULL!");
uint64_t result = m_pGOT->emit(pRegion);
return result;
}
llvm::report_fatal_error(llvm::Twine("Unable to emit section `") +
pSection.name() +
llvm::Twine("'.\n"));
return 0;
}
/// isGOTSymbol - return true if the symbol is the GOT entry.
bool MipsGNULDBackend::isGOTSymbol(const LDSymbol& pSymbol) const
{
return std::find(m_LocalGOTSyms.begin(),
m_LocalGOTSyms.end(), &pSymbol) != m_LocalGOTSyms.end() ||
std::find(m_GlobalGOTSyms.begin(),
m_GlobalGOTSyms.end(), &pSymbol) != m_GlobalGOTSyms.end();
}
/// emitDynamicSymbol - emit dynamic symbol.
void MipsGNULDBackend::emitDynamicSymbol(llvm::ELF::Elf32_Sym& sym32,
Output& pOutput,
LDSymbol& pSymbol,
const Layout& pLayout,
char* strtab,
size_t strtabsize,
size_t symtabIdx)
{
// maintain output's symbol and index map
bool sym_exist = false;
HashTableType::entry_type* entry = 0;
entry = m_pSymIndexMap->insert(&pSymbol, sym_exist);
entry->setValue(symtabIdx);
// FIXME: check the endian between host and target
// write out symbol
sym32.st_name = strtabsize;
sym32.st_value = pSymbol.value();
sym32.st_size = getSymbolSize(pSymbol);
sym32.st_info = getSymbolInfo(pSymbol);
sym32.st_other = pSymbol.visibility();
sym32.st_shndx = getSymbolShndx(pSymbol, pLayout);
// write out string
strcpy((strtab + strtabsize), pSymbol.name());
}
/// emitNamePools - emit dynamic name pools - .dyntab, .dynstr, .hash
///
/// the size of these tables should be computed before layout
/// layout should computes the start offset of these tables
void MipsGNULDBackend::emitDynNamePools(Output& pOutput,
SymbolCategory& pSymbols,
const Layout& pLayout,
const MCLDInfo& pLDInfo)
{
assert(pOutput.hasMemArea());
ELFFileFormat* file_format = getOutputFormat(pOutput);
LDSection& symtab_sect = file_format->getDynSymTab();
LDSection& strtab_sect = file_format->getDynStrTab();
LDSection& hash_sect = file_format->getHashTab();
LDSection& dyn_sect = file_format->getDynamic();
MemoryRegion* symtab_region = pOutput.memArea()->request(symtab_sect.offset(),
symtab_sect.size());
MemoryRegion* strtab_region = pOutput.memArea()->request(strtab_sect.offset(),
strtab_sect.size());
MemoryRegion* hash_region = pOutput.memArea()->request(hash_sect.offset(),
hash_sect.size());
MemoryRegion* dyn_region = pOutput.memArea()->request(dyn_sect.offset(),
dyn_sect.size());
// set up symtab_region
llvm::ELF::Elf32_Sym* symtab32 = NULL;
symtab32 = (llvm::ELF::Elf32_Sym*)symtab_region->start();
symtab32[0].st_name = 0;
symtab32[0].st_value = 0;
symtab32[0].st_size = 0;
symtab32[0].st_info = 0;
symtab32[0].st_other = 0;
symtab32[0].st_shndx = 0;
// set up strtab_region
char* strtab = (char*)strtab_region->start();
strtab[0] = '\0';
bool sym_exist = false;
HashTableType::entry_type* entry = 0;
// add index 0 symbol into SymIndexMap
entry = m_pSymIndexMap->insert(NULL, sym_exist);
entry->setValue(0);
size_t symtabIdx = 1;
size_t strtabsize = 1;
// emit of .dynsym, and .dynstr except GOT entries
for (SymbolCategory::iterator symbol = pSymbols.begin(),
sym_end = pSymbols.end(); symbol != sym_end; ++symbol) {
if (!isDynamicSymbol(**symbol, pOutput))
continue;
if (isGOTSymbol(**symbol))
continue;
emitDynamicSymbol(symtab32[symtabIdx], pOutput, **symbol, pLayout, strtab,
strtabsize, symtabIdx);
// sum up counters
++symtabIdx;
strtabsize += (*symbol)->nameSize() + 1;
}
// emit global GOT
for (std::vector<LDSymbol*>::const_iterator symbol = m_GlobalGOTSyms.begin(),
symbol_end = m_GlobalGOTSyms.end();
symbol != symbol_end; ++symbol) {
emitDynamicSymbol(symtab32[symtabIdx], pOutput, **symbol, pLayout, strtab,
strtabsize, symtabIdx);
// sum up counters
++symtabIdx;
strtabsize += (*symbol)->nameSize() + 1;
}
// emit DT_NEED
// add DT_NEED strings into .dynstr
// Rules:
// 1. ignore --no-add-needed
// 2. force count in --no-as-needed
// 3. judge --as-needed
ELFDynamic::iterator dt_need = dynamic().needBegin();
InputTree::const_bfs_iterator input, inputEnd = pLDInfo.inputs().bfs_end();
for (input = pLDInfo.inputs().bfs_begin(); input != inputEnd; ++input) {
if (Input::DynObj == (*input)->type()) {
// --add-needed
if ((*input)->attribute()->isAddNeeded()) {
// --no-as-needed
if (!(*input)->attribute()->isAsNeeded()) {
strcpy((strtab + strtabsize), (*input)->name().c_str());
(*dt_need)->setValue(llvm::ELF::DT_NEEDED, strtabsize);
strtabsize += (*input)->name().size() + 1;
++dt_need;
}
// --as-needed
else if ((*input)->isNeeded()) {
strcpy((strtab + strtabsize), (*input)->name().c_str());
(*dt_need)->setValue(llvm::ELF::DT_NEEDED, strtabsize);
strtabsize += (*input)->name().size() + 1;
++dt_need;
}
}
}
} // for
// emit soname
// initialize value of ELF .dynamic section
dynamic().applySoname(strtabsize);
dynamic().applyEntries(pLDInfo, *file_format);
dynamic().emit(dyn_sect, *dyn_region);
strcpy((strtab + strtabsize), pOutput.name().c_str());
strtabsize += pOutput.name().size() + 1;
// emit hash table
// FIXME: this verion only emit SVR4 hash section.
// Please add GNU new hash section
// both 32 and 64 bits hash table use 32-bit entry
// set up hash_region
uint32_t* word_array = (uint32_t*)hash_region->start();
uint32_t& nbucket = word_array[0];
uint32_t& nchain = word_array[1];
nbucket = getHashBucketCount(symtabIdx, false);
nchain = symtabIdx;
uint32_t* bucket = (word_array + 2);
uint32_t* chain = (bucket + nbucket);
// initialize bucket
bzero((void*)bucket, nbucket);
StringHash<ELF> hash_func;
for (size_t sym_idx=0; sym_idx < symtabIdx; ++sym_idx) {
llvm::StringRef name(strtab + symtab32[sym_idx].st_name);
size_t bucket_pos = hash_func(name) % nbucket;
chain[sym_idx] = bucket[bucket_pos];
bucket[bucket_pos] = sym_idx;
}
}
MipsGOT& MipsGNULDBackend::getGOT()
{
assert(NULL != m_pGOT);
return *m_pGOT;
}
const MipsGOT& MipsGNULDBackend::getGOT() const
{
assert(NULL != m_pGOT);
return *m_pGOT;
}
OutputRelocSection& MipsGNULDBackend::getRelDyn()
{
assert(NULL != m_pRelDyn);
return *m_pRelDyn;
}
const OutputRelocSection& MipsGNULDBackend::getRelDyn() const
{
assert(NULL != m_pRelDyn);
return *m_pRelDyn;
}
unsigned int
MipsGNULDBackend::getTargetSectionOrder(const Output& pOutput,
const LDSection& pSectHdr) const
{
ELFFileFormat* file_format = getOutputFormat(pOutput);
if (&pSectHdr == &file_format->getGOT())
return SHO_DATA;
return SHO_UNDEFINED;
}
/// finalizeSymbol - finalize the symbol value
/// If the symbol's reserved field is not zero, MCLinker will call back this
/// function to ask the final value of the symbol
bool MipsGNULDBackend::finalizeSymbol(LDSymbol& pSymbol) const
{
if (&pSymbol == m_pGpDispSymbol) {
m_pGpDispSymbol->setValue(m_pGOT->getSection().addr() + 0x7FF0);
return true;
}
return false;
}
/// allocateCommonSymbols - allocate common symbols in the corresponding
/// sections.
/// @refer Google gold linker: common.cc: 214
/// FIXME: Mips needs to allocate small common symbol
bool
MipsGNULDBackend::allocateCommonSymbols(const MCLDInfo& pInfo, MCLinker& pLinker) const
{
// SymbolCategory contains all symbols that must emit to the output files.
// We are not like Google gold linker, we don't remember symbols before symbol
// resolution. All symbols in SymbolCategory are already resolved. Therefore, we
// don't need to care about some symbols may be changed its category due to symbol
// resolution.
SymbolCategory& symbol_list = pLinker.getOutputSymbols();
if (symbol_list.emptyCommons() && symbol_list.emptyLocals())
return true;
// addralign := max value of all common symbols
uint64_t addralign = 0x0;
// Due to the visibility, some common symbols may be forcefully local.
SymbolCategory::iterator com_sym, com_end = symbol_list.localEnd();
for (com_sym = symbol_list.localBegin(); com_sym != com_end; ++com_sym) {
if (ResolveInfo::Common == (*com_sym)->desc()) {
if ((*com_sym)->value() > addralign)
addralign = (*com_sym)->value();
}
}
// global common symbols.
com_end = symbol_list.commonEnd();
for (com_sym = symbol_list.commonBegin(); com_sym != com_end; ++com_sym) {
if ((*com_sym)->value() > addralign)
addralign = (*com_sym)->value();
}
// FIXME: If the order of common symbols is defined, then sort common symbols
// com_sym = symbol_list.commonBegin();
// std::sort(com_sym, com_end, some kind of order);
// get or create corresponding BSS LDSection
LDSection* bss_sect_hdr = NULL;
if (ResolveInfo::ThreadLocal == (*com_sym)->type()) {
bss_sect_hdr = &pLinker.getOrCreateOutputSectHdr(
".tbss",
LDFileFormat::BSS,
llvm::ELF::SHT_NOBITS,
llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC);
}
else {
bss_sect_hdr = &pLinker.getOrCreateOutputSectHdr(".bss",
LDFileFormat::BSS,
llvm::ELF::SHT_NOBITS,
llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC);
}
// get or create corresponding BSS MCSectionData
assert(NULL != bss_sect_hdr);
llvm::MCSectionData& bss_section = pLinker.getOrCreateSectData(*bss_sect_hdr);
// allocate all common symbols
uint64_t offset = bss_sect_hdr->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()) {
alignAddress(offset, (*com_sym)->value());
// 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);
llvm::MCFragment* frag = new llvm::MCFillFragment(0x0, 1, (*com_sym)->size(), &bss_section);
(*com_sym)->setFragmentRef(new MCFragmentRef(*frag, 0));
offset += (*com_sym)->size();
}
}
// allocate all global common symbols
com_end = symbol_list.commonEnd();
for (com_sym = symbol_list.commonBegin(); com_sym != com_end; ++com_sym) {
alignAddress(offset, (*com_sym)->value());
// 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);
llvm::MCFragment* frag = new llvm::MCFillFragment(0x0, 1, (*com_sym)->size(), &bss_section);
(*com_sym)->setFragmentRef(new MCFragmentRef(*frag, 0));
offset += (*com_sym)->size();
}
bss_sect_hdr->setSize(offset);
symbol_list.changeCommonsToGlobal();
return true;
}
void MipsGNULDBackend::updateAddend(Relocation& pReloc,
const LDSymbol& pInputSym,
const Layout& pLayout) const
{
// Update value keep in addend if we meet a section symbol
if(pReloc.symInfo()->type() == ResolveInfo::Section) {
pReloc.setAddend(pLayout.getOutputOffset(
*pInputSym.fragRef()) + pReloc.addend());
}
}
void MipsGNULDBackend::scanLocalReloc(Relocation& pReloc,
const LDSymbol& pInputSym,
MCLinker& pLinker,
const MCLDInfo& pLDInfo,
const Output& pOutput)
{
ResolveInfo* rsym = pReloc.symInfo();
updateAddend(pReloc, pInputSym, pLinker.getLayout());
switch (pReloc.type()){
case llvm::ELF::R_MIPS_NONE:
case llvm::ELF::R_MIPS_16:
break;
case llvm::ELF::R_MIPS_32:
if (Output::DynObj == pOutput.type()) {
// TODO: (simon) The gold linker does not create an entry in .rel.dyn
// section if the symbol section flags contains SHF_EXECINSTR.
// 1. Find the reason of this condition.
// 2. Check this condition here.
if (NULL == m_pRelDyn)
createRelDyn(pLinker, pOutput);
m_pRelDyn->reserveEntry(*m_pRelocFactory);
rsym->setReserved(rsym->reserved() | ReserveRel);
}
break;
case llvm::ELF::R_MIPS_REL32:
case llvm::ELF::R_MIPS_26:
case llvm::ELF::R_MIPS_HI16:
case llvm::ELF::R_MIPS_LO16:
case llvm::ELF::R_MIPS_PC16:
case llvm::ELF::R_MIPS_SHIFT5:
case llvm::ELF::R_MIPS_SHIFT6:
case llvm::ELF::R_MIPS_64:
case llvm::ELF::R_MIPS_GOT_PAGE:
case llvm::ELF::R_MIPS_GOT_OFST:
case llvm::ELF::R_MIPS_SUB:
case llvm::ELF::R_MIPS_INSERT_A:
case llvm::ELF::R_MIPS_INSERT_B:
case llvm::ELF::R_MIPS_DELETE:
case llvm::ELF::R_MIPS_HIGHER:
case llvm::ELF::R_MIPS_HIGHEST:
case llvm::ELF::R_MIPS_SCN_DISP:
case llvm::ELF::R_MIPS_REL16:
case llvm::ELF::R_MIPS_ADD_IMMEDIATE:
case llvm::ELF::R_MIPS_PJUMP:
case llvm::ELF::R_MIPS_RELGOT:
case llvm::ELF::R_MIPS_JALR:
case llvm::ELF::R_MIPS_GLOB_DAT:
case llvm::ELF::R_MIPS_COPY:
case llvm::ELF::R_MIPS_JUMP_SLOT:
break;
case llvm::ELF::R_MIPS_GOT16:
case llvm::ELF::R_MIPS_CALL16:
if (NULL == m_pGOT)
createGOT(pLinker, pOutput);
if (!(rsym->reserved() & MipsGNULDBackend::ReserveGot)) {
m_pGOT->reserveLocalEntry();
rsym->setReserved(rsym->reserved() | ReserveGot);
m_LocalGOTSyms.push_back(rsym->outSymbol());
}
break;
case llvm::ELF::R_MIPS_GPREL32:
case llvm::ELF::R_MIPS_GPREL16:
case llvm::ELF::R_MIPS_LITERAL:
break;
case llvm::ELF::R_MIPS_GOT_DISP:
case llvm::ELF::R_MIPS_GOT_HI16:
case llvm::ELF::R_MIPS_CALL_HI16:
case llvm::ELF::R_MIPS_GOT_LO16:
case llvm::ELF::R_MIPS_CALL_LO16:
break;
case llvm::ELF::R_MIPS_TLS_DTPMOD32:
case llvm::ELF::R_MIPS_TLS_DTPREL32:
case llvm::ELF::R_MIPS_TLS_DTPMOD64:
case llvm::ELF::R_MIPS_TLS_DTPREL64:
case llvm::ELF::R_MIPS_TLS_GD:
case llvm::ELF::R_MIPS_TLS_LDM:
case llvm::ELF::R_MIPS_TLS_DTPREL_HI16:
case llvm::ELF::R_MIPS_TLS_DTPREL_LO16:
case llvm::ELF::R_MIPS_TLS_GOTTPREL:
case llvm::ELF::R_MIPS_TLS_TPREL32:
case llvm::ELF::R_MIPS_TLS_TPREL64:
case llvm::ELF::R_MIPS_TLS_TPREL_HI16:
case llvm::ELF::R_MIPS_TLS_TPREL_LO16:
break;
default:
llvm::report_fatal_error(llvm::Twine("Unknown relocation ") +
llvm::Twine(pReloc.type()) +
llvm::Twine("for the local symbol `") +
pReloc.symInfo()->name() +
llvm::Twine("'."));
}
}
void MipsGNULDBackend::scanGlobalReloc(Relocation& pReloc,
const LDSymbol& pInputSym,
MCLinker& pLinker,
const MCLDInfo& pLDInfo,
const Output& pOutput)
{
ResolveInfo* rsym = pReloc.symInfo();
switch (pReloc.type()){
case llvm::ELF::R_MIPS_NONE:
case llvm::ELF::R_MIPS_INSERT_A:
case llvm::ELF::R_MIPS_INSERT_B:
case llvm::ELF::R_MIPS_DELETE:
case llvm::ELF::R_MIPS_TLS_DTPMOD64:
case llvm::ELF::R_MIPS_TLS_DTPREL64:
case llvm::ELF::R_MIPS_REL16:
case llvm::ELF::R_MIPS_ADD_IMMEDIATE:
case llvm::ELF::R_MIPS_PJUMP:
case llvm::ELF::R_MIPS_RELGOT:
case llvm::ELF::R_MIPS_TLS_TPREL64:
break;
case llvm::ELF::R_MIPS_32:
case llvm::ELF::R_MIPS_64:
case llvm::ELF::R_MIPS_HI16:
case llvm::ELF::R_MIPS_LO16:
if (isSymbolNeedsDynRel(*rsym, pOutput)) {
if (NULL == m_pRelDyn)
createRelDyn(pLinker, pOutput);
m_pRelDyn->reserveEntry(*m_pRelocFactory);
rsym->setReserved(rsym->reserved() | ReserveRel);
}
break;
case llvm::ELF::R_MIPS_GOT16:
case llvm::ELF::R_MIPS_CALL16:
case llvm::ELF::R_MIPS_GOT_DISP:
case llvm::ELF::R_MIPS_GOT_HI16:
case llvm::ELF::R_MIPS_CALL_HI16:
case llvm::ELF::R_MIPS_GOT_LO16:
case llvm::ELF::R_MIPS_CALL_LO16:
case llvm::ELF::R_MIPS_GOT_PAGE:
case llvm::ELF::R_MIPS_GOT_OFST:
if (NULL == m_pGOT)
createGOT(pLinker, pOutput);
if (!(rsym->reserved() & MipsGNULDBackend::ReserveGot)) {
m_pGOT->reserveGlobalEntry();
rsym->setReserved(rsym->reserved() | ReserveGot);
m_GlobalGOTSyms.push_back(rsym->outSymbol());
}
break;
case llvm::ELF::R_MIPS_LITERAL:
case llvm::ELF::R_MIPS_GPREL32:
llvm::report_fatal_error(llvm::Twine("Relocation ") +
llvm::Twine(pReloc.type()) +
llvm::Twine(" is not defined for the "
"global symbol `") +
pReloc.symInfo()->name() +
llvm::Twine("'."));
break;
case llvm::ELF::R_MIPS_GPREL16:
break;
case llvm::ELF::R_MIPS_26:
case llvm::ELF::R_MIPS_PC16:
break;
case llvm::ELF::R_MIPS_16:
case llvm::ELF::R_MIPS_SHIFT5:
case llvm::ELF::R_MIPS_SHIFT6:
case llvm::ELF::R_MIPS_SUB:
case llvm::ELF::R_MIPS_HIGHER:
case llvm::ELF::R_MIPS_HIGHEST:
case llvm::ELF::R_MIPS_SCN_DISP:
break;
case llvm::ELF::R_MIPS_TLS_DTPREL32:
case llvm::ELF::R_MIPS_TLS_GD:
case llvm::ELF::R_MIPS_TLS_LDM:
case llvm::ELF::R_MIPS_TLS_DTPREL_HI16:
case llvm::ELF::R_MIPS_TLS_DTPREL_LO16:
case llvm::ELF::R_MIPS_TLS_GOTTPREL:
case llvm::ELF::R_MIPS_TLS_TPREL32:
case llvm::ELF::R_MIPS_TLS_TPREL_HI16:
case llvm::ELF::R_MIPS_TLS_TPREL_LO16:
break;
case llvm::ELF::R_MIPS_REL32:
break;
case llvm::ELF::R_MIPS_JALR:
break;
case llvm::ELF::R_MIPS_COPY:
case llvm::ELF::R_MIPS_GLOB_DAT:
case llvm::ELF::R_MIPS_JUMP_SLOT:
llvm::report_fatal_error(llvm::Twine("Relocation ") +
llvm::Twine(pReloc.type()) +
llvm::Twine("for the global symbol `") +
pReloc.symInfo()->name() +
llvm::Twine("' should only be seen "
"by the dynamic linker"));
break;
default:
llvm::report_fatal_error(llvm::Twine("Unknown relocation ") +
llvm::Twine(pReloc.type()) +
llvm::Twine("for the global symbol `") +
pReloc.symInfo()->name() +
llvm::Twine("'."));
}
}
bool MipsGNULDBackend::isSymbolNeedsPLT(ResolveInfo& pSym,
const Output& pOutput) const
{
return (Output::DynObj == pOutput.type() &&
ResolveInfo::Function == pSym.type() &&
(pSym.isDyn() || pSym.isUndef()));
}
bool MipsGNULDBackend::isSymbolNeedsDynRel(ResolveInfo& pSym,
const Output& pOutput) const
{
if(pSym.isUndef() && Output::Exec == pOutput.type())
return false;
if(pSym.isAbsolute())
return false;
if(Output::DynObj == pOutput.type())
return true;
if(pSym.isDyn() || pSym.isUndef())
return true;
return false;
}
void MipsGNULDBackend::createGOT(MCLinker& pLinker, const Output& pOutput)
{
ELFFileFormat* file_format = getOutputFormat(pOutput);
LDSection& got = file_format->getGOT();
m_pGOT = new MipsGOT(got, pLinker.getOrCreateSectData(got));
// define symbol _GLOBAL_OFFSET_TABLE_ when .got create
if( m_pGOTSymbol != NULL ) {
pLinker.defineSymbol<MCLinker::Force, MCLinker::Unresolve>(
"_GLOBAL_OFFSET_TABLE_",
false,
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
pLinker.getLayout().getFragmentRef(*(m_pGOT->begin()), 0x0),
ResolveInfo::Hidden);
}
else {
m_pGOTSymbol = pLinker.defineSymbol<MCLinker::Force, MCLinker::Resolve>(
"_GLOBAL_OFFSET_TABLE_",
false,
ResolveInfo::Object,
ResolveInfo::Define,
ResolveInfo::Local,
0x0, // size
0x0, // value
pLinker.getLayout().getFragmentRef(*(m_pGOT->begin()), 0x0),
ResolveInfo::Hidden);
}
}
void MipsGNULDBackend::createRelDyn(MCLinker& pLinker, const Output& pOutput)
{
ELFFileFormat* file_format = getOutputFormat(pOutput);
// get .rel.dyn LDSection and create MCSectionData
LDSection& reldyn = file_format->getRelDyn();
// create MCSectionData and ARMRelDynSection
m_pRelDyn = new OutputRelocSection(reldyn,
pLinker.getOrCreateSectData(reldyn),
8);
}
ELFFileFormat* MipsGNULDBackend::getOutputFormat(const Output& pOutput) const
{
switch (pOutput.type()) {
case Output::DynObj:
return getDynObjFileFormat();
case Output::Exec:
return getExecFileFormat();
case Output::Object:
return NULL;
default:
llvm::report_fatal_error(llvm::Twine("Unsupported output file format: ") +
llvm::Twine(pOutput.type()));
return NULL;
}
}
//===----------------------------------------------------------------------===//
/// createMipsLDBackend - the help funtion to create corresponding MipsLDBackend
///
static TargetLDBackend* createMipsLDBackend(const llvm::Target& pTarget,
const std::string& pTriple)
{
llvm::Triple theTriple(pTriple);
if (theTriple.isOSDarwin()) {
assert(0 && "MachO linker is not supported yet");
}
if (theTriple.isOSWindows()) {
assert(0 && "COFF linker is not supported yet");
}
return new MipsGNULDBackend();
}
} // namespace of mcld
//=============================
// Force static initialization.
extern "C" void LLVMInitializeMipsLDBackend() {
// Register the linker backend
mcld::TargetRegistry::RegisterTargetLDBackend(mcld::TheMipselTarget,
mcld::createMipsLDBackend);
}