mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-04-01 12:43:47 +00:00
[elf2] Add basic archive file support.
llvm-svn: 246886
This commit is contained in:
parent
2becc987ef
commit
1b348a68e5
@ -45,35 +45,17 @@ MemoryBufferRef LinkerDriver::openFile(StringRef Path) {
|
||||
return MBRef;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB,
|
||||
bool IsShared) {
|
||||
if (IsShared)
|
||||
return make_unique<SharedFile<ELFT>>(MB);
|
||||
return make_unique<ObjectFile<ELFT>>(MB);
|
||||
}
|
||||
|
||||
static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) {
|
||||
using namespace llvm::sys::fs;
|
||||
file_magic Magic = identify_magic(MB.getBuffer());
|
||||
|
||||
std::pair<unsigned char, unsigned char> Type =
|
||||
object::getElfArchType(MB.getBuffer());
|
||||
if (Type.second != ELF::ELFDATA2LSB && Type.second != ELF::ELFDATA2MSB)
|
||||
error("Invalid data encoding");
|
||||
if (Magic == file_magic::archive)
|
||||
return make_unique<ArchiveFile>(MB);
|
||||
|
||||
bool IsShared = Magic == file_magic::elf_shared_object;
|
||||
if (Type.first == ELF::ELFCLASS32) {
|
||||
if (Type.second == ELF::ELFDATA2LSB)
|
||||
return createFile<object::ELF32LE>(MB, IsShared);
|
||||
return createFile<object::ELF32BE>(MB, IsShared);
|
||||
}
|
||||
if (Type.first == ELF::ELFCLASS64) {
|
||||
if (Type.second == ELF::ELFDATA2LSB)
|
||||
return createFile<object::ELF64LE>(MB, IsShared);
|
||||
return createFile<object::ELF64BE>(MB, IsShared);
|
||||
}
|
||||
error("Invalid file class");
|
||||
if (Magic == file_magic::elf_shared_object)
|
||||
return createELFFile<SharedFile>(MB);
|
||||
|
||||
return createELFFile<ObjectFile>(MB);
|
||||
}
|
||||
|
||||
void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "Symbols.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
|
||||
@ -150,6 +151,36 @@ SymbolBody *elf2::ObjectFile<ELFT>::createSymbolBody(StringRef StringTable,
|
||||
}
|
||||
}
|
||||
|
||||
void ArchiveFile::parse() {
|
||||
auto ArchiveOrErr = Archive::create(MB);
|
||||
error(ArchiveOrErr, "Failed to parse archive");
|
||||
File = std::move(*ArchiveOrErr);
|
||||
|
||||
// Allocate a buffer for Lazy objects.
|
||||
size_t NumSyms = File->getNumberOfSymbols();
|
||||
LazySymbols.reserve(NumSyms);
|
||||
|
||||
// Read the symbol table to construct Lazy objects.
|
||||
for (const Archive::Symbol &Sym : File->symbols())
|
||||
LazySymbols.emplace_back(this, Sym);
|
||||
}
|
||||
|
||||
// Returns a buffer pointing to a member file containing a given symbol.
|
||||
MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
|
||||
ErrorOr<Archive::child_iterator> ItOrErr = Sym->getMember();
|
||||
error(ItOrErr,
|
||||
Twine("Could not get the member for symbol ") + Sym->getName());
|
||||
Archive::child_iterator It = *ItOrErr;
|
||||
|
||||
if (!Seen.insert(It->getChildOffset()).second) {
|
||||
return MemoryBufferRef();
|
||||
}
|
||||
ErrorOr<MemoryBufferRef> Ret = It->getMemoryBufferRef();
|
||||
error(Ret, Twine("Could not get the buffer for the member defining symbol ") +
|
||||
Sym->getName());
|
||||
return *Ret;
|
||||
}
|
||||
|
||||
template <class ELFT> void SharedFile<ELFT>::parse() { this->openELF(MB); }
|
||||
|
||||
namespace lld {
|
||||
|
@ -11,19 +11,27 @@
|
||||
#define LLD_ELF_INPUT_FILES_H
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf2 {
|
||||
|
||||
using llvm::object::Archive;
|
||||
|
||||
class Lazy;
|
||||
class SymbolBody;
|
||||
|
||||
// The root class of input files.
|
||||
class InputFile {
|
||||
public:
|
||||
enum Kind { ObjectKind, SharedKind };
|
||||
enum Kind { ObjectKind, SharedKind, ArchiveKind };
|
||||
Kind kind() const { return FileKind; }
|
||||
virtual ~InputFile() {}
|
||||
|
||||
@ -137,6 +145,25 @@ private:
|
||||
ArrayRef<Elf_Word> SymtabSHNDX;
|
||||
};
|
||||
|
||||
class ArchiveFile : public InputFile {
|
||||
public:
|
||||
explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
|
||||
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
|
||||
void parse() override;
|
||||
|
||||
// Returns a memory buffer for a given symbol. An empty memory buffer
|
||||
// is returned if we have already returned the same memory buffer.
|
||||
// (So that we don't instantiate same members more than once.)
|
||||
MemoryBufferRef getMember(const Archive::Symbol *Sym);
|
||||
|
||||
llvm::MutableArrayRef<Lazy> getLazySymbols() { return LazySymbols; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<Archive> File;
|
||||
std::vector<Lazy> LazySymbols;
|
||||
llvm::DenseSet<uint64_t> Seen;
|
||||
};
|
||||
|
||||
// .so file.
|
||||
class SharedFileBase : public ELFFileBase {
|
||||
public:
|
||||
@ -159,6 +186,28 @@ public:
|
||||
void parse() override;
|
||||
};
|
||||
|
||||
template <template <class> class T>
|
||||
std::unique_ptr<ELFFileBase> createELFFile(MemoryBufferRef MB) {
|
||||
using namespace llvm;
|
||||
|
||||
std::pair<unsigned char, unsigned char> Type =
|
||||
object::getElfArchType(MB.getBuffer());
|
||||
if (Type.second != ELF::ELFDATA2LSB && Type.second != ELF::ELFDATA2MSB)
|
||||
error("Invalid data encoding");
|
||||
|
||||
if (Type.first == ELF::ELFCLASS32) {
|
||||
if (Type.second == ELF::ELFDATA2LSB)
|
||||
return make_unique<T<object::ELF32LE>>(MB);
|
||||
return make_unique<T<object::ELF32BE>>(MB);
|
||||
}
|
||||
if (Type.first == ELF::ELFCLASS64) {
|
||||
if (Type.second == ELF::ELFDATA2LSB)
|
||||
return make_unique<T<object::ELF64LE>>(MB);
|
||||
return make_unique<T<object::ELF64BE>>(MB);
|
||||
}
|
||||
error("Invalid file class");
|
||||
}
|
||||
|
||||
} // namespace elf2
|
||||
} // namespace lld
|
||||
|
||||
|
@ -23,8 +23,13 @@ SymbolTable::SymbolTable() {
|
||||
void SymbolTable::addFile(std::unique_ptr<InputFile> File) {
|
||||
File->parse();
|
||||
InputFile *FileP = File.release();
|
||||
auto *P = cast<ELFFileBase>(FileP);
|
||||
addELFFile(P);
|
||||
if (auto *AF = dyn_cast<ArchiveFile>(FileP)) {
|
||||
ArchiveFiles.emplace_back(AF);
|
||||
for (Lazy &Sym : AF->getLazySymbols())
|
||||
addLazy(&Sym);
|
||||
return;
|
||||
}
|
||||
addELFFile(cast<ELFFileBase>(FileP));
|
||||
}
|
||||
|
||||
template <class ELFT> void SymbolTable::init() {
|
||||
@ -78,22 +83,64 @@ void SymbolTable::reportRemainingUndefines() {
|
||||
// This function resolves conflicts if there's an existing symbol with
|
||||
// the same name. Decisions are made based on symbol type.
|
||||
template <class ELFT> void SymbolTable::resolve(SymbolBody *New) {
|
||||
Symbol *Sym = insert(New);
|
||||
if (Sym->Body == New)
|
||||
return;
|
||||
|
||||
SymbolBody *Existing = Sym->Body;
|
||||
|
||||
if (Lazy *L = dyn_cast<Lazy>(Existing)) {
|
||||
if (New->isUndefined()) {
|
||||
addMemberFile(L);
|
||||
return;
|
||||
}
|
||||
|
||||
// Found a definition for something also in an archive. Ignore the archive
|
||||
// definition.
|
||||
Sym->Body = New;
|
||||
return;
|
||||
}
|
||||
|
||||
// compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
|
||||
// equivalent (conflicting), or more preferable, respectively.
|
||||
int comp = Existing->compare<ELFT>(New);
|
||||
if (comp < 0)
|
||||
Sym->Body = New;
|
||||
if (comp == 0)
|
||||
error(Twine("duplicate symbol: ") + Sym->Body->getName());
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::insert(SymbolBody *New) {
|
||||
// Find an existing Symbol or create and insert a new one.
|
||||
StringRef Name = New->getName();
|
||||
Symbol *&Sym = Symtab[Name];
|
||||
if (!Sym) {
|
||||
Sym = new (Alloc) Symbol(New);
|
||||
New->setBackref(Sym);
|
||||
return;
|
||||
return Sym;
|
||||
}
|
||||
New->setBackref(Sym);
|
||||
|
||||
// compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
|
||||
// equivalent (conflicting), or more preferable, respectively.
|
||||
SymbolBody *Existing = Sym->Body;
|
||||
int comp = Existing->compare<ELFT>(New);
|
||||
if (comp < 0)
|
||||
Sym->Body = New;
|
||||
if (comp == 0)
|
||||
error(Twine("duplicate symbol: ") + Name);
|
||||
return Sym;
|
||||
}
|
||||
|
||||
void SymbolTable::addLazy(Lazy *New) {
|
||||
Symbol *Sym = insert(New);
|
||||
if (Sym->Body == New)
|
||||
return;
|
||||
SymbolBody *Existing = Sym->Body;
|
||||
if (Existing->isDefined() || Existing->isLazy())
|
||||
return;
|
||||
Sym->Body = New;
|
||||
assert(Existing->isUndefined() && "Unexpected symbol kind.");
|
||||
addMemberFile(New);
|
||||
}
|
||||
|
||||
void SymbolTable::addMemberFile(Lazy *Body) {
|
||||
std::unique_ptr<InputFile> File = Body->getMember();
|
||||
|
||||
// getMember returns nullptr if the member was already read from the library.
|
||||
if (!File)
|
||||
return;
|
||||
|
||||
addFile(std::move(File));
|
||||
}
|
||||
|
@ -54,12 +54,17 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Symbol *insert(SymbolBody *New);
|
||||
template <class ELFT> void addELFFile(ELFFileBase *File);
|
||||
void addELFFile(ELFFileBase *File);
|
||||
void addLazy(Lazy *New);
|
||||
void addMemberFile(Lazy *Body);
|
||||
|
||||
template <class ELFT> void init();
|
||||
template <class ELFT> void resolve(SymbolBody *Body);
|
||||
|
||||
std::vector<std::unique_ptr<ArchiveFile>> ArchiveFiles;
|
||||
|
||||
llvm::DenseMap<StringRef, Symbol *> Symtab;
|
||||
llvm::BumpPtrAllocator Alloc;
|
||||
|
||||
|
@ -12,6 +12,9 @@
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
@ -29,6 +32,7 @@ static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
|
||||
// Returns 1, 0 or -1 if this symbol should take precedence
|
||||
// over the Other, tie or lose, respectively.
|
||||
template <class ELFT> int SymbolBody::compare(SymbolBody *Other) {
|
||||
assert(!isLazy() && !Other->isLazy());
|
||||
std::pair<bool, bool> L(isDefined(), !isWeak());
|
||||
std::pair<bool, bool> R(Other->isDefined(), !Other->isWeak());
|
||||
|
||||
@ -67,6 +71,17 @@ template <class ELFT> int SymbolBody::compare(SymbolBody *Other) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::unique_ptr<InputFile> Lazy::getMember() {
|
||||
MemoryBufferRef MBRef = File->getMember(&Sym);
|
||||
|
||||
// getMember returns an empty buffer if the member was already
|
||||
// read from the library.
|
||||
if (MBRef.getBuffer().empty())
|
||||
return std::unique_ptr<InputFile>(nullptr);
|
||||
|
||||
return createELFFile<ObjectFile>(MBRef);
|
||||
}
|
||||
|
||||
template int SymbolBody::compare<ELF32LE>(SymbolBody *Other);
|
||||
template int SymbolBody::compare<ELF32BE>(SymbolBody *Other);
|
||||
template int SymbolBody::compare<ELF64LE>(SymbolBody *Other);
|
||||
|
@ -13,13 +13,13 @@
|
||||
#include "Chunks.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf2 {
|
||||
|
||||
using llvm::object::ELFFile;
|
||||
|
||||
class ArchiveFile;
|
||||
class Chunk;
|
||||
class InputFile;
|
||||
class SymbolBody;
|
||||
@ -42,16 +42,18 @@ public:
|
||||
DefinedAbsoluteKind = 1,
|
||||
DefinedCommonKind = 2,
|
||||
DefinedLast = 2,
|
||||
UndefinedKind = 3
|
||||
UndefinedKind = 3,
|
||||
LazyKind = 4,
|
||||
};
|
||||
|
||||
Kind kind() const { return static_cast<Kind>(SymbolKind); }
|
||||
|
||||
bool isWeak() const { return IsWeak; }
|
||||
bool isUndefined() const { return SymbolKind == UndefinedKind; }
|
||||
bool isDefined() const { return !isUndefined(); }
|
||||
bool isDefined() const { return SymbolKind <= DefinedLast; }
|
||||
bool isStrongUndefined() const { return !IsWeak && isUndefined(); }
|
||||
bool isCommon() const { return SymbolKind == DefinedCommonKind; }
|
||||
bool isLazy() const { return SymbolKind == LazyKind; }
|
||||
|
||||
// Returns the symbol name.
|
||||
StringRef getName() const { return Name; }
|
||||
@ -200,6 +202,28 @@ public:
|
||||
template <class ELFT>
|
||||
typename Undefined<ELFT>::Elf_Sym Undefined<ELFT>::Synthetic;
|
||||
|
||||
// This class represents a symbol defined in an archive file. It is
|
||||
// created from an archive file header, and it knows how to load an
|
||||
// object file from an archive to replace itself with a defined
|
||||
// symbol. If the resolver finds both Undefined and Lazy for
|
||||
// the same name, it will ask the Lazy to load a file.
|
||||
class Lazy : public SymbolBody {
|
||||
public:
|
||||
Lazy(ArchiveFile *F, const llvm::object::Archive::Symbol S)
|
||||
: SymbolBody(LazyKind, S.getName(), false, llvm::ELF::STV_DEFAULT),
|
||||
File(F), Sym(S) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) { return S->kind() == LazyKind; }
|
||||
|
||||
// Returns an object file for this symbol, or a nullptr if the file
|
||||
// was already returned.
|
||||
std::unique_ptr<InputFile> getMember();
|
||||
|
||||
private:
|
||||
ArchiveFile *File;
|
||||
const llvm::object::Archive::Symbol Sym;
|
||||
};
|
||||
|
||||
} // namespace elf2
|
||||
} // namespace lld
|
||||
|
||||
|
@ -302,6 +302,8 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
|
||||
StringRef Name = P.first;
|
||||
Symbol *Sym = P.second;
|
||||
SymbolBody *Body = Sym->Body;
|
||||
if (Body->isLazy())
|
||||
continue;
|
||||
const Elf_Sym &InputSym = cast<ELFSymbolBody<ELFT>>(Body)->Sym;
|
||||
|
||||
uint8_t V = Body->getMostConstrainingVisibility();
|
||||
@ -325,6 +327,8 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
|
||||
assert(Body->isWeak() && "Should be defined by now");
|
||||
case SymbolBody::DefinedAbsoluteKind:
|
||||
break;
|
||||
case SymbolBody::LazyKind:
|
||||
llvm_unreachable("Lazy symbol got to output symbol table!");
|
||||
}
|
||||
|
||||
ESym->setBindingAndType(InputSym.getBinding(), InputSym.getType());
|
||||
@ -450,6 +454,8 @@ template <class ELFT> void Writer<ELFT>::createSections() {
|
||||
for (auto &P : Symtab.getSymbols()) {
|
||||
StringRef Name = P.first;
|
||||
SymbolBody *Body = P.second->Body;
|
||||
if (Body->isLazy())
|
||||
continue;
|
||||
if (auto *C = dyn_cast<DefinedCommon<ELFT>>(Body))
|
||||
CommonSymbols.push_back(C);
|
||||
uint8_t V = Body->getMostConstrainingVisibility();
|
||||
|
5
lld/test/elf2/Inputs/archive.s
Normal file
5
lld/test/elf2/Inputs/archive.s
Normal file
@ -0,0 +1,5 @@
|
||||
.globl _start;
|
||||
_start:
|
||||
|
||||
.globl end;
|
||||
end:
|
13
lld/test/elf2/archive.s
Normal file
13
lld/test/elf2/archive.s
Normal file
@ -0,0 +1,13 @@
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/archive.s -o %t2
|
||||
# RUN: llvm-ar rcs %tar %t2
|
||||
# RUN: lld -flavor gnu2 %t %tar -o %tout
|
||||
# RUN: llvm-nm %tout | FileCheck %s
|
||||
# REQUIRES: x86
|
||||
|
||||
# Nothing here. Just needed for the linker to create a undefined _start symbol.
|
||||
|
||||
.quad end
|
||||
|
||||
# CHECK: T _start
|
||||
# CHECK: T end
|
Loading…
x
Reference in New Issue
Block a user