[PATCH] D12545: [elf2] Add basic archive file support.
Rafael Espíndola via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 3 13:18:09 PDT 2015
Rebased on top of current trunk.
On 3 September 2015 at 15:45, Rafael Espíndola
<rafael.espindola at gmail.com> wrote:
> I also noticed that the Files vector with all the input files (.o or
> .a) is not used. We can just have a obj vector and a archive vector.
> See the attached patch.
>
>
>
> On 3 September 2015 at 15:32, Rafael Espíndola
> <rafael.espindola at gmail.com> wrote:
>> Patch rebased on current trunk.
>>
>> On 3 September 2015 at 13:40, Rui Ueyama <ruiu at google.com> wrote:
>>> On Thu, Sep 3, 2015 at 6:17 AM, Rafael Espíndola
>>> <rafael.espindola at gmail.com> wrote:
>>>>
>>>> Another thing: I see that you moved createFile out of Driver.cpp.
>>>>
>>>> That is not what COFF does and I think I agree with COFF on this one.
>>>> The files we will support at the top level are not the ones we support
>>>> inside a .a. It is better to error early if we are given a .so or a
>>>> linker script inside an archive.
>>>
>>>
>>> Yes, that's an deliberate choice that I made. A few more examples: In COFF,
>>> import files are given only as members of archive files. Both in COFF and
>>> ELF, archive files cannot be members of archive files.
>>>
>>>>
>>>>
>>>> Cheers,
>>>> Rafael
>>>>
>>>>
>>>>
>>>> On 2 September 2015 at 16:48, Rafael Espíndola
>>>> <rafael.espindola at gmail.com> wrote:
>>>> > The warnings with the file kind were really an issue with the existing
>>>> > code. I have fixed that and rebased your patch. I also include the
>>>> > change to use std::vector<Lazy> LazySymbols;.
>>>> >
>>>> > The remaining warnings look like new issues:
>>>> >
>>>> > /home/espindola/llvm/llvm/tools/lld/ELF/Writer.cpp:317:13: warning:
>>>> > enumeration value 'LazyKind' not handled in switch [-Wswitch]
>>>> > switch (Body->kind()) {
>>>> > ^
>>>> > /home/espindola/llvm/llvm/tools/lld/ELF/Writer.cpp:317:13: warning:
>>>> > enumeration value 'LazyKind' not handled in switch [-Wswitch]
>>>> > /home/espindola/llvm/llvm/tools/lld/ELF/Writer.cpp:317:13: warning:
>>>> > enumeration value 'LazyKind' not handled in switch [-Wswitch]
>>>> > /home/espindola/llvm/llvm/tools/lld/ELF/Writer.cpp:317:13: warning:
>>>> > enumeration value 'LazyKind' not handled in switch [-Wswitch]
>>>> > /home/espindola/llvm/llvm/tools/lld/ELF/Writer.cpp:317:13: warning:
>>>> > enumeration value 'LazyKind' not handled in switch [-Wswitch
>>>> >
>>>> > Please fix them and upload a new patch.
>>>> >
>>>> >
>>>> > On 2 September 2015 at 11:50, Rafael Espíndola
>>>> > <rafael.espindola at gmail.com> wrote:
>>>> >> Why use a llvm::MallocAllocator? It seems better to not have an
>>>> >> allocator and use a smart pointer or use a BumpPtrAllocator.
>>>> >>
>>>> >> Given that the allocator is used for exactly one allocation per file,
>>>> >> wouldn't it be the same to replace
>>>> >>
>>>> >> std::vector<Lazy *> LazySymbols;
>>>> >> llvm::MallocAllocator Alloc;
>>>> >>
>>>> >> With
>>>> >>
>>>> >> std::vector<Lazy> LazySymbols
>>>> >> ?
>>>> >>
>>>> >> On 2 September 2015 at 11:33, Rafael Ávila de Espíndola
>>>> >> <llvm-commits at lists.llvm.org> wrote:
>>>> >>> rafael added inline comments.
>>>> >>>
>>>> >>> ================
>>>> >>> Comment at: ELF/InputFiles.cpp:15
>>>> >>> @@ -14,2 +14,3 @@
>>>> >>> #include "llvm/ADT/STLExtras.h"
>>>> >>> +#include "llvm/Support/FileSystem.h"
>>>> >>>
>>>> >>> ----------------
>>>> >>> Not used.
>>>> >>>
>>>> >>>
>>>> >>> http://reviews.llvm.org/D12545
>>>> >>>
>>>> >>>
>>>> >>>
>>>> >>> _______________________________________________
>>>> >>> llvm-commits mailing list
>>>> >>> llvm-commits at lists.llvm.org
>>>> >>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>>
>>>
-------------- next part --------------
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 7da02a0..13d8d86 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -57,6 +57,9 @@ static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) {
using namespace llvm::sys::fs;
file_magic Magic = identify_magic(MB.getBuffer());
+ if (Magic == file_magic::archive)
+ return llvm::make_unique<ArchiveFile>(MB);
+
std::pair<unsigned char, unsigned char> Type =
object::getElfArchType(MB.getBuffer());
if (Type.second != ELF::ELFDATA2LSB && Type.second != ELF::ELFDATA2MSB)
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index b856fe5..08efa5c 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -13,9 +13,13 @@
#include "Symbols.h"
#include "llvm/ADT/STLExtras.h"
+using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using llvm::sys::fs::identify_magic;
+using llvm::sys::fs::file_magic;
+
using namespace lld;
using namespace lld::elf2;
@@ -149,6 +153,39 @@ 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) {
+ auto ItOrErr = Sym->getMember();
+ error(ItOrErr,
+ Twine("Could not get the member for symbol ") + Sym->getName());
+ Archive::child_iterator It = *ItOrErr;
+
+ // Return an empty buffer if we have already returned the same buffer.
+ bool &SeenMember = Seen[It->getChildOffset()];
+ if (SeenMember) {
+ return MemoryBufferRef();
+ }
+ SeenMember = true;
+ 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 {
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index e5dc7c3..75492cf 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -14,16 +14,22 @@
#include "Symbols.h"
#include "lld/Core/LLVM.h"
+#include "llvm/ADT/DenseMap.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 +143,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::DenseMap<uint64_t, bool> Seen;
+};
+
// .so file.
class SharedFileBase : public ELFFileBase {
public:
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index b7803a2..b7a0c57 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -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() {
@@ -88,22 +93,65 @@ 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);
+ return Sym;
+}
- // compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
- // equivalent (conflicting), or more preferable, respectively.
+void SymbolTable::addLazy(Lazy *New) {
+ Symbol *Sym = insert(New);
+ if (Sym->Body == New)
+ return;
SymbolBody *Existing = Sym->Body;
- int comp = Existing->compare<ELFT>(New);
- if (comp < 0)
- Sym->Body = New;
- if (comp == 0)
- error(Twine("duplicate symbol: ") + Name);
+ if (Existing->isDefined() || isa<Lazy>(Existing))
+ return;
+ Sym->Body = New;
+ if (Existing->isUndefined())
+ addMemberFile(New);
+}
+
+void SymbolTable::addMemberFile(Lazy *Body) {
+ std::unique_ptr<InputFile> File = Body->getMember();
+
+ // getMember returns an empty buffer if the member was already
+ // read from the library.
+ if (!File)
+ return;
+
+ addFile(std::move(File));
}
diff --git a/ELF/SymbolTable.h b/ELF/SymbolTable.h
index 80dc65e..1854f6c 100644
--- a/ELF/SymbolTable.h
+++ b/ELF/SymbolTable.h
@@ -54,11 +54,16 @@ public:
}
private:
+ Symbol *insert(SymbolBody *New);
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;
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index 37e0033..3d20617 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -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,36 @@ template <class ELFT> int SymbolBody::compare(SymbolBody *Other) {
return 1;
}
+static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) {
+ // FIXME: duplicated
+ std::pair<unsigned char, unsigned char> Type = getElfArchType(MB.getBuffer());
+ if (Type.second != ELFDATA2LSB && Type.second != ELFDATA2MSB)
+ error("Invalid data encoding");
+
+ if (Type.first == ELFCLASS32) {
+ if (Type.second == ELFDATA2LSB)
+ return make_unique<ObjectFile<ELF32LE>>(MB);
+ return make_unique<ObjectFile<ELF32BE>>(MB);
+ }
+ if (Type.first == ELFCLASS64) {
+ if (Type.second == ELFDATA2LSB)
+ return make_unique<ObjectFile<ELF64LE>>(MB);
+ return make_unique<ObjectFile<ELF64BE>>(MB);
+ }
+ error("Invalid file class");
+}
+
+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 createFile(MBRef);
+}
+
template int SymbolBody::compare<ELF32LE>(SymbolBody *Other);
template int SymbolBody::compare<ELF32BE>(SymbolBody *Other);
template int SymbolBody::compare<ELF64LE>(SymbolBody *Other);
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index 5051890..fd27d96 100644
--- a/ELF/Symbols.h
+++ b/ELF/Symbols.h
@@ -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,7 +42,8 @@ public:
DefinedAbsoluteKind = 1,
DefinedCommonKind = 2,
DefinedLast = 2,
- UndefinedKind = 3
+ UndefinedKind = 3,
+ LazyKind = 4,
};
Kind kind() const { return static_cast<Kind>(SymbolKind); }
@@ -52,6 +53,7 @@ public:
bool isDefined() const { return !isUndefined(); }
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
diff --git a/test/elf2/archive.s b/test/elf2/archive.s
new file mode 100644
index 0000000..7a7c1c0
--- /dev/null
+++ b/test/elf2/archive.s
@@ -0,0 +1,21 @@
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/basic.s -o %t2
+// RUN: llvm-ar rcs %tar %t2
+// RUN: lld -flavor gnu2 %t %tar -o %tout
+// RUN: llvm-objdump -t -d %tout | FileCheck %s
+// REQUIRES: x86
+
+
+.section .text,"ax"
+call _start
+
+// CHECK: Disassembly of section .text:
+// CHECK: .text:
+// CHECK: e8 03 00 00 00 callq 3
+// CHECK: _start:
+// CHECK: 48 c7 c0 3c 00 00 00 movq $60, %rax
+// CHECK: 48 c7 c7 2a 00 00 00 movq $42, %rdi
+// CHECK: 0f 05 syscall
+// CHECK: SYMBOL TABLE:
+// CHECK: 0000000000000000 *UND* 00000000
+// CHECK: 0000000000001008 .text 00000000 _start
More information about the llvm-commits
mailing list