blob: 354cd51240cdcca8f79b1c215500d7f5380199ef [file] [log] [blame]
//===- X86LDBackend.cpp ---------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "X86.h"
#include "X86ELFDynamic.h"
#include "X86LDBackend.h"
#include "X86RelocationFactory.h"
#include <llvm/ADT/Triple.h>
#include <mcld/Support/MemoryRegion.h>
#include <mcld/Support/TargetRegistry.h>
#include <mcld/MC/MCLDInfo.h>
#include <mcld/MC/MCLDOutput.h>
#include <mcld/MC/MCLinker.h>
#include <mcld/LD/SectionMap.h>
#include <mcld/MC/MCRegionFragment.h>
#include <cstring>
using namespace mcld;
X86GNULDBackend::X86GNULDBackend()
: m_pRelocFactory(NULL),
m_pGOT(NULL),
m_pPLT(NULL),
m_pRelDyn(NULL),
m_pRelPLT(NULL),
m_pDynamic(NULL) {
}
X86GNULDBackend::~X86GNULDBackend()
{
if (NULL != m_pRelocFactory)
delete m_pRelocFactory;
if (NULL != m_pGOT)
delete m_pGOT;
if (NULL != m_pPLT)
delete m_pPLT;
if (NULL !=m_pRelDyn)
delete m_pRelDyn;
if (NULL != m_pRelPLT)
delete m_pRelPLT;
if (NULL != m_pDynamic)
delete m_pDynamic;
}
RelocationFactory* X86GNULDBackend::getRelocFactory()
{
assert(NULL != m_pRelocFactory);
return m_pRelocFactory;
}
bool X86GNULDBackend::initRelocFactory(const MCLinker& pLinker)
{
if (NULL == m_pRelocFactory) {
m_pRelocFactory = new X86RelocationFactory(1024, *this);
m_pRelocFactory->setLayout(pLinker.getLayout());
}
return true;
}
void X86GNULDBackend::doPreLayout(const Output& pOutput,
const MCLDInfo& pInfo,
MCLinker& pLinker)
{
// when building shared object, the .got section is needed
if(pOutput.type() == Output::DynObj && (NULL == m_pGOT))
createX86GOT(pLinker, pOutput);
}
void X86GNULDBackend::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.
X86ELFDynamic& X86GNULDBackend::dynamic()
{
if (NULL == m_pDynamic)
m_pDynamic = new X86ELFDynamic(*this);
return *m_pDynamic;
}
/// dynamic - the dynamic section of the target machine.
/// Use co-variant return type to return its own dynamic section.
const X86ELFDynamic& X86GNULDBackend::dynamic() const
{
assert( NULL != m_pDynamic);
return *m_pDynamic;
}
void X86GNULDBackend::createX86GOT(MCLinker& pLinker, const Output& pOutput)
{
// get .got LDSection and create MCSectionData
ELFFileFormat* file_format = getOutputFormat(pOutput);
LDSection& got = file_format->getGOT();
m_pGOT = new X86GOT(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 X86GNULDBackend::createX86PLTandRelPLT(MCLinker& pLinker,
const Output& pOutput)
{
ELFFileFormat* file_format = getOutputFormat(pOutput);
LDSection& plt = file_format->getPLT();
LDSection& relplt = file_format->getRelPlt();
// create MCSectionData and X86PLT
m_pPLT = new X86PLT(plt, pLinker.getOrCreateSectData(plt), *m_pGOT, pOutput);
// set info of .rel.plt to .plt
relplt.setLink(&plt);
// create MCSectionData and X86RelDynSection
m_pRelPLT = new OutputRelocSection(relplt,
pLinker.getOrCreateSectData(relplt),
8);
}
void X86GNULDBackend::createX86RelDyn(MCLinker& pLinker,
const Output& pOutput)
{
// get .rel.dyn LDSection and create MCSectionData
ELFFileFormat* file_format = getOutputFormat(pOutput);
LDSection& reldyn = file_format->getRelDyn();
// create MCSectionData and X86RelDynSection
m_pRelDyn = new OutputRelocSection(reldyn,
pLinker.getOrCreateSectData(reldyn),
8);
}
ELFFileFormat* X86GNULDBackend::getOutputFormat(const Output& pOutput) const
{
switch (pOutput.type()) {
case Output::DynObj:
return getDynObjFileFormat();
case Output::Exec:
return getExecFileFormat();
// FIXME: We do not support building .o now
case Output::Object:
default:
llvm::report_fatal_error(llvm::Twine("Unsupported output file format: ") +
llvm::Twine(pOutput.type()));
return NULL;
}
}
bool X86GNULDBackend::isSymbolNeedsPLT(const ResolveInfo& pSym,
const MCLDInfo& pLDInfo,
const Output& pOutput) const
{
return((Output::DynObj == pOutput.type())
&&(ResolveInfo::Function == pSym.type())
&&(pSym.isDyn() || pSym.isUndef() ||
isSymbolPreemptible(pSym, pLDInfo, pOutput))
);
}
bool X86GNULDBackend::isSymbolNeedsDynRel(const ResolveInfo& pSym,
const Output& pOutput,
bool isAbsReloc) const
{
if(pSym.isUndef() && (pOutput.type()==Output::Exec))
return false;
if(pSym.isAbsolute())
return false;
if(pOutput.type()==Output::DynObj && isAbsReloc)
return true;
if(pSym.isDyn() || pSym.isUndef())
return true;
return false;
}
bool X86GNULDBackend::isSymbolPreemptible(const ResolveInfo& pSym,
const MCLDInfo& pLDInfo,
const Output& pOutput) const
{
if(pSym.other() != ResolveInfo::Default)
return false;
if(pOutput.type() != Output::DynObj)
return false;
if(pLDInfo.options().Bsymbolic())
return false;
return true;
}
void X86GNULDBackend::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 X86GNULDBackend::scanLocalReloc(Relocation& pReloc,
const LDSymbol& pInputSym,
MCLinker& pLinker,
const MCLDInfo& pLDInfo,
const Output& pOutput)
{
// rsym - The relocation target symbol
ResolveInfo* rsym = pReloc.symInfo();
updateAddend(pReloc, pInputSym, pLinker.getLayout());
switch(pReloc.type()){
case llvm::ELF::R_386_32:
// If buiding PIC object (shared library or PIC executable),
// a dynamic relocations with RELATIVE type to this location is needed.
// Reserve an entry in .rel.dyn
if(Output::DynObj == pOutput.type()) {
// create .rel.dyn section if not exist
if(NULL == m_pRelDyn)
createX86RelDyn(pLinker, pOutput);
m_pRelDyn->reserveEntry(*m_pRelocFactory);
// set Rel bit
rsym->setReserved(rsym->reserved() | ReserveRel);
}
return;
case llvm::ELF::R_386_GOTOFF:
case llvm::ELF::R_386_GOTPC:
// A GOT section is needed
if(NULL == m_pGOT)
createX86GOT(pLinker, pOutput);
return;
case llvm::ELF::R_386_PC32:
return;
default:
llvm::report_fatal_error(llvm::Twine("unexpected reloc ") +
llvm::Twine((int) pReloc.type()) +
llvm::Twine(" in object file"));
break;
} // end switch
}
void X86GNULDBackend::scanGlobalReloc(Relocation& pReloc,
const LDSymbol& pInputSym,
MCLinker& pLinker,
const MCLDInfo& pLDInfo,
const Output& pOutput)
{
// rsym - The relocation target symbol
ResolveInfo* rsym = pReloc.symInfo();
switch(pReloc.type()) {
case llvm::ELF::R_386_32:
// Absolute relocation type, symbol may needs PLT entry or
// dynamic relocation entry
if(isSymbolNeedsPLT(*rsym, pLDInfo, pOutput)) {
// create plt for this symbol if it does not have one
if(!(rsym->reserved() & ReservePLT)){
// Create .got section if it dosen't exist
if(NULL == m_pGOT)
createX86GOT(pLinker, pOutput);
// create .plt and .rel.plt if not exist
if(NULL == m_pPLT)
createX86PLTandRelPLT(pLinker, pOutput);
// Symbol needs PLT entry, we need to reserve a PLT entry
// and the corresponding GOT and dynamic relocation entry
// in .got and .rel.plt. (GOT entry will be reserved simultaneously
// when calling X86PLT->reserveEntry())
m_pPLT->reserveEntry();
m_pRelPLT->reserveEntry(*m_pRelocFactory);
// set PLT bit
rsym->setReserved(rsym->reserved() | ReservePLT);
}
}
if(isSymbolNeedsDynRel(*rsym, pOutput, true)) {
// symbol needs dynamic relocation entry, reserve an entry in .rel.dyn
// create .rel.dyn section if not exist
if(NULL == m_pRelDyn)
createX86RelDyn(pLinker, pOutput);
m_pRelDyn->reserveEntry(*m_pRelocFactory);
// set Rel bit
rsym->setReserved(rsym->reserved() | ReserveRel);
}
return;
case llvm::ELF::R_386_GOTOFF:
case llvm::ELF::R_386_GOTPC: {
// A GOT section is needed
if(NULL == m_pGOT)
createX86GOT(pLinker, pOutput);
return;
}
case llvm::ELF::R_386_PLT32:
// A PLT entry is needed when building shared library
// return if we already create plt for this symbol
if(rsym->reserved() & ReservePLT)
return;
// if symbol is defined in the ouput file and it's not
// preemptible, no need plt
if(rsym->isDefine() && !rsym->isDyn() &&
!isSymbolPreemptible(*rsym, pLDInfo, pOutput)) {
return;
}
// Create .got section if it dosen't exist
if(NULL == m_pGOT)
createX86GOT(pLinker, pOutput);
// create .plt and .rel.plt if not exist
if(NULL == m_pPLT)
createX86PLTandRelPLT(pLinker, pOutput);
// Symbol needs PLT entry, we need to reserve a PLT entry
// and the corresponding GOT and dynamic relocation entry
// in .got and .rel.plt. (GOT entry will be reserved simultaneously
// when calling X86PLT->reserveEntry())
m_pPLT->reserveEntry();
m_pRelPLT->reserveEntry(*m_pRelocFactory);
// set PLT bit
rsym->setReserved(rsym->reserved() | ReservePLT);
return;
case llvm::ELF::R_386_GOT32:
// Symbol needs GOT entry, reserve entry in .got
// return if we already create GOT for this symbol
if(rsym->reserved() & (ReserveGOT | GOTRel))
return;
if(NULL == m_pGOT)
createX86GOT(pLinker, pOutput);
m_pGOT->reserveEntry();
// If building shared object or the symbol is undefined, a dynamic
// relocation is needed to relocate this GOT entry. Reserve an
// entry in .rel.dyn
if(Output::DynObj == pOutput.type() || rsym->isUndef() || rsym->isDyn()) {
// create .rel.dyn section if not exist
if(NULL == m_pRelDyn)
createX86RelDyn(pLinker, pOutput);
m_pRelDyn->reserveEntry(*m_pRelocFactory);
// set GOTRel bit
rsym->setReserved(rsym->reserved() | GOTRel);
return;
}
// set GOT bit
rsym->setReserved(rsym->reserved() | ReserveGOT);
return;
case llvm::ELF::R_386_PC32:
// We allow R_386_PC32 only if it isn't preemptible. Otherwise
// we will generate writable text section in output.
if (!isSymbolPreemptible(*rsym, pLDInfo, pOutput))
return;
default: {
llvm::report_fatal_error(llvm::Twine("Unexpected reloc ") +
llvm::Twine((int) pReloc.type()) +
llvm::Twine(" in object file"));
break;
}
} // end switch
}
void X86GNULDBackend::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");
// Scan relocation type to determine if an GOT/PLT/Dynamic Relocation
// entries should be created.
// FIXME: Below judgements concern only .so is generated as output
// FIXME: Below judgements concren nothing about TLS related relocation
// 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()) {
createX86GOT(pLinker, pOutput);
}
}
// rsym is local
if(rsym->isLocal())
scanLocalReloc(pReloc, pInputSym, pLinker, pLDInfo, pOutput);
// rsym is external
else
scanGlobalReloc(pReloc, pInputSym ,pLinker, pLDInfo, pOutput);
}
uint64_t X86GNULDBackend::emitSectionData(const Output& pOutput,
const LDSection& pSection,
const MCLDInfo& pInfo,
MemoryRegion& pRegion) const
{
assert(pRegion.size() && "Size of MemoryRegion is zero!");
ELFFileFormat* FileFormat = getOutputFormat(pOutput);
assert(FileFormat &&
"ELFFileFormat is NULL in X86GNULDBackend::emitSectionData!");
unsigned int EntrySize = 0;
uint64_t RegionSize = 0;
if (&pSection == &(FileFormat->getPLT())) {
assert(m_pPLT && "emitSectionData failed, m_pPLT is NULL!");
unsigned char* buffer = pRegion.getBuffer();
m_pPLT->applyPLT0();
m_pPLT->applyPLT1();
X86PLT::iterator it = m_pPLT->begin();
unsigned int plt0_size = llvm::cast<X86PLT0>((*it)).getEntrySize();
memcpy(buffer, llvm::cast<X86PLT0>((*it)).getContent(), plt0_size);
RegionSize += plt0_size;
++it;
X86PLT1* plt1 = 0;
X86PLT::iterator ie = m_pPLT->end();
while (it != ie) {
plt1 = &(llvm::cast<X86PLT1>(*it));
EntrySize = plt1->getEntrySize();
memcpy(buffer + RegionSize, plt1->getContent(), EntrySize);
RegionSize += EntrySize;
++it;
}
}
else if (&pSection == &(FileFormat->getGOT())) {
assert(m_pGOT && "emitSectionData failed, m_pGOT is NULL!");
m_pGOT->applyGOT0(FileFormat->getDynamic().addr());
uint32_t* buffer = reinterpret_cast<uint32_t*>(pRegion.getBuffer());
GOTEntry* got = 0;
EntrySize = m_pGOT->getEntrySize();
for (X86GOT::iterator it = m_pGOT->begin(),
ie = m_pGOT->end(); it != ie; ++it, ++buffer) {
got = &(llvm::cast<GOTEntry>((*it)));
*buffer = static_cast<uint32_t>(got->getContent());
RegionSize += EntrySize;
}
}
else
llvm::report_fatal_error("unsupported section name "
+ pSection.name() + " !");
return RegionSize;
}
uint32_t X86GNULDBackend::machine() const
{
return llvm::ELF::EM_386;
}
X86GOT& X86GNULDBackend::getGOT()
{
assert(NULL != m_pGOT);
return *m_pGOT;
}
const X86GOT& X86GNULDBackend::getGOT() const
{
assert(NULL != m_pGOT);
return *m_pGOT;
}
X86PLT& X86GNULDBackend::getPLT()
{
assert(NULL != m_pPLT && "PLT section not exist");
return *m_pPLT;
}
const X86PLT& X86GNULDBackend::getPLT() const
{
assert(NULL != m_pPLT && "PLT section not exist");
return *m_pPLT;
}
OutputRelocSection& X86GNULDBackend::getRelDyn()
{
assert(NULL != m_pRelDyn && ".rel.dyn section not exist");
return *m_pRelDyn;
}
const OutputRelocSection& X86GNULDBackend::getRelDyn() const
{
assert(NULL != m_pRelDyn && ".rel.dyn section not exist");
return *m_pRelDyn;
}
OutputRelocSection& X86GNULDBackend::getRelPLT()
{
assert(NULL != m_pRelPLT && ".rel.plt section not exist");
return *m_pRelPLT;
}
const OutputRelocSection& X86GNULDBackend::getRelPLT() const
{
assert(NULL != m_pRelPLT && ".rel.plt section not exist");
return *m_pRelPLT;
}
unsigned int
X86GNULDBackend::getTargetSectionOrder(const Output& pOutput,
const LDSection& pSectHdr) const
{
ELFFileFormat* file_format = getOutputFormat(pOutput);
// FIXME: if command line option, "-z now", is given, we can let the order of
// .got and .got.plt be the same as RELRO sections
if (&pSectHdr == &file_format->getGOT())
return SHO_RELRO_LAST;
if (&pSectHdr == &file_format->getGOTPLT())
return SHO_NON_RELRO_FIRST;
if (&pSectHdr == &file_format->getPLT())
return SHO_PLT;
return SHO_UNDEFINED;
}
unsigned int X86GNULDBackend::bitclass() const
{
return 32;
}
bool X86GNULDBackend::initTargetSectionMap(SectionMap& pSectionMap)
{
return true;
}
void X86GNULDBackend::initTargetSections(MCLinker& pLinker)
{
}
void X86GNULDBackend::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);
}
/// 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 X86GNULDBackend::finalizeSymbol(LDSymbol& pSymbol) const
{
return false;
}
/// allocateCommonSymbols - allocate common symbols in the corresponding
/// sections.
/// @refer Google gold linker: common.cc: 214
bool
X86GNULDBackend::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()) {
// 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());
(*com_sym)->setFragmentRef(new MCFragmentRef(*frag, 0));
uint64_t size = pLinker.getLayout().appendFragment(*frag,
bss_section,
(*com_sym)->value());
offset += size;
}
}
// 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);
llvm::MCFragment* frag = new llvm::MCFillFragment(0x0, 1, (*com_sym)->size());
(*com_sym)->setFragmentRef(new MCFragmentRef(*frag, 0));
uint64_t size = pLinker.getLayout().appendFragment(*frag,
bss_section,
(*com_sym)->value());
offset += size;
}
bss_sect_hdr->setSize(offset);
symbol_list.changeCommonsToGlobal();
return true;
}
namespace mcld {
//===----------------------------------------------------------------------===//
/// createX86LDBackend - the help funtion to create corresponding X86LDBackend
///
TargetLDBackend* createX86LDBackend(const llvm::Target& pTarget,
const std::string& pTriple)
{
Triple theTriple(pTriple);
if (theTriple.isOSDarwin()) {
assert(0 && "MachO linker is not supported yet");
/**
return new X86MachOLDBackend(createX86MachOArchiveReader,
createX86MachOObjectReader,
createX86MachOObjectWriter);
**/
}
if (theTriple.isOSWindows()) {
assert(0 && "COFF linker is not supported yet");
/**
return new X86COFFLDBackend(createX86COFFArchiveReader,
createX86COFFObjectReader,
createX86COFFObjectWriter);
**/
}
return new X86GNULDBackend();
}
} // namespace of mcld
//=============================
// Force static initialization.
extern "C" void LLVMInitializeX86LDBackend() {
// Register the linker backend
mcld::TargetRegistry::RegisterTargetLDBackend(TheX86Target, createX86LDBackend);
}