//===- ELFObjectWriter.cpp ------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <mcld/LD/ELFObjectWriter.h>
#include <mcld/Module.h>
#include <mcld/LinkerConfig.h>
#include <mcld/Target/GNULDBackend.h>
#include <mcld/Support/MemoryArea.h>
#include <mcld/Support/MemoryRegion.h>
#include <mcld/Support/MsgHandling.h>
#include <mcld/ADT/SizeTraits.h>
#include <mcld/Fragment/FragmentLinker.h>
#include <mcld/Fragment/AlignFragment.h>
#include <mcld/Fragment/FillFragment.h>
#include <mcld/Fragment/RegionFragment.h>
#include <mcld/Fragment/Stub.h>
#include <mcld/Fragment/NullFragment.h>
#include <mcld/LD/LDSection.h>
#include <mcld/LD/SectionData.h>
#include <mcld/LD/ELFSegment.h>
#include <mcld/LD/ELFSegmentFactory.h>
#include <mcld/LD/RelocData.h>
#include <mcld/LD/EhFrame.h>
#include <llvm/Support/ErrorHandling.h>
#include <llvm/Support/system_error.h>
#include <llvm/Support/ELF.h>
#include <llvm/Support/Casting.h>
using namespace llvm;
using namespace llvm::ELF;
using namespace mcld;
//===----------------------------------------------------------------------===//
// ELFObjectWriter
//===----------------------------------------------------------------------===//
ELFObjectWriter::ELFObjectWriter(GNULDBackend& pBackend,
const LinkerConfig& pConfig)
: ObjectWriter(), m_Backend(pBackend), m_Config(pConfig)
{
}
ELFObjectWriter::~ELFObjectWriter()
{
}
void ELFObjectWriter::writeSection(MemoryArea& pOutput, LDSection *section)
{
MemoryRegion* region;
// Request output region
switch (section->kind()) {
case LDFileFormat::Note:
if (section->getSectionData() == NULL)
return;
// Fall through
case LDFileFormat::Regular:
case LDFileFormat::Relocation:
case LDFileFormat::Target:
case LDFileFormat::Debug:
case LDFileFormat::GCCExceptTable:
case LDFileFormat::EhFrame: {
region = pOutput.request(section->offset(), section->size());
if (NULL == region) {
llvm::report_fatal_error(llvm::Twine("cannot get enough memory region for output section `") +
llvm::Twine(section->name()) +
llvm::Twine("'.\n"));
}
break;
}
case LDFileFormat::Null:
case LDFileFormat::NamePool:
case LDFileFormat::BSS:
case LDFileFormat::MetaData:
case LDFileFormat::Version:
case LDFileFormat::EhFrameHdr:
case LDFileFormat::StackNote:
// Ignore these sections
return;
default:
llvm::errs() << "WARNING: unsupported section kind: "
<< section->kind()
<< " of section "
<< section->name()
<< ".\n";
return;
}
// Write out sections with data
switch(section->kind()) {
case LDFileFormat::GCCExceptTable:
case LDFileFormat::EhFrame:
case LDFileFormat::Regular:
case LDFileFormat::Debug:
case LDFileFormat::Note:
// FIXME: if optimization of exception handling sections is enabled,
// then we should emit these sections by the other way.
emitSectionData(*section, *region);
break;
case LDFileFormat::Relocation:
emitRelocation(m_Config, *section, *region);
break;
case LDFileFormat::Target:
target().emitSectionData(*section, *region);
break;
default:
llvm_unreachable("invalid section kind");
}
}
llvm::error_code ELFObjectWriter::writeObject(Module& pModule,
MemoryArea& pOutput)
{
bool is_dynobj = m_Config.codeGenType() == LinkerConfig::DynObj;
bool is_exec = m_Config.codeGenType() == LinkerConfig::Exec;
bool is_binary = m_Config.codeGenType() == LinkerConfig::Binary;
bool is_object = m_Config.codeGenType() == LinkerConfig::Object;
assert(is_dynobj || is_exec || is_binary || is_object);
if (is_dynobj || is_exec) {
// Allow backend to sort symbols before emitting
target().orderSymbolTable(pModule);
// Write out the interpreter section: .interp
target().emitInterp(pOutput);
// Write out name pool sections: .dynsym, .dynstr, .hash
target().emitDynNamePools(pModule, pOutput);
}
if (is_object || is_dynobj || is_exec) {
// Write out name pool sections: .symtab, .strtab
target().emitRegNamePools(pModule, pOutput);
}
if (is_binary) {
// Iterate over the loadable segments and write the corresponding sections
ELFSegmentFactory::iterator seg, segEnd = target().elfSegmentTable().end();
for (seg = target().elfSegmentTable().begin(); seg != segEnd; ++seg) {
if (llvm::ELF::PT_LOAD == (*seg).type()) {
ELFSegment::sect_iterator sect, sectEnd = (*seg).end();
for (sect = (*seg).begin(); sect != sectEnd; ++sect)
writeSection(pOutput, *sect);
}
}
} else {
// Write out regular ELF sections
Module::iterator sect, sectEnd = pModule.end();
for (sect = pModule.begin(); sect != sectEnd; ++sect)
writeSection(pOutput, *sect);
emitShStrTab(target().getOutputFormat()->getShStrTab(), pModule, pOutput);
if (m_Config.targets().is32Bits()) {
// Write out ELF header
// Write out section header table
writeELFHeader<32>(m_Config, pModule, pOutput);
if (is_dynobj || is_exec)
emitProgramHeader<32>(pOutput);
emitSectionHeader<32>(pModule, m_Config, pOutput);
}
else if (m_Config.targets().is64Bits()) {
// Write out ELF header
// Write out section header table
writeELFHeader<64>(m_Config, pModule, pOutput);
if (is_dynobj || is_exec)
emitProgramHeader<64>(pOutput);
emitSectionHeader<64>(pModule, m_Config, pOutput);
}
else
return make_error_code(errc::not_supported);
}
pOutput.clear();
return llvm::make_error_code(llvm::errc::success);
}
// writeELFHeader - emit ElfXX_Ehdr
template<size_t SIZE>
void ELFObjectWriter::writeELFHeader(const LinkerConfig& pConfig,
const Module& pModule,
MemoryArea& pOutput) const
{
typedef typename ELFSizeTraits<SIZE>::Ehdr ElfXX_Ehdr;
typedef typename ELFSizeTraits<SIZE>::Shdr ElfXX_Shdr;
typedef typename ELFSizeTraits<SIZE>::Phdr ElfXX_Phdr;
// ELF header must start from 0x0
MemoryRegion *region = pOutput.request(0, sizeof(ElfXX_Ehdr));
ElfXX_Ehdr* header = (ElfXX_Ehdr*)region->start();
memcpy(header->e_ident, ElfMagic, EI_MAG3+1);
header->e_ident[EI_CLASS] = (SIZE == 32) ? ELFCLASS32 : ELFCLASS64;
header->e_ident[EI_DATA] = pConfig.targets().isLittleEndian()?
ELFDATA2LSB : ELFDATA2MSB;
header->e_ident[EI_VERSION] = target().getInfo().ELFVersion();
header->e_ident[EI_OSABI] = target().getInfo().OSABI();
header->e_ident[EI_ABIVERSION] = target().getInfo().ABIVersion();
// FIXME: add processor-specific and core file types.
switch(pConfig.codeGenType()) {
case LinkerConfig::Object:
header->e_type = ET_REL;
break;
case LinkerConfig::DynObj:
header->e_type = ET_DYN;
break;
case LinkerConfig::Exec:
header->e_type = ET_EXEC;
break;
default:
llvm::errs() << "unspported output file type: " << pConfig.codeGenType() << ".\n";
header->e_type = ET_NONE;
}
header->e_machine = target().getInfo().machine();
header->e_version = header->e_ident[EI_VERSION];
header->e_entry = getEntryPoint(pConfig, pModule);
if (LinkerConfig::Object != pConfig.codeGenType())
header->e_phoff = sizeof(ElfXX_Ehdr);
else
header->e_phoff = 0x0;
header->e_shoff = getLastStartOffset<SIZE>(pModule);
header->e_flags = target().getInfo().flags();
header->e_ehsize = sizeof(ElfXX_Ehdr);
header->e_phentsize = sizeof(ElfXX_Phdr);
header->e_phnum = target().numOfSegments();
header->e_shentsize = sizeof(ElfXX_Shdr);
header->e_shnum = pModule.size();
header->e_shstrndx = pModule.getSection(".shstrtab")->index();
}
/// getEntryPoint
uint64_t ELFObjectWriter::getEntryPoint(const LinkerConfig& pConfig,
const Module& pModule) const
{
llvm::StringRef entry_name;
if (pConfig.options().hasEntry())
entry_name = pConfig.options().entry();
else
entry_name = target().getInfo().entry();
uint64_t result = 0x0;
bool issue_warning = (pConfig.options().hasEntry() &&
LinkerConfig::Object != pConfig.codeGenType() &&
LinkerConfig::DynObj != pConfig.codeGenType());
const LDSymbol* entry_symbol = pModule.getNamePool().findSymbol(entry_name);
// found the symbol
if (NULL != entry_symbol) {
if (entry_symbol->desc() != ResolveInfo::Define && issue_warning) {
llvm::errs() << "WARNING: entry symbol '"
<< entry_symbol->name()
<< "' exists but is not defined.\n";
}
result = entry_symbol->value();
}
// not in the symbol pool
else {
// We should parse entry as a number.
// @ref GNU ld manual, Options -e. e.g., -e 0x1000.
char* endptr;
result = strtoull(entry_name.data(), &endptr, 0);
if (*endptr != '\0') {
if (issue_warning) {
llvm::errs() << "cannot find entry symbol '"
<< entry_name.data()
<< "'.\n";
}
result = 0x0;
}
}
return result;
}
// emitSectionHeader - emit ElfXX_Shdr
template<size_t SIZE>
void ELFObjectWriter::emitSectionHeader(const Module& pModule,
const LinkerConfig& pConfig,
MemoryArea& pOutput) const
{
typedef typename ELFSizeTraits<SIZE>::Shdr ElfXX_Shdr;
// emit section header
unsigned int sectNum = pModule.size();
unsigned int header_size = sizeof(ElfXX_Shdr) * sectNum;
MemoryRegion* region = pOutput.request(getLastStartOffset<SIZE>(pModule),
header_size);
ElfXX_Shdr* shdr = (ElfXX_Shdr*)region->start();
// Iterate the SectionTable in LDContext
unsigned int sectIdx = 0;
unsigned int shstridx = 0; // NULL section has empty name
for (; sectIdx < sectNum; ++sectIdx) {
const LDSection *ld_sect = pModule.getSectionTable().at(sectIdx);
shdr[sectIdx].sh_name = shstridx;
shdr[sectIdx].sh_type = ld_sect->type();
shdr[sectIdx].sh_flags = ld_sect->flag();
shdr[sectIdx].sh_addr = ld_sect->addr();
shdr[sectIdx].sh_offset = ld_sect->offset();
shdr[sectIdx].sh_size = ld_sect->size();
shdr[sectIdx].sh_addralign = ld_sect->align();
shdr[sectIdx].sh_entsize = getSectEntrySize<SIZE>(*ld_sect);
shdr[sectIdx].sh_link = getSectLink(*ld_sect, pConfig);
shdr[sectIdx].sh_info = getSectInfo(*ld_sect);
// adjust strshidx
shstridx += ld_sect->name().size() + 1;
}
}
// emitProgramHeader - emit ElfXX_Phdr
template<size_t SIZE>
void ELFObjectWriter::emitProgramHeader(MemoryArea& pOutput) const
{
typedef typename ELFSizeTraits<SIZE>::Ehdr ElfXX_Ehdr;
typedef typename ELFSizeTraits<SIZE>::Phdr ElfXX_Phdr;
uint64_t start_offset, phdr_size;
start_offset = sizeof(ElfXX_Ehdr);
phdr_size = sizeof(ElfXX_Phdr);
// Program header must start directly after ELF header
MemoryRegion *region = pOutput.request(start_offset,
target().numOfSegments() * phdr_size);
ElfXX_Phdr* phdr = (ElfXX_Phdr*)region->start();
// Iterate the elf segment table in GNULDBackend
size_t index = 0;
ELFSegmentFactory::const_iterator seg = target().elfSegmentTable().begin(),
segEnd = target().elfSegmentTable().end();
for (; seg != segEnd; ++seg, ++index) {
phdr[index].p_type = (*seg).type();
phdr[index].p_flags = (*seg).flag();
phdr[index].p_offset = (*seg).offset();
phdr[index].p_vaddr = (*seg).vaddr();
phdr[index].p_paddr = (*seg).paddr();
phdr[index].p_filesz = (*seg).filesz();
phdr[index].p_memsz = (*seg).memsz();
phdr[index].p_align = (*seg).align();
}
}
/// emitShStrTab - emit section string table
void
ELFObjectWriter::emitShStrTab(const LDSection& pShStrTab,
const Module& pModule,
MemoryArea& pOutput)
{
// write out data
MemoryRegion* region = pOutput.request(pShStrTab.offset(), pShStrTab.size());
unsigned char* data = region->start();
size_t shstrsize = 0;
Module::const_iterator section, sectEnd = pModule.end();
for (section = pModule.begin(); section != sectEnd; ++section) {
strcpy((char*)(data + shstrsize), (*section)->name().data());
shstrsize += (*section)->name().size() + 1;
}
}
/// emitSectionData
void
ELFObjectWriter::emitSectionData(const LDSection& pSection,
MemoryRegion& pRegion) const
{
const SectionData* sd = NULL;
switch (pSection.kind()) {
case LDFileFormat::Relocation:
assert(pSection.hasRelocData());
return;
case LDFileFormat::EhFrame:
assert(pSection.hasEhFrame());
sd = pSection.getEhFrame()->getSectionData();
break;
default:
assert(pSection.hasSectionData());
sd = pSection.getSectionData();
break;
}
emitSectionData(*sd, pRegion);
}
/// emitRelocation
void ELFObjectWriter::emitRelocation(const LinkerConfig& pConfig,
const LDSection& pSection,
MemoryRegion& pRegion) const
{
const RelocData* sect_data = pSection.getRelocData();
assert(NULL != sect_data && "SectionData is NULL in emitRelocation!");
if (pSection.type() == SHT_REL) {
if (pConfig.targets().is32Bits())
emitRel<32>(pConfig, *sect_data, pRegion);
else if (pConfig.targets().is64Bits())
emitRel<64>(pConfig, *sect_data, pRegion);
else {
fatal(diag::unsupported_bitclass) << pConfig.targets().triple().str()
<< pConfig.targets().bitclass();
}
} else if (pSection.type() == SHT_RELA) {
if (pConfig.targets().is32Bits())
emitRela<32>(pConfig, *sect_data, pRegion);
else if (pConfig.targets().is64Bits())
emitRela<64>(pConfig, *sect_data, pRegion);
else {
fatal(diag::unsupported_bitclass) << pConfig.targets().triple().str()
<< pConfig.targets().bitclass();
}
} else
llvm::report_fatal_error("unsupported relocation section type!");
}
// emitRel - emit ElfXX_Rel
template<size_t SIZE>
void ELFObjectWriter::emitRel(const LinkerConfig& pConfig,
const RelocData& pRelocData,
MemoryRegion& pRegion) const
{
typedef typename ELFSizeTraits<SIZE>::Rel ElfXX_Rel;
typedef typename ELFSizeTraits<SIZE>::Addr ElfXX_Addr;
typedef typename ELFSizeTraits<SIZE>::Word ElfXX_Word;
ElfXX_Rel* rel = reinterpret_cast<ElfXX_Rel*>(pRegion.start());
const Relocation* relocation = 0;
const FragmentRef* frag_ref = 0;
for (RelocData::const_iterator it = pRelocData.begin(),
ie = pRelocData.end(); it != ie; ++it, ++rel) {
relocation = &(llvm::cast<Relocation>(*it));
frag_ref = &(relocation->targetRef());
if(LinkerConfig::DynObj == pConfig.codeGenType() ||
LinkerConfig::Exec == pConfig.codeGenType()) {
rel->r_offset = static_cast<ElfXX_Addr>(
frag_ref->frag()->getParent()->getSection().addr() +
frag_ref->getOutputOffset());
}
else {
rel->r_offset = static_cast<ElfXX_Addr>(frag_ref->getOutputOffset());
}
ElfXX_Word Index;
if( relocation->symInfo() == NULL )
Index = 0;
else
Index = static_cast<ElfXX_Word>(
target().getSymbolIdx(relocation->symInfo()->outSymbol()));
rel->setSymbolAndType(Index, relocation->type());
}
}
// emitRela - emit ElfXX_Rela
template<size_t SIZE>
void ELFObjectWriter::emitRela(const LinkerConfig& pConfig,
const RelocData& pRelocData,
MemoryRegion& pRegion) const
{
typedef typename ELFSizeTraits<SIZE>::Rela ElfXX_Rela;
typedef typename ELFSizeTraits<SIZE>::Addr ElfXX_Addr;
typedef typename ELFSizeTraits<SIZE>::Word ElfXX_Word;
ElfXX_Rela* rel = reinterpret_cast<ElfXX_Rela*>(pRegion.start());
const Relocation* relocation = 0;
const FragmentRef* frag_ref = 0;
for (RelocData::const_iterator it = pRelocData.begin(),
ie = pRelocData.end(); it != ie; ++it, ++rel) {
relocation = &(llvm::cast<Relocation>(*it));
frag_ref = &(relocation->targetRef());
if(LinkerConfig::DynObj == pConfig.codeGenType() ||
LinkerConfig::Exec == pConfig.codeGenType()) {
rel->r_offset = static_cast<ElfXX_Addr>(
frag_ref->frag()->getParent()->getSection().addr() +
frag_ref->getOutputOffset());
}
else {
rel->r_offset = static_cast<ElfXX_Addr>(frag_ref->getOutputOffset());
}
ElfXX_Word Index;
if( relocation->symInfo() == NULL )
Index = 0;
else
Index = static_cast<ElfXX_Word>(
target().getSymbolIdx(relocation->symInfo()->outSymbol()));
rel->setSymbolAndType(Index, relocation->type());
rel->r_addend = relocation->addend();
}
}
/// getSectEntrySize - compute ElfXX_Shdr::sh_entsize
template<size_t SIZE>
uint64_t ELFObjectWriter::getSectEntrySize(const LDSection& pSection) const
{
typedef typename ELFSizeTraits<SIZE>::Word ElfXX_Word;
typedef typename ELFSizeTraits<SIZE>::Sym ElfXX_Sym;
typedef typename ELFSizeTraits<SIZE>::Rel ElfXX_Rel;
typedef typename ELFSizeTraits<SIZE>::Rela ElfXX_Rela;
typedef typename ELFSizeTraits<SIZE>::Dyn ElfXX_Dyn;
if (llvm::ELF::SHT_DYNSYM == pSection.type() ||
llvm::ELF::SHT_SYMTAB == pSection.type())
return sizeof(ElfXX_Sym);
if (llvm::ELF::SHT_REL == pSection.type())
return sizeof(ElfXX_Rel);
if (llvm::ELF::SHT_RELA == pSection.type())
return sizeof(ElfXX_Rela);
if (llvm::ELF::SHT_HASH == pSection.type() ||
llvm::ELF::SHT_GNU_HASH == pSection.type())
return sizeof(ElfXX_Word);
if (llvm::ELF::SHT_DYNAMIC == pSection.type())
return sizeof(ElfXX_Dyn);
return 0x0;
}
/// getSectLink - compute ElfXX_Shdr::sh_link
uint64_t ELFObjectWriter::getSectLink(const LDSection& pSection,
const LinkerConfig& pConfig) const
{
if (llvm::ELF::SHT_SYMTAB == pSection.type())
return target().getOutputFormat()->getStrTab().index();
if (llvm::ELF::SHT_DYNSYM == pSection.type())
return target().getOutputFormat()->getDynStrTab().index();
if (llvm::ELF::SHT_DYNAMIC == pSection.type())
return target().getOutputFormat()->getDynStrTab().index();
if (llvm::ELF::SHT_HASH == pSection.type() ||
llvm::ELF::SHT_GNU_HASH == pSection.type())
return target().getOutputFormat()->getDynSymTab().index();
if (llvm::ELF::SHT_REL == pSection.type() ||
llvm::ELF::SHT_RELA == pSection.type()) {
if (LinkerConfig::Object == pConfig.codeGenType())
return target().getOutputFormat()->getSymTab().index();
else
return target().getOutputFormat()->getDynSymTab().index();
}
// FIXME: currently we link ARM_EXIDX section to output text section here
if (llvm::ELF::SHT_ARM_EXIDX == pSection.type())
return target().getOutputFormat()->getText().index();
return llvm::ELF::SHN_UNDEF;
}
/// getSectInfo - compute ElfXX_Shdr::sh_info
uint64_t ELFObjectWriter::getSectInfo(const LDSection& pSection) const
{
if (llvm::ELF::SHT_SYMTAB == pSection.type() ||
llvm::ELF::SHT_DYNSYM == pSection.type())
return pSection.getInfo();
if (llvm::ELF::SHT_REL == pSection.type() ||
llvm::ELF::SHT_RELA == pSection.type()) {
const LDSection* info_link = pSection.getLink();
if (NULL != info_link)
return info_link->index();
}
return 0x0;
}
/// getLastStartOffset
template<>
uint64_t ELFObjectWriter::getLastStartOffset<32>(const Module& pModule) const
{
const LDSection* lastSect = pModule.back();
assert(lastSect != NULL);
return Align<32>(lastSect->offset() + lastSect->size());
}
/// getLastStartOffset
template<>
uint64_t ELFObjectWriter::getLastStartOffset<64>(const Module& pModule) const
{
const LDSection* lastSect = pModule.back();
assert(lastSect != NULL);
return Align<64>(lastSect->offset() + lastSect->size());
}
/// emitSectionData
void ELFObjectWriter::emitSectionData(const SectionData& pSD,
MemoryRegion& pRegion) const
{
SectionData::const_iterator fragIter, fragEnd = pSD.end();
size_t cur_offset = 0;
for (fragIter = pSD.begin(); fragIter != fragEnd; ++fragIter) {
size_t size = fragIter->size();
switch(fragIter->getKind()) {
case Fragment::Region: {
const RegionFragment& region_frag = llvm::cast<RegionFragment>(*fragIter);
const uint8_t* from = region_frag.getRegion().start();
memcpy(pRegion.getBuffer(cur_offset), from, size);
break;
}
case Fragment::Alignment: {
// TODO: emit values with different sizes (> 1 byte), and emit nops
const AlignFragment& align_frag = llvm::cast<AlignFragment>(*fragIter);
uint64_t count = size / align_frag.getValueSize();
switch (align_frag.getValueSize()) {
case 1u:
std::memset(pRegion.getBuffer(cur_offset),
align_frag.getValue(),
count);
break;
default:
llvm::report_fatal_error("unsupported value size for align fragment emission yet.\n");
break;
}
break;
}
case Fragment::Fillment: {
const FillFragment& fill_frag = llvm::cast<FillFragment>(*fragIter);
if (0 == size ||
0 == fill_frag.getValueSize() ||
0 == fill_frag.size()) {
// ignore virtual fillment
break;
}
uint64_t num_tiles = fill_frag.size() / fill_frag.getValueSize();
for (uint64_t i = 0; i != num_tiles; ++i) {
std::memset(pRegion.getBuffer(cur_offset),
fill_frag.getValue(),
fill_frag.getValueSize());
}
break;
}
case Fragment::Stub: {
const Stub& stub_frag = llvm::cast<Stub>(*fragIter);
memcpy(pRegion.getBuffer(cur_offset), stub_frag.getContent(), size);
break;
}
case Fragment::Null: {
assert(0x0 == size);
break;
}
case Fragment::Target:
llvm::report_fatal_error("Target fragment should not be in a regular section.\n");
break;
default:
llvm::report_fatal_error("invalid fragment should not be in a regular section.\n");
break;
}
cur_offset += size;
}
}