You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1028 lines
35 KiB
1028 lines
35 KiB
//===- ELFReader.cpp ------------------------------------------------------===//
|
|
//
|
|
// The MCLinker Project
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "mcld/LD/ELFReader.h"
|
|
|
|
#include "mcld/IRBuilder.h"
|
|
#include "mcld/Fragment/FillFragment.h"
|
|
#include "mcld/LD/EhFrame.h"
|
|
#include "mcld/LD/LDContext.h"
|
|
#include "mcld/LD/SectionData.h"
|
|
#include "mcld/Object/ObjectBuilder.h"
|
|
#include "mcld/Support/MemoryArea.h"
|
|
#include "mcld/Support/MsgHandling.h"
|
|
#include "mcld/Target/GNUInfo.h"
|
|
#include "mcld/Target/GNULDBackend.h"
|
|
|
|
#include <llvm/ADT/StringRef.h>
|
|
#include <llvm/ADT/Twine.h>
|
|
#include <llvm/Support/ELF.h>
|
|
#include <llvm/Support/Host.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include <cstring>
|
|
|
|
namespace mcld {
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ELFReader<32, true>
|
|
//===----------------------------------------------------------------------===//
|
|
/// constructor
|
|
ELFReader<32, true>::ELFReader(GNULDBackend& pBackend) : ELFReaderIF(pBackend) {
|
|
}
|
|
|
|
/// destructor
|
|
ELFReader<32, true>::~ELFReader() {
|
|
}
|
|
|
|
/// isELF - is this a ELF file
|
|
bool ELFReader<32, true>::isELF(const void* pELFHeader) const {
|
|
const llvm::ELF::Elf32_Ehdr* hdr =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(pELFHeader);
|
|
if (memcmp(llvm::ELF::ElfMagic, hdr, 4) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// readRegularSection - read a regular section and create fragments.
|
|
bool ELFReader<32, true>::readRegularSection(Input& pInput,
|
|
SectionData& pSD) const {
|
|
uint32_t offset = pInput.fileOffset() + pSD.getSection().offset();
|
|
uint32_t size = pSD.getSection().size();
|
|
|
|
Fragment* frag = IRBuilder::CreateRegion(pInput, offset, size);
|
|
ObjectBuilder::AppendFragment(*frag, pSD);
|
|
return true;
|
|
}
|
|
|
|
/// readSymbols - read ELF symbols and create LDSymbol
|
|
bool ELFReader<32, true>::readSymbols(Input& pInput,
|
|
IRBuilder& pBuilder,
|
|
llvm::StringRef pRegion,
|
|
const char* pStrTab) const {
|
|
// get number of symbols
|
|
size_t entsize = pRegion.size() / sizeof(llvm::ELF::Elf32_Sym);
|
|
const llvm::ELF::Elf32_Sym* symtab =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Sym*>(pRegion.begin());
|
|
|
|
uint32_t st_name = 0x0;
|
|
uint32_t st_value = 0x0;
|
|
uint32_t st_size = 0x0;
|
|
uint8_t st_info = 0x0;
|
|
uint8_t st_other = 0x0;
|
|
uint16_t st_shndx = 0x0;
|
|
|
|
// skip the first NULL symbol
|
|
pInput.context()->addSymbol(LDSymbol::Null());
|
|
|
|
/// recording symbols added from DynObj to analyze weak alias
|
|
std::vector<AliasInfo> potential_aliases;
|
|
bool is_dyn_obj = (pInput.type() == Input::DynObj);
|
|
for (size_t idx = 1; idx < entsize; ++idx) {
|
|
st_info = symtab[idx].st_info;
|
|
st_other = symtab[idx].st_other;
|
|
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
st_name = symtab[idx].st_name;
|
|
st_value = symtab[idx].st_value;
|
|
st_size = symtab[idx].st_size;
|
|
st_shndx = symtab[idx].st_shndx;
|
|
} else {
|
|
st_name = mcld::bswap32(symtab[idx].st_name);
|
|
st_value = mcld::bswap32(symtab[idx].st_value);
|
|
st_size = mcld::bswap32(symtab[idx].st_size);
|
|
st_shndx = mcld::bswap16(symtab[idx].st_shndx);
|
|
}
|
|
|
|
// If the section should not be included, set the st_shndx SHN_UNDEF
|
|
// - A section in interrelated groups are not included.
|
|
if (pInput.type() == Input::Object && st_shndx < llvm::ELF::SHN_LORESERVE &&
|
|
st_shndx != llvm::ELF::SHN_UNDEF) {
|
|
if (pInput.context()->getSection(st_shndx) == NULL)
|
|
st_shndx = llvm::ELF::SHN_UNDEF;
|
|
}
|
|
|
|
// get ld_type
|
|
ResolveInfo::Type ld_type = getSymType(st_info, st_shndx);
|
|
|
|
// get ld_desc
|
|
ResolveInfo::Desc ld_desc = getSymDesc(st_shndx, pInput);
|
|
|
|
// get ld_binding
|
|
ResolveInfo::Binding ld_binding =
|
|
getSymBinding((st_info >> 4), st_shndx, st_other);
|
|
|
|
// get ld_value - ld_value must be section relative.
|
|
uint64_t ld_value = getSymValue(st_value, st_shndx, pInput);
|
|
|
|
// get ld_vis
|
|
ResolveInfo::Visibility ld_vis = getSymVisibility(st_other);
|
|
|
|
// get section
|
|
LDSection* section = NULL;
|
|
if (st_shndx < llvm::ELF::SHN_LORESERVE) // including ABS and COMMON
|
|
section = pInput.context()->getSection(st_shndx);
|
|
|
|
// get ld_name
|
|
std::string ld_name;
|
|
if (ResolveInfo::Section == ld_type) {
|
|
// Section symbol's st_name is the section index.
|
|
assert(section != NULL && "get a invalid section");
|
|
ld_name = section->name();
|
|
} else {
|
|
ld_name = std::string(pStrTab + st_name);
|
|
}
|
|
|
|
LDSymbol* psym = pBuilder.AddSymbol(pInput,
|
|
ld_name,
|
|
ld_type,
|
|
ld_desc,
|
|
ld_binding,
|
|
st_size,
|
|
ld_value,
|
|
section,
|
|
ld_vis);
|
|
|
|
if (is_dyn_obj && psym != NULL && ResolveInfo::Undefined != ld_desc &&
|
|
(ResolveInfo::Global == ld_binding ||
|
|
ResolveInfo::Weak == ld_binding) &&
|
|
ResolveInfo::Object == ld_type) {
|
|
AliasInfo p;
|
|
p.pt_alias = psym;
|
|
p.ld_binding = ld_binding;
|
|
p.ld_value = ld_value;
|
|
potential_aliases.push_back(p);
|
|
}
|
|
} // end of for loop
|
|
|
|
// analyze weak alias
|
|
// FIXME: it is better to let IRBuilder handle alias anlysis.
|
|
// 1. eliminate code duplication
|
|
// 2. easy to know if a symbol is from .so
|
|
// (so that it may be a potential alias)
|
|
if (is_dyn_obj) {
|
|
// sort symbols by symbol value and then weak before strong
|
|
std::sort(potential_aliases.begin(), potential_aliases.end(), less);
|
|
|
|
// for each weak symbol, find out all its aliases, and
|
|
// then link them as a circular list in Module
|
|
std::vector<AliasInfo>::iterator sym_it, sym_e;
|
|
sym_e = potential_aliases.end();
|
|
for (sym_it = potential_aliases.begin(); sym_it != sym_e; ++sym_it) {
|
|
if (ResolveInfo::Weak != sym_it->ld_binding)
|
|
continue;
|
|
|
|
Module& pModule = pBuilder.getModule();
|
|
std::vector<AliasInfo>::iterator alias_it = sym_it + 1;
|
|
while (alias_it != sym_e) {
|
|
if (sym_it->ld_value != alias_it->ld_value)
|
|
break;
|
|
|
|
if (sym_it + 1 == alias_it)
|
|
pModule.CreateAliasList(*sym_it->pt_alias->resolveInfo());
|
|
pModule.addAlias(*alias_it->pt_alias->resolveInfo());
|
|
++alias_it;
|
|
}
|
|
|
|
sym_it = alias_it - 1;
|
|
} // end of for loop
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ELFReader::read relocations - read ELF rela and rel, and create Relocation
|
|
//===----------------------------------------------------------------------===//
|
|
/// ELFReader::readRela - read ELF rela and create Relocation
|
|
bool ELFReader<32, true>::readRela(Input& pInput,
|
|
LDSection& pSection,
|
|
llvm::StringRef pRegion) const {
|
|
// get the number of rela
|
|
size_t entsize = pRegion.size() / sizeof(llvm::ELF::Elf32_Rela);
|
|
const llvm::ELF::Elf32_Rela* relaTab =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Rela*>(pRegion.begin());
|
|
|
|
for (size_t idx = 0; idx < entsize; ++idx) {
|
|
Relocation::Type r_type = 0x0;
|
|
uint32_t r_sym = 0x0;
|
|
uint32_t r_offset = 0x0;
|
|
int32_t r_addend = 0;
|
|
if (!target()
|
|
.readRelocation(relaTab[idx], r_type, r_sym, r_offset, r_addend)) {
|
|
return false;
|
|
}
|
|
|
|
LDSymbol* symbol = pInput.context()->getSymbol(r_sym);
|
|
if (symbol == NULL) {
|
|
fatal(diag::err_cannot_read_symbol) << r_sym << pInput.path();
|
|
}
|
|
|
|
IRBuilder::AddRelocation(pSection, r_type, *symbol, r_offset, r_addend);
|
|
} // end of for
|
|
return true;
|
|
}
|
|
|
|
/// readRel - read ELF rel and create Relocation
|
|
bool ELFReader<32, true>::readRel(Input& pInput,
|
|
LDSection& pSection,
|
|
llvm::StringRef pRegion) const {
|
|
// get the number of rel
|
|
size_t entsize = pRegion.size() / sizeof(llvm::ELF::Elf32_Rel);
|
|
const llvm::ELF::Elf32_Rel* relTab =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Rel*>(pRegion.begin());
|
|
|
|
for (size_t idx = 0; idx < entsize; ++idx) {
|
|
Relocation::Type r_type = 0x0;
|
|
uint32_t r_sym = 0x0;
|
|
uint32_t r_offset = 0x0;
|
|
|
|
if (!target().readRelocation(relTab[idx], r_type, r_sym, r_offset))
|
|
return false;
|
|
|
|
LDSymbol* symbol = pInput.context()->getSymbol(r_sym);
|
|
if (symbol == NULL) {
|
|
fatal(diag::err_cannot_read_symbol) << r_sym << pInput.path();
|
|
}
|
|
|
|
IRBuilder::AddRelocation(pSection, r_type, *symbol, r_offset);
|
|
} // end of for
|
|
return true;
|
|
}
|
|
|
|
/// isMyEndian - is this ELF file in the same endian to me?
|
|
bool ELFReader<32, true>::isMyEndian(const void* pELFHeader) const {
|
|
const llvm::ELF::Elf32_Ehdr* hdr =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(pELFHeader);
|
|
|
|
return (hdr->e_ident[llvm::ELF::EI_DATA] == llvm::ELF::ELFDATA2LSB);
|
|
}
|
|
|
|
/// isMyMachine - is this ELF file generated for the same machine.
|
|
bool ELFReader<32, true>::isMyMachine(const void* pELFHeader) const {
|
|
const llvm::ELF::Elf32_Ehdr* hdr =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(pELFHeader);
|
|
|
|
if (llvm::sys::IsLittleEndianHost)
|
|
return (hdr->e_machine == target().getInfo().machine());
|
|
return (mcld::bswap16(hdr->e_machine) == target().getInfo().machine());
|
|
}
|
|
|
|
/// fileType - return the file type
|
|
Input::Type ELFReader<32, true>::fileType(const void* pELFHeader) const {
|
|
const llvm::ELF::Elf32_Ehdr* hdr =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(pELFHeader);
|
|
uint32_t type = 0x0;
|
|
if (llvm::sys::IsLittleEndianHost)
|
|
type = hdr->e_type;
|
|
else
|
|
type = mcld::bswap16(hdr->e_type);
|
|
|
|
switch (type) {
|
|
case llvm::ELF::ET_REL:
|
|
return Input::Object;
|
|
case llvm::ELF::ET_EXEC:
|
|
return Input::Exec;
|
|
case llvm::ELF::ET_DYN:
|
|
return Input::DynObj;
|
|
case llvm::ELF::ET_CORE:
|
|
return Input::CoreFile;
|
|
case llvm::ELF::ET_NONE:
|
|
default:
|
|
return Input::Unknown;
|
|
}
|
|
}
|
|
|
|
/// readSectionHeaders - read ELF section header table and create LDSections
|
|
bool ELFReader<32, true>::readSectionHeaders(Input& pInput,
|
|
const void* pELFHeader) const {
|
|
const llvm::ELF::Elf32_Ehdr* ehdr =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Ehdr*>(pELFHeader);
|
|
|
|
uint32_t shoff = 0x0;
|
|
uint16_t shentsize = 0x0;
|
|
uint32_t shnum = 0x0;
|
|
uint32_t shstrtab = 0x0;
|
|
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
shoff = ehdr->e_shoff;
|
|
shentsize = ehdr->e_shentsize;
|
|
shnum = ehdr->e_shnum;
|
|
shstrtab = ehdr->e_shstrndx;
|
|
} else {
|
|
shoff = mcld::bswap32(ehdr->e_shoff);
|
|
shentsize = mcld::bswap16(ehdr->e_shentsize);
|
|
shnum = mcld::bswap16(ehdr->e_shnum);
|
|
shstrtab = mcld::bswap16(ehdr->e_shstrndx);
|
|
}
|
|
|
|
// If the file has no section header table, e_shoff holds zero.
|
|
if (shoff == 0x0)
|
|
return true;
|
|
|
|
const llvm::ELF::Elf32_Shdr* shdr = NULL;
|
|
llvm::StringRef shdr_region;
|
|
uint32_t sh_name = 0x0;
|
|
uint32_t sh_type = 0x0;
|
|
uint32_t sh_flags = 0x0;
|
|
uint32_t sh_offset = 0x0;
|
|
uint32_t sh_size = 0x0;
|
|
uint32_t sh_link = 0x0;
|
|
uint32_t sh_info = 0x0;
|
|
uint32_t sh_addralign = 0x0;
|
|
|
|
// if shnum and shstrtab overflow, the actual values are in the 1st shdr
|
|
if (shnum == llvm::ELF::SHN_UNDEF || shstrtab == llvm::ELF::SHN_XINDEX) {
|
|
shdr_region =
|
|
pInput.memArea()->request(pInput.fileOffset() + shoff, shentsize);
|
|
shdr = reinterpret_cast<const llvm::ELF::Elf32_Shdr*>(shdr_region.begin());
|
|
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
sh_size = shdr->sh_size;
|
|
sh_link = shdr->sh_link;
|
|
} else {
|
|
sh_size = mcld::bswap32(shdr->sh_size);
|
|
sh_link = mcld::bswap32(shdr->sh_link);
|
|
}
|
|
|
|
if (shnum == llvm::ELF::SHN_UNDEF)
|
|
shnum = sh_size;
|
|
if (shstrtab == llvm::ELF::SHN_XINDEX)
|
|
shstrtab = sh_link;
|
|
|
|
shoff += shentsize;
|
|
}
|
|
|
|
shdr_region =
|
|
pInput.memArea()->request(pInput.fileOffset() + shoff, shnum * shentsize);
|
|
const llvm::ELF::Elf32_Shdr* shdrTab =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Shdr*>(shdr_region.begin());
|
|
|
|
// get .shstrtab first
|
|
shdr = &shdrTab[shstrtab];
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
sh_offset = shdr->sh_offset;
|
|
sh_size = shdr->sh_size;
|
|
} else {
|
|
sh_offset = mcld::bswap32(shdr->sh_offset);
|
|
sh_size = mcld::bswap32(shdr->sh_size);
|
|
}
|
|
|
|
llvm::StringRef sect_name_region =
|
|
pInput.memArea()->request(pInput.fileOffset() + sh_offset, sh_size);
|
|
const char* sect_name = sect_name_region.begin();
|
|
|
|
LinkInfoList link_info_list;
|
|
|
|
// create all LDSections, including first NULL section.
|
|
for (size_t idx = 0; idx < shnum; ++idx) {
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
sh_name = shdrTab[idx].sh_name;
|
|
sh_type = shdrTab[idx].sh_type;
|
|
sh_flags = shdrTab[idx].sh_flags;
|
|
sh_offset = shdrTab[idx].sh_offset;
|
|
sh_size = shdrTab[idx].sh_size;
|
|
sh_link = shdrTab[idx].sh_link;
|
|
sh_info = shdrTab[idx].sh_info;
|
|
sh_addralign = shdrTab[idx].sh_addralign;
|
|
} else {
|
|
sh_name = mcld::bswap32(shdrTab[idx].sh_name);
|
|
sh_type = mcld::bswap32(shdrTab[idx].sh_type);
|
|
sh_flags = mcld::bswap32(shdrTab[idx].sh_flags);
|
|
sh_offset = mcld::bswap32(shdrTab[idx].sh_offset);
|
|
sh_size = mcld::bswap32(shdrTab[idx].sh_size);
|
|
sh_link = mcld::bswap32(shdrTab[idx].sh_link);
|
|
sh_info = mcld::bswap32(shdrTab[idx].sh_info);
|
|
sh_addralign = mcld::bswap32(shdrTab[idx].sh_addralign);
|
|
}
|
|
|
|
LDSection* section = IRBuilder::CreateELFHeader(
|
|
pInput, sect_name + sh_name, sh_type, sh_flags, sh_addralign);
|
|
section->setSize(sh_size);
|
|
section->setOffset(sh_offset);
|
|
section->setInfo(sh_info);
|
|
|
|
if (sh_link != 0x0 || sh_info != 0x0) {
|
|
LinkInfo link_info = {section, sh_link, sh_info};
|
|
link_info_list.push_back(link_info);
|
|
}
|
|
} // end of for
|
|
|
|
// set up InfoLink
|
|
LinkInfoList::iterator info, infoEnd = link_info_list.end();
|
|
for (info = link_info_list.begin(); info != infoEnd; ++info) {
|
|
if (LDFileFormat::Relocation == info->section->kind())
|
|
info->section->setLink(pInput.context()->getSection(info->sh_info));
|
|
else
|
|
info->section->setLink(pInput.context()->getSection(info->sh_link));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// readSignature - read a symbol from the given Input and index in symtab
|
|
/// This is used to get the signature of a group section.
|
|
ResolveInfo* ELFReader<32, true>::readSignature(Input& pInput,
|
|
LDSection& pSymTab,
|
|
uint32_t pSymIdx) const {
|
|
LDSection* symtab = &pSymTab;
|
|
LDSection* strtab = symtab->getLink();
|
|
assert(symtab != NULL && strtab != NULL);
|
|
|
|
uint32_t offset = pInput.fileOffset() + symtab->offset() +
|
|
sizeof(llvm::ELF::Elf32_Sym) * pSymIdx;
|
|
llvm::StringRef symbol_region =
|
|
pInput.memArea()->request(offset, sizeof(llvm::ELF::Elf32_Sym));
|
|
const llvm::ELF::Elf32_Sym* entry =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Sym*>(symbol_region.begin());
|
|
|
|
uint32_t st_name = 0x0;
|
|
uint8_t st_info = 0x0;
|
|
uint8_t st_other = 0x0;
|
|
uint16_t st_shndx = 0x0;
|
|
st_info = entry->st_info;
|
|
st_other = entry->st_other;
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
st_name = entry->st_name;
|
|
st_shndx = entry->st_shndx;
|
|
} else {
|
|
st_name = mcld::bswap32(entry->st_name);
|
|
st_shndx = mcld::bswap16(entry->st_shndx);
|
|
}
|
|
|
|
llvm::StringRef strtab_region = pInput.memArea()->request(
|
|
pInput.fileOffset() + strtab->offset(), strtab->size());
|
|
|
|
// get ld_name
|
|
llvm::StringRef ld_name(strtab_region.begin() + st_name);
|
|
|
|
ResolveInfo* result = ResolveInfo::Create(ld_name);
|
|
result->setSource(pInput.type() == Input::DynObj);
|
|
result->setType(static_cast<ResolveInfo::Type>(st_info & 0xF));
|
|
result->setDesc(getSymDesc(st_shndx, pInput));
|
|
result->setBinding(getSymBinding((st_info >> 4), st_shndx, st_other));
|
|
result->setVisibility(getSymVisibility(st_other));
|
|
|
|
return result;
|
|
}
|
|
|
|
/// readDynamic - read ELF .dynamic in input dynobj
|
|
bool ELFReader<32, true>::readDynamic(Input& pInput) const {
|
|
assert(pInput.type() == Input::DynObj);
|
|
const LDSection* dynamic_sect = pInput.context()->getSection(".dynamic");
|
|
if (dynamic_sect == NULL) {
|
|
fatal(diag::err_cannot_read_section) << ".dynamic";
|
|
}
|
|
const LDSection* dynstr_sect = dynamic_sect->getLink();
|
|
if (dynstr_sect == NULL) {
|
|
fatal(diag::err_cannot_read_section) << ".dynstr";
|
|
}
|
|
|
|
llvm::StringRef dynamic_region = pInput.memArea()->request(
|
|
pInput.fileOffset() + dynamic_sect->offset(), dynamic_sect->size());
|
|
|
|
llvm::StringRef dynstr_region = pInput.memArea()->request(
|
|
pInput.fileOffset() + dynstr_sect->offset(), dynstr_sect->size());
|
|
|
|
const llvm::ELF::Elf32_Dyn* dynamic =
|
|
reinterpret_cast<const llvm::ELF::Elf32_Dyn*>(dynamic_region.begin());
|
|
const char* dynstr = dynstr_region.begin();
|
|
bool hasSOName = false;
|
|
size_t numOfEntries = dynamic_sect->size() / sizeof(llvm::ELF::Elf32_Dyn);
|
|
|
|
for (size_t idx = 0; idx < numOfEntries; ++idx) {
|
|
llvm::ELF::Elf32_Sword d_tag = 0x0;
|
|
llvm::ELF::Elf32_Word d_val = 0x0;
|
|
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
d_tag = dynamic[idx].d_tag;
|
|
d_val = dynamic[idx].d_un.d_val;
|
|
} else {
|
|
d_tag = mcld::bswap32(dynamic[idx].d_tag);
|
|
d_val = mcld::bswap32(dynamic[idx].d_un.d_val);
|
|
}
|
|
|
|
switch (d_tag) {
|
|
case llvm::ELF::DT_SONAME:
|
|
assert(d_val < dynstr_sect->size());
|
|
pInput.setName(sys::fs::Path(dynstr + d_val).filename().native());
|
|
hasSOName = true;
|
|
break;
|
|
case llvm::ELF::DT_NEEDED:
|
|
// TODO:
|
|
break;
|
|
case llvm::ELF::DT_NULL:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if there is no SONAME in .dynamic, then set it from input path
|
|
if (!hasSOName)
|
|
pInput.setName(pInput.path().filename().native());
|
|
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ELFReader<64, true>
|
|
//===----------------------------------------------------------------------===//
|
|
/// constructor
|
|
ELFReader<64, true>::ELFReader(GNULDBackend& pBackend) : ELFReaderIF(pBackend) {
|
|
}
|
|
|
|
/// destructor
|
|
ELFReader<64, true>::~ELFReader() {
|
|
}
|
|
|
|
/// isELF - is this a ELF file
|
|
bool ELFReader<64, true>::isELF(const void* pELFHeader) const {
|
|
const llvm::ELF::Elf64_Ehdr* hdr =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(pELFHeader);
|
|
if (memcmp(llvm::ELF::ElfMagic, hdr, 4) == 0)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// readRegularSection - read a regular section and create fragments.
|
|
bool ELFReader<64, true>::readRegularSection(Input& pInput,
|
|
SectionData& pSD) const {
|
|
uint64_t offset = pInput.fileOffset() + pSD.getSection().offset();
|
|
uint64_t size = pSD.getSection().size();
|
|
|
|
Fragment* frag = IRBuilder::CreateRegion(pInput, offset, size);
|
|
ObjectBuilder::AppendFragment(*frag, pSD);
|
|
return true;
|
|
}
|
|
|
|
/// readSymbols - read ELF symbols and create LDSymbol
|
|
bool ELFReader<64, true>::readSymbols(Input& pInput,
|
|
IRBuilder& pBuilder,
|
|
llvm::StringRef pRegion,
|
|
const char* pStrTab) const {
|
|
// get number of symbols
|
|
size_t entsize = pRegion.size() / sizeof(llvm::ELF::Elf64_Sym);
|
|
const llvm::ELF::Elf64_Sym* symtab =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Sym*>(pRegion.begin());
|
|
|
|
uint32_t st_name = 0x0;
|
|
uint64_t st_value = 0x0;
|
|
uint64_t st_size = 0x0;
|
|
uint8_t st_info = 0x0;
|
|
uint8_t st_other = 0x0;
|
|
uint16_t st_shndx = 0x0;
|
|
|
|
// skip the first NULL symbol
|
|
pInput.context()->addSymbol(LDSymbol::Null());
|
|
|
|
/// recording symbols added from DynObj to analyze weak alias
|
|
std::vector<AliasInfo> potential_aliases;
|
|
bool is_dyn_obj = (pInput.type() == Input::DynObj);
|
|
for (size_t idx = 1; idx < entsize; ++idx) {
|
|
st_info = symtab[idx].st_info;
|
|
st_other = symtab[idx].st_other;
|
|
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
st_name = symtab[idx].st_name;
|
|
st_value = symtab[idx].st_value;
|
|
st_size = symtab[idx].st_size;
|
|
st_shndx = symtab[idx].st_shndx;
|
|
} else {
|
|
st_name = mcld::bswap32(symtab[idx].st_name);
|
|
st_value = mcld::bswap64(symtab[idx].st_value);
|
|
st_size = mcld::bswap64(symtab[idx].st_size);
|
|
st_shndx = mcld::bswap16(symtab[idx].st_shndx);
|
|
}
|
|
|
|
// If the section should not be included, set the st_shndx SHN_UNDEF
|
|
// - A section in interrelated groups are not included.
|
|
if (pInput.type() == Input::Object && st_shndx < llvm::ELF::SHN_LORESERVE &&
|
|
st_shndx != llvm::ELF::SHN_UNDEF) {
|
|
if (pInput.context()->getSection(st_shndx) == NULL)
|
|
st_shndx = llvm::ELF::SHN_UNDEF;
|
|
}
|
|
|
|
// get ld_type
|
|
ResolveInfo::Type ld_type = getSymType(st_info, st_shndx);
|
|
|
|
// get ld_desc
|
|
ResolveInfo::Desc ld_desc = getSymDesc(st_shndx, pInput);
|
|
|
|
// get ld_binding
|
|
ResolveInfo::Binding ld_binding =
|
|
getSymBinding((st_info >> 4), st_shndx, st_other);
|
|
|
|
// get ld_value - ld_value must be section relative.
|
|
uint64_t ld_value = getSymValue(st_value, st_shndx, pInput);
|
|
|
|
// get ld_vis
|
|
ResolveInfo::Visibility ld_vis = getSymVisibility(st_other);
|
|
|
|
// get section
|
|
LDSection* section = NULL;
|
|
if (st_shndx < llvm::ELF::SHN_LORESERVE) // including ABS and COMMON
|
|
section = pInput.context()->getSection(st_shndx);
|
|
|
|
// get ld_name
|
|
std::string ld_name;
|
|
if (ResolveInfo::Section == ld_type) {
|
|
// Section symbol's st_name is the section index.
|
|
assert(section != NULL && "get a invalid section");
|
|
ld_name = section->name();
|
|
} else {
|
|
ld_name = std::string(pStrTab + st_name);
|
|
}
|
|
|
|
LDSymbol* psym = pBuilder.AddSymbol(pInput,
|
|
ld_name,
|
|
ld_type,
|
|
ld_desc,
|
|
ld_binding,
|
|
st_size,
|
|
ld_value,
|
|
section,
|
|
ld_vis);
|
|
|
|
if (is_dyn_obj && psym != NULL && ResolveInfo::Undefined != ld_desc &&
|
|
(ResolveInfo::Global == ld_binding ||
|
|
ResolveInfo::Weak == ld_binding) &&
|
|
ResolveInfo::Object == ld_type) {
|
|
AliasInfo p;
|
|
p.pt_alias = psym;
|
|
p.ld_binding = ld_binding;
|
|
p.ld_value = ld_value;
|
|
potential_aliases.push_back(p);
|
|
}
|
|
} // end of for loop
|
|
|
|
// analyze weak alias here
|
|
if (is_dyn_obj) {
|
|
// sort symbols by symbol value and then weak before strong
|
|
std::sort(potential_aliases.begin(), potential_aliases.end(), less);
|
|
|
|
// for each weak symbol, find out all its aliases, and
|
|
// then link them as a circular list in Module
|
|
std::vector<AliasInfo>::iterator sym_it, sym_e;
|
|
sym_e = potential_aliases.end();
|
|
for (sym_it = potential_aliases.begin(); sym_it != sym_e; ++sym_it) {
|
|
if (ResolveInfo::Weak != sym_it->ld_binding)
|
|
continue;
|
|
|
|
Module& pModule = pBuilder.getModule();
|
|
std::vector<AliasInfo>::iterator alias_it = sym_it + 1;
|
|
while (alias_it != sym_e) {
|
|
if (sym_it->ld_value != alias_it->ld_value)
|
|
break;
|
|
|
|
if (sym_it + 1 == alias_it)
|
|
pModule.CreateAliasList(*sym_it->pt_alias->resolveInfo());
|
|
pModule.addAlias(*alias_it->pt_alias->resolveInfo());
|
|
++alias_it;
|
|
}
|
|
|
|
sym_it = alias_it - 1;
|
|
} // end of for loop
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// ELFReader::read relocations - read ELF rela and rel, and create Relocation
|
|
//===----------------------------------------------------------------------===//
|
|
/// ELFReader::readRela - read ELF rela and create Relocation
|
|
bool ELFReader<64, true>::readRela(Input& pInput,
|
|
LDSection& pSection,
|
|
llvm::StringRef pRegion) const {
|
|
// get the number of rela
|
|
size_t entsize = pRegion.size() / sizeof(llvm::ELF::Elf64_Rela);
|
|
const llvm::ELF::Elf64_Rela* relaTab =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Rela*>(pRegion.begin());
|
|
|
|
for (size_t idx = 0; idx < entsize; ++idx) {
|
|
Relocation::Type r_type = 0x0;
|
|
uint32_t r_sym = 0x0;
|
|
uint64_t r_offset = 0x0;
|
|
int64_t r_addend = 0;
|
|
if (!target()
|
|
.readRelocation(relaTab[idx], r_type, r_sym, r_offset, r_addend)) {
|
|
return false;
|
|
}
|
|
|
|
LDSymbol* symbol = pInput.context()->getSymbol(r_sym);
|
|
if (symbol == NULL) {
|
|
fatal(diag::err_cannot_read_symbol) << r_sym << pInput.path();
|
|
}
|
|
|
|
IRBuilder::AddRelocation(pSection, r_type, *symbol, r_offset, r_addend);
|
|
} // end of for
|
|
return true;
|
|
}
|
|
|
|
/// readRel - read ELF rel and create Relocation
|
|
bool ELFReader<64, true>::readRel(Input& pInput,
|
|
LDSection& pSection,
|
|
llvm::StringRef pRegion) const {
|
|
// get the number of rel
|
|
size_t entsize = pRegion.size() / sizeof(llvm::ELF::Elf64_Rel);
|
|
const llvm::ELF::Elf64_Rel* relTab =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Rel*>(pRegion.begin());
|
|
|
|
for (size_t idx = 0; idx < entsize; ++idx) {
|
|
Relocation::Type r_type = 0x0;
|
|
uint32_t r_sym = 0x0;
|
|
uint64_t r_offset = 0x0;
|
|
if (!target().readRelocation(relTab[idx], r_type, r_sym, r_offset))
|
|
return false;
|
|
|
|
LDSymbol* symbol = pInput.context()->getSymbol(r_sym);
|
|
if (symbol == NULL) {
|
|
fatal(diag::err_cannot_read_symbol) << r_sym << pInput.path();
|
|
}
|
|
|
|
IRBuilder::AddRelocation(pSection, r_type, *symbol, r_offset);
|
|
} // end of for
|
|
return true;
|
|
}
|
|
|
|
/// isMyEndian - is this ELF file in the same endian to me?
|
|
bool ELFReader<64, true>::isMyEndian(const void* pELFHeader) const {
|
|
const llvm::ELF::Elf64_Ehdr* hdr =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(pELFHeader);
|
|
|
|
return (hdr->e_ident[llvm::ELF::EI_DATA] == llvm::ELF::ELFDATA2LSB);
|
|
}
|
|
|
|
/// isMyMachine - is this ELF file generated for the same machine.
|
|
bool ELFReader<64, true>::isMyMachine(const void* pELFHeader) const {
|
|
const llvm::ELF::Elf64_Ehdr* hdr =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(pELFHeader);
|
|
|
|
if (llvm::sys::IsLittleEndianHost)
|
|
return (hdr->e_machine == target().getInfo().machine());
|
|
return (mcld::bswap16(hdr->e_machine) == target().getInfo().machine());
|
|
}
|
|
|
|
/// fileType - return the file type
|
|
Input::Type ELFReader<64, true>::fileType(const void* pELFHeader) const {
|
|
const llvm::ELF::Elf64_Ehdr* hdr =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(pELFHeader);
|
|
uint32_t type = 0x0;
|
|
if (llvm::sys::IsLittleEndianHost)
|
|
type = hdr->e_type;
|
|
else
|
|
type = mcld::bswap16(hdr->e_type);
|
|
|
|
switch (type) {
|
|
case llvm::ELF::ET_REL:
|
|
return Input::Object;
|
|
case llvm::ELF::ET_EXEC:
|
|
return Input::Exec;
|
|
case llvm::ELF::ET_DYN:
|
|
return Input::DynObj;
|
|
case llvm::ELF::ET_CORE:
|
|
return Input::CoreFile;
|
|
case llvm::ELF::ET_NONE:
|
|
default:
|
|
return Input::Unknown;
|
|
}
|
|
}
|
|
|
|
/// readSectionHeaders - read ELF section header table and create LDSections
|
|
bool ELFReader<64, true>::readSectionHeaders(Input& pInput,
|
|
const void* pELFHeader) const {
|
|
const llvm::ELF::Elf64_Ehdr* ehdr =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Ehdr*>(pELFHeader);
|
|
|
|
uint64_t shoff = 0x0;
|
|
uint16_t shentsize = 0x0;
|
|
uint32_t shnum = 0x0;
|
|
uint32_t shstrtab = 0x0;
|
|
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
shoff = ehdr->e_shoff;
|
|
shentsize = ehdr->e_shentsize;
|
|
shnum = ehdr->e_shnum;
|
|
shstrtab = ehdr->e_shstrndx;
|
|
} else {
|
|
shoff = mcld::bswap64(ehdr->e_shoff);
|
|
shentsize = mcld::bswap16(ehdr->e_shentsize);
|
|
shnum = mcld::bswap16(ehdr->e_shnum);
|
|
shstrtab = mcld::bswap16(ehdr->e_shstrndx);
|
|
}
|
|
|
|
// If the file has no section header table, e_shoff holds zero.
|
|
if (shoff == 0x0)
|
|
return true;
|
|
|
|
const llvm::ELF::Elf64_Shdr* shdr = NULL;
|
|
llvm::StringRef shdr_region;
|
|
uint32_t sh_name = 0x0;
|
|
uint32_t sh_type = 0x0;
|
|
uint64_t sh_flags = 0x0;
|
|
uint64_t sh_offset = 0x0;
|
|
uint64_t sh_size = 0x0;
|
|
uint32_t sh_link = 0x0;
|
|
uint32_t sh_info = 0x0;
|
|
uint64_t sh_addralign = 0x0;
|
|
|
|
// if shnum and shstrtab overflow, the actual values are in the 1st shdr
|
|
if (shnum == llvm::ELF::SHN_UNDEF || shstrtab == llvm::ELF::SHN_XINDEX) {
|
|
shdr_region =
|
|
pInput.memArea()->request(pInput.fileOffset() + shoff, shentsize);
|
|
shdr = reinterpret_cast<const llvm::ELF::Elf64_Shdr*>(shdr_region.begin());
|
|
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
sh_size = shdr->sh_size;
|
|
sh_link = shdr->sh_link;
|
|
} else {
|
|
sh_size = mcld::bswap64(shdr->sh_size);
|
|
sh_link = mcld::bswap32(shdr->sh_link);
|
|
}
|
|
|
|
if (shnum == llvm::ELF::SHN_UNDEF)
|
|
shnum = sh_size;
|
|
if (shstrtab == llvm::ELF::SHN_XINDEX)
|
|
shstrtab = sh_link;
|
|
|
|
shoff += shentsize;
|
|
}
|
|
|
|
shdr_region =
|
|
pInput.memArea()->request(pInput.fileOffset() + shoff, shnum * shentsize);
|
|
const llvm::ELF::Elf64_Shdr* shdrTab =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Shdr*>(shdr_region.begin());
|
|
|
|
// get .shstrtab first
|
|
shdr = &shdrTab[shstrtab];
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
sh_offset = shdr->sh_offset;
|
|
sh_size = shdr->sh_size;
|
|
} else {
|
|
sh_offset = mcld::bswap64(shdr->sh_offset);
|
|
sh_size = mcld::bswap64(shdr->sh_size);
|
|
}
|
|
|
|
llvm::StringRef sect_name_region =
|
|
pInput.memArea()->request(pInput.fileOffset() + sh_offset, sh_size);
|
|
const char* sect_name = sect_name_region.begin();
|
|
|
|
LinkInfoList link_info_list;
|
|
|
|
// create all LDSections, including first NULL section.
|
|
for (size_t idx = 0; idx < shnum; ++idx) {
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
sh_name = shdrTab[idx].sh_name;
|
|
sh_type = shdrTab[idx].sh_type;
|
|
sh_flags = shdrTab[idx].sh_flags;
|
|
sh_offset = shdrTab[idx].sh_offset;
|
|
sh_size = shdrTab[idx].sh_size;
|
|
sh_link = shdrTab[idx].sh_link;
|
|
sh_info = shdrTab[idx].sh_info;
|
|
sh_addralign = shdrTab[idx].sh_addralign;
|
|
} else {
|
|
sh_name = mcld::bswap32(shdrTab[idx].sh_name);
|
|
sh_type = mcld::bswap32(shdrTab[idx].sh_type);
|
|
sh_flags = mcld::bswap64(shdrTab[idx].sh_flags);
|
|
sh_offset = mcld::bswap64(shdrTab[idx].sh_offset);
|
|
sh_size = mcld::bswap64(shdrTab[idx].sh_size);
|
|
sh_link = mcld::bswap32(shdrTab[idx].sh_link);
|
|
sh_info = mcld::bswap32(shdrTab[idx].sh_info);
|
|
sh_addralign = mcld::bswap64(shdrTab[idx].sh_addralign);
|
|
}
|
|
|
|
LDSection* section = IRBuilder::CreateELFHeader(
|
|
pInput, sect_name + sh_name, sh_type, sh_flags, sh_addralign);
|
|
section->setSize(sh_size);
|
|
section->setOffset(sh_offset);
|
|
section->setInfo(sh_info);
|
|
|
|
if (sh_link != 0x0 || sh_info != 0x0) {
|
|
LinkInfo link_info = {section, sh_link, sh_info};
|
|
link_info_list.push_back(link_info);
|
|
}
|
|
} // end of for
|
|
|
|
// set up InfoLink
|
|
LinkInfoList::iterator info, infoEnd = link_info_list.end();
|
|
for (info = link_info_list.begin(); info != infoEnd; ++info) {
|
|
if (LDFileFormat::Relocation == info->section->kind())
|
|
info->section->setLink(pInput.context()->getSection(info->sh_info));
|
|
else
|
|
info->section->setLink(pInput.context()->getSection(info->sh_link));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// readSignature - read a symbol from the given Input and index in symtab
|
|
/// This is used to get the signature of a group section.
|
|
ResolveInfo* ELFReader<64, true>::readSignature(Input& pInput,
|
|
LDSection& pSymTab,
|
|
uint32_t pSymIdx) const {
|
|
LDSection* symtab = &pSymTab;
|
|
LDSection* strtab = symtab->getLink();
|
|
assert(symtab != NULL && strtab != NULL);
|
|
|
|
uint64_t offset = pInput.fileOffset() + symtab->offset() +
|
|
sizeof(llvm::ELF::Elf64_Sym) * pSymIdx;
|
|
llvm::StringRef symbol_region =
|
|
pInput.memArea()->request(offset, sizeof(llvm::ELF::Elf64_Sym));
|
|
const llvm::ELF::Elf64_Sym* entry =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Sym*>(symbol_region.begin());
|
|
|
|
uint32_t st_name = 0x0;
|
|
uint8_t st_info = 0x0;
|
|
uint8_t st_other = 0x0;
|
|
uint16_t st_shndx = 0x0;
|
|
st_info = entry->st_info;
|
|
st_other = entry->st_other;
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
st_name = entry->st_name;
|
|
st_shndx = entry->st_shndx;
|
|
} else {
|
|
st_name = mcld::bswap32(entry->st_name);
|
|
st_shndx = mcld::bswap16(entry->st_shndx);
|
|
}
|
|
|
|
llvm::StringRef strtab_region = pInput.memArea()->request(
|
|
pInput.fileOffset() + strtab->offset(), strtab->size());
|
|
|
|
// get ld_name
|
|
llvm::StringRef ld_name(strtab_region.begin() + st_name);
|
|
|
|
ResolveInfo* result = ResolveInfo::Create(ld_name);
|
|
result->setSource(pInput.type() == Input::DynObj);
|
|
result->setType(static_cast<ResolveInfo::Type>(st_info & 0xF));
|
|
result->setDesc(getSymDesc(st_shndx, pInput));
|
|
result->setBinding(getSymBinding((st_info >> 4), st_shndx, st_other));
|
|
result->setVisibility(getSymVisibility(st_other));
|
|
|
|
return result;
|
|
}
|
|
|
|
/// readDynamic - read ELF .dynamic in input dynobj
|
|
bool ELFReader<64, true>::readDynamic(Input& pInput) const {
|
|
assert(pInput.type() == Input::DynObj);
|
|
const LDSection* dynamic_sect = pInput.context()->getSection(".dynamic");
|
|
if (dynamic_sect == NULL) {
|
|
fatal(diag::err_cannot_read_section) << ".dynamic";
|
|
}
|
|
const LDSection* dynstr_sect = dynamic_sect->getLink();
|
|
if (dynstr_sect == NULL) {
|
|
fatal(diag::err_cannot_read_section) << ".dynstr";
|
|
}
|
|
|
|
llvm::StringRef dynamic_region = pInput.memArea()->request(
|
|
pInput.fileOffset() + dynamic_sect->offset(), dynamic_sect->size());
|
|
|
|
llvm::StringRef dynstr_region = pInput.memArea()->request(
|
|
pInput.fileOffset() + dynstr_sect->offset(), dynstr_sect->size());
|
|
|
|
const llvm::ELF::Elf64_Dyn* dynamic =
|
|
reinterpret_cast<const llvm::ELF::Elf64_Dyn*>(dynamic_region.begin());
|
|
const char* dynstr = dynstr_region.begin();
|
|
bool hasSOName = false;
|
|
size_t numOfEntries = dynamic_sect->size() / sizeof(llvm::ELF::Elf64_Dyn);
|
|
|
|
for (size_t idx = 0; idx < numOfEntries; ++idx) {
|
|
llvm::ELF::Elf64_Sxword d_tag = 0x0;
|
|
llvm::ELF::Elf64_Xword d_val = 0x0;
|
|
|
|
if (llvm::sys::IsLittleEndianHost) {
|
|
d_tag = dynamic[idx].d_tag;
|
|
d_val = dynamic[idx].d_un.d_val;
|
|
} else {
|
|
d_tag = mcld::bswap64(dynamic[idx].d_tag);
|
|
d_val = mcld::bswap64(dynamic[idx].d_un.d_val);
|
|
}
|
|
|
|
switch (d_tag) {
|
|
case llvm::ELF::DT_SONAME:
|
|
assert(d_val < dynstr_sect->size());
|
|
pInput.setName(sys::fs::Path(dynstr + d_val).filename().native());
|
|
hasSOName = true;
|
|
break;
|
|
case llvm::ELF::DT_NEEDED:
|
|
// TODO:
|
|
break;
|
|
case llvm::ELF::DT_NULL:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if there is no SONAME in .dynamic, then set it from input path
|
|
if (!hasSOName)
|
|
pInput.setName(pInput.path().filename().native());
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace mcld
|