blob: f81133f2e2ade6974549d139f7b755c33caa24ae [file] [log] [blame]
//===- GNULDBackend.cpp ---------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/Target/GNULDBackend.h>
#include <string>
#include <cstring>
#include <cassert>
#include <llvm/Support/ELF.h>
#include <mcld/ADT/SizeTraits.h>
#include <mcld/LD/LDSymbol.h>
#include <mcld/LD/Layout.h>
#include <mcld/LD/FillFragment.h>
#include <mcld/MC/MCLDInfo.h>
#include <mcld/MC/MCLDOutput.h>
#include <mcld/MC/InputTree.h>
#include <mcld/MC/SymbolCategory.h>
#include <mcld/MC/MCLinker.h>
#include <mcld/Support/MemoryArea.h>
#include <mcld/Support/MemoryRegion.h>
#include <mcld/Support/MsgHandling.h>
#include <mcld/Support/MemoryAreaFactory.h>
using namespace mcld;
//===----------------------------------------------------------------------===//
// GNULDBackend
//===----------------------------------------------------------------------===//
GNULDBackend::GNULDBackend()
: m_pArchiveReader(NULL),
m_pObjectReader(NULL),
m_pDynObjReader(NULL),
m_pObjectWriter(NULL),
m_pDynObjWriter(NULL),
m_pExecWriter(NULL),
m_pDynObjFileFormat(NULL),
m_pExecFileFormat(NULL),
m_ELFSegmentTable(9), // magic number
m_pEhFrameHdr(NULL),
f_pPreInitArrayStart(NULL),
f_pPreInitArrayEnd(NULL),
f_pInitArrayStart(NULL),
f_pInitArrayEnd(NULL),
f_pFiniArrayStart(NULL),
f_pFiniArrayEnd(NULL),
f_pStack(NULL),
f_pExecutableStart(NULL),
f_pEText(NULL),
f_p_EText(NULL),
f_p__EText(NULL),
f_pEData(NULL),
f_p_EData(NULL),
f_pBSSStart(NULL),
f_pEnd(NULL),
f_p_End(NULL) {
m_pSymIndexMap = new HashTableType(1024);
}
GNULDBackend::~GNULDBackend()
{
if (NULL != m_pArchiveReader)
delete m_pArchiveReader;
if (NULL != m_pObjectReader)
delete m_pObjectReader;
if (NULL != m_pDynObjReader)
delete m_pDynObjReader;
if (NULL != m_pObjectWriter)
delete m_pObjectWriter;
if (NULL != m_pDynObjWriter)
delete m_pDynObjWriter;
if (NULL != m_pExecWriter)
delete m_pExecWriter;
if (NULL != m_pDynObjFileFormat)
delete m_pDynObjFileFormat;
if (NULL != m_pExecFileFormat)
delete m_pExecFileFormat;
if (NULL != m_pSymIndexMap)
delete m_pSymIndexMap;
if (NULL != m_pEhFrameHdr)
delete m_pEhFrameHdr;
}
size_t GNULDBackend::sectionStartOffset() const
{
// FIXME: use fixed offset, we need 10 segments by default
return sizeof(llvm::ELF::Elf64_Ehdr)+10*sizeof(llvm::ELF::Elf64_Phdr);
}
uint64_t GNULDBackend::segmentStartAddr(const Output& pOutput,
const MCLDInfo& pInfo) const
{
// TODO: handle the user option: -TText=
if (isOutputPIC(pOutput, pInfo))
return 0x0;
else
return defaultTextSegmentAddr();
}
bool GNULDBackend::initArchiveReader(MCLinker& pLinker,
MCLDInfo& pInfo,
MemoryAreaFactory& pMemAreaFactory)
{
if (NULL == m_pArchiveReader) {
assert(NULL != m_pObjectReader);
m_pArchiveReader = new GNUArchiveReader(pInfo,
pMemAreaFactory,
*m_pObjectReader);
}
return true;
}
bool GNULDBackend::initObjectReader(MCLinker& pLinker)
{
if (NULL == m_pObjectReader)
m_pObjectReader = new ELFObjectReader(*this, pLinker);
return true;
}
bool GNULDBackend::initDynObjReader(MCLinker& pLinker)
{
if (NULL == m_pDynObjReader)
m_pDynObjReader = new ELFDynObjReader(*this, pLinker);
return true;
}
bool GNULDBackend::initObjectWriter(MCLinker&)
{
// TODO
return true;
}
bool GNULDBackend::initDynObjWriter(MCLinker& pLinker)
{
if (NULL == m_pDynObjWriter)
m_pDynObjWriter = new ELFDynObjWriter(*this, pLinker);
return true;
}
bool GNULDBackend::initExecWriter(MCLinker& pLinker)
{
if (NULL == m_pExecWriter)
m_pExecWriter = new ELFExecWriter(*this, pLinker);
return true;
}
bool GNULDBackend::initExecSections(MCLinker& pMCLinker)
{
if (NULL == m_pExecFileFormat)
m_pExecFileFormat = new ELFExecFileFormat(*this);
// initialize standard sections
m_pExecFileFormat->initStdSections(pMCLinker);
return true;
}
bool GNULDBackend::initDynObjSections(MCLinker& pMCLinker)
{
if (NULL == m_pDynObjFileFormat)
m_pDynObjFileFormat = new ELFDynObjFileFormat(*this);
// initialize standard sections
m_pDynObjFileFormat->initStdSections(pMCLinker);
return true;
}
bool GNULDBackend::initStandardSymbols(MCLinker& pLinker, const Output& pOutput)
{
ELFFileFormat* file_format = getOutputFormat(pOutput);
// ----- section symbols ----- //
// .preinit_array
FragmentRef* preinit_array = NULL;
if (file_format->hasPreInitArray()) {
preinit_array = pLinker.getLayout().getFragmentRef(
*(file_format->getPreInitArray().getSectionData()->begin()),
0x0);
}
f_pPreInitArrayStart =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__preinit_array_start",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Global,
0x0, // size
0x0, // value
preinit_array, // FragRef
ResolveInfo::Hidden);
f_pPreInitArrayEnd =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__preinit_array_end",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Global,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Hidden);
// .init_array
FragmentRef* init_array = NULL;
if (file_format->hasInitArray()) {
init_array = pLinker.getLayout().getFragmentRef(
*(file_format->getInitArray().getSectionData()->begin()),
0x0);
}
f_pInitArrayStart =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__init_array_start",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Global,
0x0, // size
0x0, // value
init_array, // FragRef
ResolveInfo::Hidden);
f_pInitArrayEnd =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__init_array_end",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Global,
0x0, // size
0x0, // value
init_array, // FragRef
ResolveInfo::Hidden);
// .fini_array
FragmentRef* fini_array = NULL;
if (file_format->hasFiniArray()) {
fini_array = pLinker.getLayout().getFragmentRef(
*(file_format->getFiniArray().getSectionData()->begin()),
0x0);
}
f_pFiniArrayStart =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__fini_array_start",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Global,
0x0, // size
0x0, // value
fini_array, // FragRef
ResolveInfo::Hidden);
f_pFiniArrayEnd =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__fini_array_end",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Global,
0x0, // size
0x0, // value
fini_array, // FragRef
ResolveInfo::Hidden);
// .stack
FragmentRef* stack = NULL;
if (file_format->hasStack()) {
stack = pLinker.getLayout().getFragmentRef(
*(file_format->getStack().getSectionData()->begin()),
0x0);
}
f_pStack =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__stack",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Global,
0x0, // size
0x0, // value
stack, // FragRef
ResolveInfo::Hidden);
// ----- segment symbols ----- //
f_pExecutableStart =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__executable_start",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
f_pEText =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("etext",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
f_p_EText =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("_etext",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
f_p__EText =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("__etext",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
f_pEData =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("edata",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
f_pEnd =
pLinker.defineSymbol<MCLinker::AsRefered,
MCLinker::Resolve>("end",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
// _edata is defined forcefully.
// @ref Google gold linker: defstd.cc: 186
f_p_EData =
pLinker.defineSymbol<MCLinker::Force,
MCLinker::Resolve>("_edata",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
// __bss_start is defined forcefully.
// @ref Google gold linker: defstd.cc: 214
f_pBSSStart =
pLinker.defineSymbol<MCLinker::Force,
MCLinker::Resolve>("__bss_start",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
// _end is defined forcefully.
// @ref Google gold linker: defstd.cc: 228
f_p_End =
pLinker.defineSymbol<MCLinker::Force,
MCLinker::Resolve>("_end",
false, // isDyn
ResolveInfo::NoType,
ResolveInfo::Define,
ResolveInfo::Absolute,
0x0, // size
0x0, // value
NULL, // FragRef
ResolveInfo::Default);
return true;
}
bool
GNULDBackend::finalizeStandardSymbols(MCLinker& pLinker, const Output& pOutput)
{
ELFFileFormat* file_format = getOutputFormat(pOutput);
// ----- section symbols ----- //
if (NULL != f_pPreInitArrayStart) {
if (!f_pPreInitArrayStart->hasFragRef()) {
f_pPreInitArrayStart->resolveInfo()->setBinding(ResolveInfo::Absolute);
f_pPreInitArrayStart->setValue(0x0);
}
}
if (NULL != f_pPreInitArrayEnd) {
if (f_pPreInitArrayEnd->hasFragRef()) {
f_pPreInitArrayEnd->setValue(f_pPreInitArrayEnd->value() +
file_format->getPreInitArray().size());
}
else {
f_pPreInitArrayEnd->resolveInfo()->setBinding(ResolveInfo::Absolute);
f_pPreInitArrayEnd->setValue(0x0);
}
}
if (NULL != f_pInitArrayStart) {
if (!f_pInitArrayStart->hasFragRef()) {
f_pInitArrayStart->resolveInfo()->setBinding(ResolveInfo::Absolute);
f_pInitArrayStart->setValue(0x0);
}
}
if (NULL != f_pInitArrayEnd) {
if (f_pInitArrayEnd->hasFragRef()) {
f_pInitArrayEnd->setValue(f_pInitArrayEnd->value() +
file_format->getInitArray().size());
}
else {
f_pInitArrayEnd->resolveInfo()->setBinding(ResolveInfo::Absolute);
f_pInitArrayEnd->setValue(0x0);
}
}
if (NULL != f_pFiniArrayStart) {
if (!f_pFiniArrayStart->hasFragRef()) {
f_pFiniArrayStart->resolveInfo()->setBinding(ResolveInfo::Absolute);
f_pFiniArrayStart->setValue(0x0);
}
}
if (NULL != f_pFiniArrayEnd) {
if (f_pFiniArrayEnd->hasFragRef()) {
f_pFiniArrayEnd->setValue(f_pFiniArrayEnd->value() +
file_format->getFiniArray().size());
}
else {
f_pFiniArrayEnd->resolveInfo()->setBinding(ResolveInfo::Absolute);
f_pFiniArrayEnd->setValue(0x0);
}
}
if (NULL != f_pStack) {
if (!f_pStack->hasFragRef()) {
f_pStack->resolveInfo()->setBinding(ResolveInfo::Absolute);
f_pStack->setValue(0x0);
}
}
// ----- segment symbols ----- //
if (NULL != f_pExecutableStart) {
ELFSegment* exec_start = m_ELFSegmentTable.find(llvm::ELF::PT_LOAD, 0x0, 0x0);
if (NULL != exec_start) {
if (ResolveInfo::ThreadLocal != f_pExecutableStart->type()) {
f_pExecutableStart->setValue(f_pExecutableStart->value() +
exec_start->vaddr());
}
}
else
f_pExecutableStart->setValue(0x0);
}
if (NULL != f_pEText || NULL != f_p_EText || NULL !=f_p__EText) {
ELFSegment* etext = m_ELFSegmentTable.find(llvm::ELF::PT_LOAD,
llvm::ELF::PF_X,
llvm::ELF::PF_W);
if (NULL != etext) {
if (NULL != f_pEText && ResolveInfo::ThreadLocal != f_pEText->type()) {
f_pEText->setValue(f_pEText->value() +
etext->vaddr() +
etext->memsz());
}
if (NULL != f_p_EText && ResolveInfo::ThreadLocal != f_p_EText->type()) {
f_p_EText->setValue(f_p_EText->value() +
etext->vaddr() +
etext->memsz());
}
if (NULL != f_p__EText && ResolveInfo::ThreadLocal != f_p__EText->type()) {
f_p__EText->setValue(f_p__EText->value() +
etext->vaddr() +
etext->memsz());
}
}
else {
if (NULL != f_pEText)
f_pEText->setValue(0x0);
if (NULL != f_p_EText)
f_p_EText->setValue(0x0);
if (NULL != f_p__EText)
f_p__EText->setValue(0x0);
}
}
if (NULL != f_pEData || NULL != f_p_EData || NULL != f_pBSSStart ||
NULL != f_pEnd || NULL != f_p_End) {
ELFSegment* edata = m_ELFSegmentTable.find(llvm::ELF::PT_LOAD,
llvm::ELF::PF_W,
0x0);
if (NULL != edata) {
if (NULL != f_pEData && ResolveInfo::ThreadLocal != f_pEData->type()) {
f_pEData->setValue(f_pEData->value() +
edata->vaddr() +
edata->filesz());
}
if (NULL != f_p_EData && ResolveInfo::ThreadLocal != f_p_EData->type()) {
f_p_EData->setValue(f_p_EData->value() +
edata->vaddr() +
edata->filesz());
}
if (NULL != f_pBSSStart && ResolveInfo::ThreadLocal != f_pBSSStart->type()) {
f_pBSSStart->setValue(f_pBSSStart->value() +
edata->vaddr() +
edata->filesz());
}
if (NULL != f_pEnd && ResolveInfo::ThreadLocal != f_pEnd->type()) {
f_pEnd->setValue(f_pEnd->value() +
edata->vaddr() +
edata->memsz());
}
if (NULL != f_p_End && ResolveInfo::ThreadLocal != f_p_End->type()) {
f_p_End->setValue(f_p_End->value() +
edata->vaddr() +
edata->memsz());
}
}
else {
if (NULL != f_pEData)
f_pEData->setValue(0x0);
if (NULL != f_p_EData)
f_p_EData->setValue(0x0);
if (NULL != f_pBSSStart)
f_pBSSStart->setValue(0x0);
if (NULL != f_pEnd)
f_pEnd->setValue(0x0);
if (NULL != f_p_End)
f_p_End->setValue(0x0);
}
}
return true;
}
GNUArchiveReader *GNULDBackend::getArchiveReader()
{
assert(NULL != m_pArchiveReader);
return m_pArchiveReader;
}
const GNUArchiveReader *GNULDBackend::getArchiveReader() const
{
assert(NULL != m_pArchiveReader);
return m_pArchiveReader;
}
ELFObjectReader *GNULDBackend::getObjectReader()
{
assert(NULL != m_pObjectReader);
return m_pObjectReader;
}
const ELFObjectReader *GNULDBackend::getObjectReader() const
{
assert(NULL != m_pObjectReader);
return m_pObjectReader;
}
ELFDynObjReader *GNULDBackend::getDynObjReader()
{
assert(NULL != m_pDynObjReader);
return m_pDynObjReader;
}
const ELFDynObjReader *GNULDBackend::getDynObjReader() const
{
assert(NULL != m_pDynObjReader);
return m_pDynObjReader;
}
ELFObjectWriter *GNULDBackend::getObjectWriter()
{
// TODO
return NULL;
}
const ELFObjectWriter *GNULDBackend::getObjectWriter() const
{
// TODO
return NULL;
}
ELFDynObjWriter *GNULDBackend::getDynObjWriter()
{
assert(NULL != m_pDynObjWriter);
return m_pDynObjWriter;
}
const ELFDynObjWriter *GNULDBackend::getDynObjWriter() const
{
assert(NULL != m_pDynObjWriter);
return m_pDynObjWriter;
}
ELFExecWriter *GNULDBackend::getExecWriter()
{
assert(NULL != m_pExecWriter);
return m_pExecWriter;
}
const ELFExecWriter *GNULDBackend::getExecWriter() const
{
assert(NULL != m_pExecWriter);
return m_pExecWriter;
}
ELFFileFormat* GNULDBackend::getOutputFormat(const Output& pOutput)
{
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:
fatal(diag::unrecognized_output_file) << pOutput.type();
return NULL;
}
}
const ELFFileFormat* GNULDBackend::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:
fatal(diag::unrecognized_output_file) << pOutput.type();
return NULL;
}
}
ELFDynObjFileFormat* GNULDBackend::getDynObjFileFormat()
{
assert(NULL != m_pDynObjFileFormat);
return m_pDynObjFileFormat;
}
const ELFDynObjFileFormat* GNULDBackend::getDynObjFileFormat() const
{
assert(NULL != m_pDynObjFileFormat);
return m_pDynObjFileFormat;
}
ELFExecFileFormat* GNULDBackend::getExecFileFormat()
{
assert(NULL != m_pExecFileFormat);
return m_pExecFileFormat;
}
const ELFExecFileFormat* GNULDBackend::getExecFileFormat() const
{
assert(NULL != m_pExecFileFormat);
return m_pExecFileFormat;
}
/// sizeNamePools - compute the size of regular name pools
/// In ELF executable files, regular name pools are .symtab, .strtab,
/// .dynsym, .dynstr, and .hash
void
GNULDBackend::sizeNamePools(const Output& pOutput,
const SymbolCategory& pSymbols,
const MCLDInfo& pLDInfo)
{
// size of string tables starts from 1 to hold the null character in their
// first byte
size_t symtab = 1;
size_t dynsym = 1;
// number of entries in symbol tables starts from 1 to hold the special entry
// at index 0 (STN_UNDEF). See ELF Spec Book I, p1-21.
size_t strtab = 1;
size_t dynstr = 1;
size_t hash = 0;
// compute size of .symtab, .dynsym and .strtab
SymbolCategory::const_iterator symbol;
SymbolCategory::const_iterator symEnd = pSymbols.end();
for (symbol = pSymbols.begin(); symbol != symEnd; ++symbol) {
size_t str_size = (*symbol)->nameSize() + 1;
if (isDynamicSymbol(**symbol, pOutput)) {
++dynsym;
dynstr += str_size;
}
++symtab;
strtab += str_size;
}
ELFFileFormat* file_format = getOutputFormat(pOutput);
switch(pOutput.type()) {
// compute size of .dynstr and .hash
case Output::DynObj:
case Output::Exec: {
// add DT_NEED strings into .dynstr and .dynamic
// Rules:
// 1. ignore --no-add-needed
// 2. force count in --no-as-needed
// 3. judge --as-needed
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()) {
dynstr += (*input)->name().size() + 1;
dynamic().reserveNeedEntry();
}
// --as-needed
else if ((*input)->isNeeded()) {
dynstr += (*input)->name().size() + 1;
dynamic().reserveNeedEntry();
}
}
}
} // for
// compute .hash
// Both Elf32_Word and Elf64_Word are 4 bytes
hash = (2 + getHashBucketCount(dynsym, false) + dynsym) *
sizeof(llvm::ELF::Elf32_Word);
// set size
dynstr += pOutput.name().size() + 1;
if (32 == bitclass())
file_format->getDynSymTab().setSize(dynsym*sizeof(llvm::ELF::Elf32_Sym));
else
file_format->getDynSymTab().setSize(dynsym*sizeof(llvm::ELF::Elf64_Sym));
file_format->getDynStrTab().setSize(dynstr);
file_format->getHashTab().setSize(hash);
}
/* fall through */
case Output::Object: {
if (32 == bitclass())
file_format->getSymTab().setSize(symtab*sizeof(llvm::ELF::Elf32_Sym));
else
file_format->getSymTab().setSize(symtab*sizeof(llvm::ELF::Elf64_Sym));
file_format->getStrTab().setSize(strtab);
break;
}
} // end of switch
// reserve fixed entries in the .dynamic section.
if (Output::DynObj == pOutput.type() || Output::Exec == pOutput.type()) {
// Because some entries in .dynamic section need information of .dynsym,
// .dynstr, .symtab, .strtab and .hash, we can not reserve non-DT_NEEDED
// entries until we get the size of the sections mentioned above
dynamic().reserveEntries(pLDInfo, *file_format);
file_format->getDynamic().setSize(dynamic().numOfBytes());
}
}
/// emitRegNamePools - emit regular name pools - .symtab, .strtab
///
/// the size of these tables should be computed before layout
/// layout should computes the start offset of these tables
void GNULDBackend::emitRegNamePools(Output& pOutput,
SymbolCategory& pSymbols,
const Layout& pLayout,
const MCLDInfo& pLDInfo)
{
assert(pOutput.hasMemArea());
bool sym_exist = false;
HashTableType::entry_type* entry = 0;
ELFFileFormat* file_format = getOutputFormat(pOutput);
if (pOutput.type() == Output::Object) {
// add first symbol into m_pSymIndexMap
entry = m_pSymIndexMap->insert(NULL, sym_exist);
entry->setValue(0);
// TODO: not support yet
return;
}
LDSection& symtab_sect = file_format->getSymTab();
LDSection& strtab_sect = file_format->getStrTab();
MemoryRegion* symtab_region = pOutput.memArea()->request(symtab_sect.offset(),
symtab_sect.size());
MemoryRegion* strtab_region = pOutput.memArea()->request(strtab_sect.offset(),
strtab_sect.size());
// set up symtab_region
llvm::ELF::Elf32_Sym* symtab32 = NULL;
llvm::ELF::Elf64_Sym* symtab64 = NULL;
if (32 == bitclass())
symtab32 = (llvm::ELF::Elf32_Sym*)symtab_region->start();
else if (64 == bitclass())
symtab64 = (llvm::ELF::Elf64_Sym*)symtab_region->start();
else
llvm::report_fatal_error(llvm::Twine("unsupported bitclass ") +
llvm::Twine(bitclass()) +
llvm::Twine(".\n"));
// set up strtab_region
char* strtab = (char*)strtab_region->start();
strtab[0] = '\0';
// initialize the first ELF symbol
if (32 == bitclass()) {
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;
}
else { // must 64
symtab64[0].st_name = 0;
symtab64[0].st_value = 0;
symtab64[0].st_size = 0;
symtab64[0].st_info = 0;
symtab64[0].st_other = 0;
symtab64[0].st_shndx = 0;
}
size_t symtabIdx = 1;
size_t strtabsize = 1;
// compute size of .symtab, .dynsym and .strtab
SymbolCategory::iterator symbol;
SymbolCategory::iterator symEnd = pSymbols.end();
for (symbol = pSymbols.begin(); symbol != symEnd; ++symbol) {
// maintain output's symbol and index map if building .o file
if (Output::Object == pOutput.type()) {
entry = m_pSymIndexMap->insert(NULL, sym_exist);
entry->setValue(symtabIdx);
}
// FIXME: check the endian between host and target
// write out symbol
if (32 == bitclass()) {
symtab32[symtabIdx].st_name = strtabsize;
symtab32[symtabIdx].st_value = getSymbolValue(**symbol);
symtab32[symtabIdx].st_size = getSymbolSize(**symbol);
symtab32[symtabIdx].st_info = getSymbolInfo(**symbol);
symtab32[symtabIdx].st_other = (*symbol)->visibility();
symtab32[symtabIdx].st_shndx = getSymbolShndx(**symbol, pLayout);
}
else { // must 64
symtab64[symtabIdx].st_name = strtabsize;
symtab64[symtabIdx].st_value = getSymbolValue(**symbol);
symtab64[symtabIdx].st_size = getSymbolSize(**symbol);
symtab64[symtabIdx].st_info = getSymbolInfo(**symbol);
symtab64[symtabIdx].st_other = (*symbol)->visibility();
symtab64[symtabIdx].st_shndx = getSymbolShndx(**symbol, pLayout);
}
// write out string
strcpy((strtab + strtabsize), (*symbol)->name());
// write out
// sum up counters
++symtabIdx;
strtabsize += (*symbol)->nameSize() + 1;
}
}
/// 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 GNULDBackend::emitDynNamePools(Output& pOutput,
SymbolCategory& pSymbols,
const Layout& pLayout,
const MCLDInfo& pLDInfo)
{
assert(pOutput.hasMemArea());
ELFFileFormat* file_format = getOutputFormat(pOutput);
bool sym_exist = false;
HashTableType::entry_type* entry = 0;
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;
llvm::ELF::Elf64_Sym* symtab64 = NULL;
if (32 == bitclass())
symtab32 = (llvm::ELF::Elf32_Sym*)symtab_region->start();
else if (64 == bitclass())
symtab64 = (llvm::ELF::Elf64_Sym*)symtab_region->start();
else
llvm::report_fatal_error(llvm::Twine("unsupported bitclass ") +
llvm::Twine(bitclass()) +
llvm::Twine(".\n"));
// initialize the first ELF symbol
if (32 == bitclass()) {
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;
}
else { // must 64
symtab64[0].st_name = 0;
symtab64[0].st_value = 0;
symtab64[0].st_size = 0;
symtab64[0].st_info = 0;
symtab64[0].st_other = 0;
symtab64[0].st_shndx = 0;
}
// set up strtab_region
char* strtab = (char*)strtab_region->start();
strtab[0] = '\0';
// add the first symbol into m_pSymIndexMap
entry = m_pSymIndexMap->insert(NULL, sym_exist);
entry->setValue(0);
size_t symtabIdx = 1;
size_t strtabsize = 1;
// emit of .dynsym, and .dynstr
SymbolCategory::iterator symbol;
SymbolCategory::iterator symEnd = pSymbols.end();
for (symbol = pSymbols.begin(); symbol != symEnd; ++symbol) {
if (!isDynamicSymbol(**symbol, pOutput))
continue;
// maintain output's symbol and index map
entry = m_pSymIndexMap->insert(*symbol, sym_exist);
entry->setValue(symtabIdx);
// FIXME: check the endian between host and target
// write out symbol
if (32 == bitclass()) {
symtab32[symtabIdx].st_name = strtabsize;
symtab32[symtabIdx].st_value = (*symbol)->value();
symtab32[symtabIdx].st_size = getSymbolSize(**symbol);
symtab32[symtabIdx].st_info = getSymbolInfo(**symbol);
symtab32[symtabIdx].st_other = (*symbol)->visibility();
symtab32[symtabIdx].st_shndx = getSymbolShndx(**symbol, pLayout);
}
else { // must 64
symtab64[symtabIdx].st_name = strtabsize;
symtab64[symtabIdx].st_value = (*symbol)->value();
symtab64[symtabIdx].st_size = getSymbolSize(**symbol);
symtab64[symtabIdx].st_info = getSymbolInfo(**symbol);
symtab64[symtabIdx].st_other = (*symbol)->visibility();
symtab64[symtabIdx].st_shndx = getSymbolShndx(**symbol, pLayout);
}
// write out string
strcpy((strtab + strtabsize), (*symbol)->name());
// 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
if (Output::DynObj == pOutput.type())
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;
if (32 == bitclass()) {
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;
}
}
else if (64 == bitclass()) {
for (size_t sym_idx=0; sym_idx < symtabIdx; ++sym_idx) {
llvm::StringRef name(strtab + symtab64[sym_idx].st_name);
size_t bucket_pos = hash_func(name) % nbucket;
chain[sym_idx] = bucket[bucket_pos];
bucket[bucket_pos] = sym_idx;
}
}
}
/// sizeInterp - compute the size of the .interp section
void GNULDBackend::sizeInterp(const Output& pOutput, const MCLDInfo& pLDInfo)
{
assert(pOutput.type() == Output::Exec);
const char* dyld_name;
if (pLDInfo.options().hasDyld())
dyld_name = pLDInfo.options().dyld().c_str();
else
dyld_name = dyld();
LDSection& interp = getExecFileFormat()->getInterp();
interp.setSize(std::strlen(dyld_name) + 1);
}
/// emitInterp - emit the .interp
void GNULDBackend::emitInterp(Output& pOutput, const MCLDInfo& pLDInfo)
{
assert(pOutput.type() == Output::Exec &&
getExecFileFormat()->hasInterp() &&
pOutput.hasMemArea());
const LDSection& interp = getExecFileFormat()->getInterp();
MemoryRegion *region = pOutput.memArea()->request(
interp.offset(), interp.size());
const char* dyld_name;
if (pLDInfo.options().hasDyld())
dyld_name = pLDInfo.options().dyld().c_str();
else
dyld_name = dyld();
std::memcpy(region->start(), dyld_name, interp.size());
}
/// getSectionOrder
unsigned int GNULDBackend::getSectionOrder(const Output& pOutput,
const LDSection& pSectHdr,
const MCLDInfo& pInfo) const
{
// NULL section should be the "1st" section
if (LDFileFormat::Null == pSectHdr.kind())
return 0;
// if the section is not ALLOC, lay it out until the last possible moment
if (0 == (pSectHdr.flag() & llvm::ELF::SHF_ALLOC))
return SHO_UNDEFINED;
bool is_write = (pSectHdr.flag() & llvm::ELF::SHF_WRITE) != 0;
bool is_exec = (pSectHdr.flag() & llvm::ELF::SHF_EXECINSTR) != 0;
const ELFFileFormat* file_format = getOutputFormat(pOutput);
// TODO: need to take care other possible output sections
switch (pSectHdr.kind()) {
case LDFileFormat::Regular:
if (is_exec) {
if (&pSectHdr == &file_format->getInit())
return SHO_INIT;
if (&pSectHdr == &file_format->getFini())
return SHO_FINI;
return SHO_TEXT;
} else if (!is_write) {
return SHO_RO;
} else {
if (pInfo.options().hasRelro()) {
if (pSectHdr.type() == llvm::ELF::SHT_PREINIT_ARRAY ||
pSectHdr.type() == llvm::ELF::SHT_INIT_ARRAY ||
pSectHdr.type() == llvm::ELF::SHT_FINI_ARRAY ||
&pSectHdr == &file_format->getCtors() ||
&pSectHdr == &file_format->getDtors() ||
&pSectHdr == &file_format->getJCR() ||
0 == pSectHdr.name().compare(".data.rel.ro"))
return SHO_RELRO;
if (0 == pSectHdr.name().compare(".data.rel.ro.local"))
return SHO_RELRO_LOCAL;
}
return SHO_DATA;
}
case LDFileFormat::BSS:
return SHO_BSS;
case LDFileFormat::NamePool:
if (&pSectHdr == &file_format->getDynamic())
return SHO_RELRO;
return SHO_NAMEPOOL;
case LDFileFormat::Relocation:
if (&pSectHdr == &file_format->getRelPlt() ||
&pSectHdr == &file_format->getRelaPlt())
return SHO_REL_PLT;
return SHO_RELOCATION;
// get the order from target for target specific sections
case LDFileFormat::Target:
return getTargetSectionOrder(pOutput, pSectHdr, pInfo);
// handle .interp
case LDFileFormat::Note:
return SHO_INTERP;
case LDFileFormat::EhFrame:
case LDFileFormat::EhFrameHdr:
case LDFileFormat::GCCExceptTable:
return SHO_EXCEPTION;
case LDFileFormat::MetaData:
case LDFileFormat::Debug:
default:
return SHO_UNDEFINED;
}
}
/// getSymbolSize
uint64_t GNULDBackend::getSymbolSize(const LDSymbol& pSymbol) const
{
// @ref Google gold linker: symtab.cc: 2780
// undefined and dynamic symbols should have zero size.
if (pSymbol.isDyn() || pSymbol.desc() == ResolveInfo::Undefined)
return 0x0;
return pSymbol.resolveInfo()->size();
}
/// getSymbolInfo
uint64_t GNULDBackend::getSymbolInfo(const LDSymbol& pSymbol) const
{
// set binding
uint8_t bind = 0x0;
if (pSymbol.resolveInfo()->isLocal())
bind = llvm::ELF::STB_LOCAL;
else if (pSymbol.resolveInfo()->isGlobal())
bind = llvm::ELF::STB_GLOBAL;
else if (pSymbol.resolveInfo()->isWeak())
bind = llvm::ELF::STB_WEAK;
else if (pSymbol.resolveInfo()->isAbsolute()) {
// (Luba) Is a absolute but not global (weak or local) symbol meaningful?
bind = llvm::ELF::STB_GLOBAL;
}
if (pSymbol.visibility() == llvm::ELF::STV_INTERNAL ||
pSymbol.visibility() == llvm::ELF::STV_HIDDEN)
bind = llvm::ELF::STB_LOCAL;
uint32_t type = pSymbol.resolveInfo()->type();
// if the IndirectFunc symbol (i.e., STT_GNU_IFUNC) is from dynobj, change
// its type to Function
if (type == ResolveInfo::IndirectFunc && pSymbol.isDyn())
type = ResolveInfo::Function;
return (type | (bind << 4));
}
/// getSymbolValue - this function is called after layout()
uint64_t GNULDBackend::getSymbolValue(const LDSymbol& pSymbol) const
{
if (pSymbol.isDyn())
return 0x0;
return pSymbol.value();
}
/// getSymbolShndx - this function is called after layout()
uint64_t
GNULDBackend::getSymbolShndx(const LDSymbol& pSymbol, const Layout& pLayout) const
{
if (pSymbol.resolveInfo()->isAbsolute())
return llvm::ELF::SHN_ABS;
if (pSymbol.resolveInfo()->isCommon())
return llvm::ELF::SHN_COMMON;
if (pSymbol.resolveInfo()->isUndef() || pSymbol.isDyn())
return llvm::ELF::SHN_UNDEF;
if (pSymbol.resolveInfo()->isLocal()) {
switch (pSymbol.type()) {
case ResolveInfo::NoType:
case ResolveInfo::File:
return llvm::ELF::SHN_ABS;
}
}
assert(pSymbol.hasFragRef() && "symbols must have fragment reference to get its index");
return pLayout.getOutputLDSection(*pSymbol.fragRef()->frag())->index();
}
/// getSymbolIdx - called by emitRelocation to get the ouput symbol table index
size_t GNULDBackend::getSymbolIdx(LDSymbol* pSymbol) const
{
HashTableType::iterator entry = m_pSymIndexMap->find(pSymbol);
return entry.getEntry()->value();
}
/// allocateCommonSymbols - allocate common symbols in the corresponding
/// sections.
/// @refer Google gold linker: common.cc: 214
bool
GNULDBackend::allocateCommonSymbols(const MCLDInfo& pInfo, MCLinker& pLinker) const
{
SymbolCategory& symbol_list = pLinker.getOutputSymbols();
if (symbol_list.emptyCommons() && symbol_list.emptyLocals())
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 or create corresponding BSS LDSection
LDSection* bss_sect = &pLinker.getOrCreateOutputSectHdr(".bss",
LDFileFormat::BSS,
llvm::ELF::SHT_NOBITS,
llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC);
LDSection* tbss_sect = &pLinker.getOrCreateOutputSectHdr(
".tbss",
LDFileFormat::BSS,
llvm::ELF::SHT_NOBITS,
llvm::ELF::SHF_WRITE | llvm::ELF::SHF_ALLOC);
assert(NULL != bss_sect && NULL !=tbss_sect);
// get or create corresponding BSS SectionData
SectionData& bss_sect_data = pLinker.getOrCreateSectData(*bss_sect);
SectionData& tbss_sect_data = pLinker.getOrCreateSectData(*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());
(*com_sym)->setFragmentRef(new FragmentRef(*frag, 0));
if (ResolveInfo::ThreadLocal == (*com_sym)->type()) {
// allocate TLS common symbol in tbss section
tbss_offset += pLinker.getLayout().appendFragment(*frag,
tbss_sect_data,
(*com_sym)->value());
}
else {
bss_offset += pLinker.getLayout().appendFragment(*frag,
bss_sect_data,
(*com_sym)->value());
}
}
}
// 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());
(*com_sym)->setFragmentRef(new FragmentRef(*frag, 0));
if (ResolveInfo::ThreadLocal == (*com_sym)->type()) {
// allocate TLS common symbol in tbss section
tbss_offset += pLinker.getLayout().appendFragment(*frag,
tbss_sect_data,
(*com_sym)->value());
}
else {
bss_offset += pLinker.getLayout().appendFragment(*frag,
bss_sect_data,
(*com_sym)->value());
}
}
bss_sect->setSize(bss_offset);
tbss_sect->setSize(tbss_offset);
symbol_list.changeCommonsToGlobal();
return true;
}
/// createProgramHdrs - base on output sections to create the program headers
void GNULDBackend::createProgramHdrs(Output& pOutput, const MCLDInfo& pInfo)
{
assert(pOutput.hasContext());
ELFFileFormat *file_format = getOutputFormat(pOutput);
// make PT_PHDR
m_ELFSegmentTable.produce(llvm::ELF::PT_PHDR);
// make PT_INTERP
if (file_format->hasInterp()) {
ELFSegment* interp_seg = m_ELFSegmentTable.produce(llvm::ELF::PT_INTERP);
interp_seg->addSection(&file_format->getInterp());
}
// FIXME: Should we consider -z relro here?
if (pInfo.options().hasRelro()) {
// if -z relro is given, we need to adjust sections' offset again, and let
// PT_GNU_RELRO end on a common page boundary
LDContext::SectionTable& sect_table = pOutput.context()->getSectionTable();
size_t idx;
for (idx = 0; idx < pOutput.context()->numOfSections(); ++idx) {
// find the first non-relro section
if (getSectionOrder(pOutput, *sect_table[idx], pInfo) > SHO_RELRO_LAST) {
break;
}
}
// align the first non-relro section to page boundary
uint64_t offset = sect_table[idx]->offset();
alignAddress(offset, commonPageSize(pInfo));
sect_table[idx]->setOffset(offset);
// set up remaining section's offset
for (++idx; idx < pOutput.context()->numOfSections(); ++idx) {
uint64_t offset;
size_t prev_idx = idx - 1;
if (LDFileFormat::BSS == sect_table[prev_idx]->kind())
offset = sect_table[prev_idx]->offset();
else
offset = sect_table[prev_idx]->offset() + sect_table[prev_idx]->size();
alignAddress(offset, sect_table[idx]->align());
sect_table[idx]->setOffset(offset);
}
} // relro
uint32_t cur_seg_flag, prev_seg_flag = getSegmentFlag(0);
uint64_t padding = 0;
ELFSegment* load_seg = NULL;
// make possible PT_LOAD segments
LDContext::sect_iterator sect, sect_end = pOutput.context()->sectEnd();
for (sect = pOutput.context()->sectBegin(); sect != sect_end; ++sect) {
if (0 == ((*sect)->flag() & llvm::ELF::SHF_ALLOC) &&
LDFileFormat::Null != (*sect)->kind())
continue;
// FIXME: Now only separate writable and non-writable PT_LOAD
cur_seg_flag = getSegmentFlag((*sect)->flag());
if ((prev_seg_flag & llvm::ELF::PF_W) ^ (cur_seg_flag & llvm::ELF::PF_W) ||
LDFileFormat::Null == (*sect)->kind()) {
// create new PT_LOAD segment
load_seg = m_ELFSegmentTable.produce(llvm::ELF::PT_LOAD);
load_seg->setAlign(abiPageSize(pInfo));
// check if this segment needs padding
padding = 0;
if (((*sect)->offset() & (abiPageSize(pInfo) - 1)) != 0)
padding = abiPageSize(pInfo);
}
assert(NULL != load_seg);
load_seg->addSection((*sect));
if (cur_seg_flag != prev_seg_flag)
load_seg->updateFlag(cur_seg_flag);
if (LDFileFormat::Null != (*sect)->kind())
(*sect)->setAddr(segmentStartAddr(pOutput, pInfo) +
(*sect)->offset() +
padding);
prev_seg_flag = cur_seg_flag;
}
// make PT_DYNAMIC
if (file_format->hasDynamic()) {
ELFSegment* dyn_seg = m_ELFSegmentTable.produce(llvm::ELF::PT_DYNAMIC,
llvm::ELF::PF_R |
llvm::ELF::PF_W);
dyn_seg->addSection(&file_format->getDynamic());
}
if (pInfo.options().hasRelro()) {
// make PT_GNU_RELRO
ELFSegment* relro_seg = m_ELFSegmentTable.produce(llvm::ELF::PT_GNU_RELRO);
for (LDContext::sect_iterator sect = pOutput.context()->sectBegin();
sect != pOutput.context()->sectEnd(); ++sect) {
unsigned int order = getSectionOrder(pOutput, **sect, pInfo);
if (SHO_RELRO_LOCAL == order ||
SHO_RELRO == order ||
SHO_RELRO_LAST == order) {
relro_seg->addSection(*sect);
}
}
}
// make PT_GNU_EH_FRAME
if (file_format->hasEhFrameHdr()) {
ELFSegment* eh_seg = m_ELFSegmentTable.produce(llvm::ELF::PT_GNU_EH_FRAME);
eh_seg->addSection(&file_format->getEhFrameHdr());
}
}
/// setupProgramHdrs - set up the attributes of segments
void GNULDBackend:: setupProgramHdrs(const Output& pOutput, const MCLDInfo& pInfo)
{
// update segment info
ELFSegmentFactory::iterator seg, seg_end = m_ELFSegmentTable.end();
for (seg = m_ELFSegmentTable.begin(); seg != seg_end; ++seg) {
ELFSegment& segment = *seg;
// update PT_PHDR
if (llvm::ELF::PT_PHDR == segment.type()) {
uint64_t offset, phdr_size;
if (32 == bitclass()) {
offset = sizeof(llvm::ELF::Elf32_Ehdr);
phdr_size = sizeof(llvm::ELF::Elf32_Phdr);
}
else {
offset = sizeof(llvm::ELF::Elf64_Ehdr);
phdr_size = sizeof(llvm::ELF::Elf64_Phdr);
}
segment.setOffset(offset);
segment.setVaddr(segmentStartAddr(pOutput, pInfo) + offset);
segment.setPaddr(segment.vaddr());
segment.setFilesz(numOfSegments() * phdr_size);
segment.setMemsz(numOfSegments() * phdr_size);
segment.setAlign(bitclass() / 8);
continue;
}
// bypass if there is no section in this segment (e.g., PT_GNU_STACK)
if (segment.numOfSections() == 0)
continue;
segment.setOffset(segment.getFirstSection()->offset());
if (llvm::ELF::PT_LOAD == segment.type() &&
LDFileFormat::Null == segment.getFirstSection()->kind())
segment.setVaddr(segmentStartAddr(pOutput, pInfo));
else
segment.setVaddr(segment.getFirstSection()->addr());
segment.setPaddr(segment.vaddr());
const LDSection* last_sect = segment.getLastSection();
assert(NULL != last_sect);
uint64_t file_size = last_sect->offset() - segment.offset();
if (LDFileFormat::BSS != last_sect->kind())
file_size += last_sect->size();
segment.setFilesz(file_size);
segment.setMemsz(last_sect->addr() - segment.vaddr() + last_sect->size());
}
}
/// createGNUStackInfo - create an output GNU stack section or segment if needed
/// @ref gold linker: layout.cc:2608
void GNULDBackend::createGNUStackInfo(const Output& pOutput,
const MCLDInfo& pInfo,
MCLinker& pLinker)
{
uint32_t flag = 0x0;
if (pInfo.options().hasStackSet()) {
// 1. check the command line option (-z execstack or -z noexecstack)
if (pInfo.options().hasExecStack())
flag = llvm::ELF::SHF_EXECINSTR;
} else {
// 2. check the stack info from the input objects
size_t object_count = 0, stack_note_count = 0;
mcld::InputTree::const_bfs_iterator input, inEnd = pInfo.inputs().bfs_end();
for (input=pInfo.inputs().bfs_begin(); input!=inEnd; ++input) {
if ((*input)->type() == Input::Object) {
++object_count;
const LDSection* sect = (*input)->context()->getSection(
".note.GNU-stack");
if (NULL != sect) {
++stack_note_count;
// 2.1 found a stack note that is set as executable
if (0 != (llvm::ELF::SHF_EXECINSTR & sect->flag())) {
flag = llvm::ELF::SHF_EXECINSTR;
break;
}
}
}
}
// 2.2 there are no stack note sections in all input objects
if (0 == stack_note_count)
return;
// 2.3 a special case. Use the target default to decide if the stack should
// be executable
if (llvm::ELF::SHF_EXECINSTR != flag && object_count != stack_note_count)
if (isDefaultExecStack())
flag = llvm::ELF::SHF_EXECINSTR;
}
if (pOutput.type() != Output::Object)
m_ELFSegmentTable.produce(llvm::ELF::PT_GNU_STACK,
llvm::ELF::PF_R |
llvm::ELF::PF_W |
getSegmentFlag(flag));
else
pLinker.getOrCreateOutputSectHdr(".note.GNU-stack",
LDFileFormat::Note,
llvm::ELF::SHT_PROGBITS,
flag);
}
/// preLayout - Backend can do any needed modification before layout
void GNULDBackend::preLayout(const Output& pOutput,
const MCLDInfo& pLDInfo,
MCLinker& pLinker)
{
// prelayout target first
doPreLayout(pOutput, pLDInfo, pLinker);
if (pLDInfo.options().hasEhFrameHdr()) {
// init EhFrameHdr and size the output section
ELFFileFormat* format = getOutputFormat(pOutput);
assert(NULL != getEhFrame());
m_pEhFrameHdr = new EhFrameHdr(*getEhFrame(),
format->getEhFrame(),
format->getEhFrameHdr());
m_pEhFrameHdr->sizeOutput();
}
}
/// postLayout - Backend can do any needed modification after layout
void GNULDBackend::postLayout(const Output& pOutput,
const MCLDInfo& pInfo,
MCLinker& pLinker)
{
// 1. emit program headers
if (pOutput.type() != Output::Object) {
// 1.1 create program headers
createProgramHdrs(pLinker.getLDInfo().output(), pInfo);
}
// 1.2 create special GNU Stack note section or segment
createGNUStackInfo(pOutput, pInfo, pLinker);
if (pOutput.type() != Output::Object) {
// 1.3 set up the attributes of program headers
setupProgramHdrs(pOutput, pInfo);
}
// 2. target specific post layout
doPostLayout(pOutput, pInfo, pLinker);
}
void GNULDBackend::postProcessing(const Output& pOutput,
const MCLDInfo& pInfo,
MCLinker& pLinker)
{
if (pInfo.options().hasEhFrameHdr()) {
// emit eh_frame_hdr
if (bitclass() == 32)
m_pEhFrameHdr->emitOutput<32>(pLinker.getLDInfo().output(),
pLinker);
}
}
/// getHashBucketCount - calculate hash bucket count.
/// @ref Google gold linker, dynobj.cc:791
unsigned GNULDBackend::getHashBucketCount(unsigned pNumOfSymbols,
bool pIsGNUStyle)
{
// @ref Google gold, dynobj.cc:loc 791
static const unsigned int buckets[] =
{
1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209,
16411, 32771, 65537, 131101, 262147
};
const unsigned buckets_count = sizeof buckets / sizeof buckets[0];
unsigned int result = 1;
for (unsigned i = 0; i < buckets_count; ++i) {
if (pNumOfSymbols < buckets[i])
break;
result = buckets[i];
}
if (pIsGNUStyle && result < 2)
result = 2;
return result;
}
/// isDynamicSymbol
/// @ref Google gold linker: symtab.cc:311
bool GNULDBackend::isDynamicSymbol(const LDSymbol& pSymbol,
const Output& pOutput)
{
// If a local symbol is in the LDContext's symbol table, it's a real local
// symbol. We should not add it
if (pSymbol.binding() == ResolveInfo::Local)
return false;
// If we are building shared object, and the visibility is external, we
// need to add it.
if (Output::DynObj == pOutput.type() || Output::Exec == pOutput.type())
if (pSymbol.resolveInfo()->visibility() == ResolveInfo::Default ||
pSymbol.resolveInfo()->visibility() == ResolveInfo::Protected)
return true;
return false;
}
/// commonPageSize - the common page size of the target machine.
/// @ref gold linker: target.h:135
uint64_t GNULDBackend::commonPageSize(const MCLDInfo& pInfo) const
{
if (pInfo.options().commPageSize() > 0)
return std::min(pInfo.options().commPageSize(), abiPageSize(pInfo));
else
return std::min(static_cast<uint64_t>(0x1000), abiPageSize(pInfo));
}
/// abiPageSize - the abi page size of the target machine.
/// @ref gold linker: target.h:125
uint64_t GNULDBackend::abiPageSize(const MCLDInfo& pInfo) const
{
if (pInfo.options().maxPageSize() > 0)
return pInfo.options().maxPageSize();
else
return static_cast<uint64_t>(0x1000);
}
/// isOutputPIC - return whether the output is position-independent
bool GNULDBackend::isOutputPIC(const Output& pOutput,
const MCLDInfo& pInfo) const
{
if (Output::DynObj == pOutput.type() || pInfo.options().isPIE())
return true;
return false;
}
/// isStaticLink - return whether we're doing static link
bool GNULDBackend::isStaticLink(const Output& pOutput,
const MCLDInfo& pInfo) const
{
InputTree::const_iterator it = pInfo.inputs().begin();
if (!isOutputPIC(pOutput, pInfo) && (*it)->attribute()->isStatic())
return true;
return false;
}
/// isSymbolPreemtible - whether the symbol can be preemted by other
/// link unit
/// @ref Google gold linker, symtab.h:551
bool GNULDBackend::isSymbolPreemptible(const ResolveInfo& pSym,
const MCLDInfo& pLDInfo,
const Output& pOutput) const
{
if (pSym.other() != ResolveInfo::Default)
return false;
if (Output::DynObj != pOutput.type())
return false;
if (pLDInfo.options().Bsymbolic())
return false;
return true;
}
/// symbolNeedsPLT - return whether the symbol needs a PLT entry
/// @ref Google gold linker, symtab.h:596
bool GNULDBackend::symbolNeedsPLT(const ResolveInfo& pSym,
const MCLDInfo& pLDInfo,
const Output& pOutput) const
{
if (pSym.isUndef() && !pSym.isDyn() && pOutput.type() != Output::DynObj)
return false;
// An IndirectFunc symbol (i.e., STT_GNU_IFUNC) always needs a plt entry
if (pSym.type() == ResolveInfo::IndirectFunc)
return true;
if (pSym.type() != ResolveInfo::Function)
return false;
if (isStaticLink(pOutput, pLDInfo) || pLDInfo.options().isPIE())
return false;
return (pSym.isDyn() ||
pSym.isUndef() ||
isSymbolPreemptible(pSym, pLDInfo, pOutput));
}
/// symbolNeedsDynRel - return whether the symbol needs a dynamic relocation
/// @ref Google gold linker, symtab.h:645
bool GNULDBackend::symbolNeedsDynRel(const ResolveInfo& pSym,
bool pSymHasPLT,
const MCLDInfo& pLDInfo,
const Output& pOutput,
bool isAbsReloc) const
{
// an undefined reference in the executables should be statically
// resolved to 0 and no need a dynamic relocation
if (pSym.isUndef() && !pSym.isDyn() && (Output::Exec == pOutput.type()))
return false;
if (pSym.isAbsolute())
return false;
if (isOutputPIC(pOutput, pLDInfo) && isAbsReloc)
return true;
if (pSymHasPLT && ResolveInfo::Function == pSym.type())
return false;
if (!isOutputPIC(pOutput, pLDInfo) && pSymHasPLT)
return false;
if (pSym.isDyn() || pSym.isUndef() ||
isSymbolPreemptible(pSym, pLDInfo, pOutput))
return true;
return false;
}
/// symbolNeedsCopyReloc - return whether the symbol needs a copy relocation
bool GNULDBackend::symbolNeedsCopyReloc(const Layout& pLayout,
const Relocation& pReloc,
const ResolveInfo& pSym,
const MCLDInfo& pLDInfo,
const Output& pOutput) const
{
// only the reference from dynamic executable to non-function symbol in
// the dynamic objects may need copy relocation
if (isOutputPIC(pOutput, pLDInfo) ||
!pSym.isDyn() ||
pSym.type() == ResolveInfo::Function ||
pSym.size() == 0)
return false;
// check if the option -z nocopyreloc is given
if (pLDInfo.options().hasNoCopyReloc())
return false;
// TODO: Is this check necessary?
// if relocation target place is readonly, a copy relocation is needed
if ((pLayout.getOutputLDSection(*pReloc.targetRef().frag())->flag() &
llvm::ELF::SHF_WRITE) == 0)
return true;
return false;
}