[PATCH] D24492: [LTO] Switch to the new resolution-based API.
Rafael EspĂndola via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 28 10:29:12 PDT 2016
rebased version attached, taking another look.
On 28 September 2016 at 10:18, Davide Italiano <dccitaliano at gmail.com> wrote:
> davide added inline comments.
>
> ================
> Comment at: lld/ELF/InputFiles.cpp:661
> @@ -679,1 +660,3 @@
> + return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
> + CanOmitFromDynSym, false, F);
>
> ----------------
> rafael wrote:
>> You have lost HasUnnamedAddr. Where do you compensate for that?
> I think it's not needed anymore becuase lib/LTO will compute it based on `GlobalResolution`.
> I'm garbage collecting it for now.
>
>
> https://reviews.llvm.org/D24492
>
>
>
-------------- next part --------------
diff --git a/lld/ELF/Error.h b/lld/ELF/Error.h
index 5887e4c..88af5bf 100644
--- a/lld/ELF/Error.h
+++ b/lld/ELF/Error.h
@@ -1,61 +1,70 @@
//===- Error.h --------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_ERROR_H
#define LLD_ELF_ERROR_H
#include "lld/Core/LLVM.h"
+#include "llvm/Support/Error.h"
+
namespace lld {
namespace elf {
extern bool HasError;
extern llvm::raw_ostream *ErrorOS;
void log(const Twine &Msg);
void warning(const Twine &Msg);
void error(const Twine &Msg);
void error(std::error_code EC, const Twine &Prefix);
template <typename T> void error(const ErrorOr<T> &V, const Twine &Prefix) {
error(V.getError(), Prefix);
}
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg, const Twine &Prefix);
+inline void check(Error E, std::string Msg = "") {
+ handleAllErrors(std::move(E), [&](llvm::ErrorInfoBase &EIB) {
+ error(EIB.message().c_str());
+ return Error::success();
+ });
+}
+
template <class T> T check(ErrorOr<T> E) {
if (auto EC = E.getError())
fatal(EC.message());
return std::move(*E);
}
template <class T> T check(Expected<T> E) {
if (!E)
fatal(errorToErrorCode(E.takeError()).message());
return std::move(*E);
}
template <class T> T check(ErrorOr<T> E, const Twine &Prefix) {
if (auto EC = E.getError())
fatal(EC.message(), Prefix);
return std::move(*E);
}
template <class T> T check(Expected<T> E, const Twine &Prefix) {
if (!E)
fatal(errorToErrorCode(E.takeError()).message(), Prefix);
return std::move(*E);
}
} // namespace elf
} // namespace lld
#endif
diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp
index d15a705..c2398a6 100644
--- a/lld/ELF/InputFiles.cpp
+++ b/lld/ELF/InputFiles.cpp
@@ -1,915 +1,871 @@
//===- InputFiles.cpp -----------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
#include "Driver.h"
#include "ELFCreator.h"
#include "Error.h"
#include "InputSection.h"
#include "LinkerScript.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/LTO/LTO.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::sys::fs;
using namespace lld;
using namespace lld::elf;
std::vector<InputFile *> InputFile::Pool;
// Deletes all InputFile instances created so far.
void InputFile::freePool() {
// Files are freed in reverse order so that files created
// from other files (e.g. object files extracted from archives)
// are freed in the proper order.
for (int I = Pool.size() - 1; I >= 0; --I)
delete Pool[I];
}
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
std::string elf::getFilename(const InputFile *F) {
if (!F)
return "(internal)";
if (!F->ArchiveName.empty())
return (F->ArchiveName + "(" + F->getName() + ")").str();
return F->getName();
}
template <class ELFT>
static ELFFile<ELFT> createELFObj(MemoryBufferRef MB) {
std::error_code EC;
ELFFile<ELFT> F(MB.getBuffer(), EC);
if (EC)
error(EC, "failed to read " + MB.getBufferIdentifier());
return F;
}
template <class ELFT> static ELFKind getELFKind() {
if (ELFT::TargetEndianness == support::little)
return ELFT::Is64Bits ? ELF64LEKind : ELF32LEKind;
return ELFT::Is64Bits ? ELF64BEKind : ELF32BEKind;
}
template <class ELFT>
ELFFileBase<ELFT>::ELFFileBase(Kind K, MemoryBufferRef MB)
: InputFile(K, MB), ELFObj(createELFObj<ELFT>(MB)) {
EKind = getELFKind<ELFT>();
EMachine = ELFObj.getHeader()->e_machine;
}
template <class ELFT>
typename ELFT::SymRange ELFFileBase<ELFT>::getElfSymbols(bool OnlyGlobals) {
if (!Symtab)
return Elf_Sym_Range(nullptr, nullptr);
Elf_Sym_Range Syms = ELFObj.symbols(Symtab);
uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
uint32_t FirstNonLocal = Symtab->sh_info;
if (FirstNonLocal > NumSymbols)
fatal(getFilename(this) + ": invalid sh_info in symbol table");
if (OnlyGlobals)
return makeArrayRef(Syms.begin() + FirstNonLocal, Syms.end());
return makeArrayRef(Syms.begin(), Syms.end());
}
template <class ELFT>
uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
uint32_t I = Sym.st_shndx;
if (I == ELF::SHN_XINDEX)
return ELFObj.getExtendedSymbolTableIndex(&Sym, Symtab, SymtabSHNDX);
if (I >= ELF::SHN_LORESERVE)
return 0;
return I;
}
template <class ELFT> void ELFFileBase<ELFT>::initStringTable() {
if (!Symtab)
return;
StringTable = check(ELFObj.getStringTableForSymtab(*Symtab));
}
template <class ELFT>
elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M)
: ELFFileBase<ELFT>(Base::ObjectKind, M) {}
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getNonLocalSymbols() {
if (!this->Symtab)
return this->SymbolBodies;
uint32_t FirstNonLocal = this->Symtab->sh_info;
return makeArrayRef(this->SymbolBodies).slice(FirstNonLocal);
}
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getLocalSymbols() {
if (!this->Symtab)
return this->SymbolBodies;
uint32_t FirstNonLocal = this->Symtab->sh_info;
return makeArrayRef(this->SymbolBodies).slice(1, FirstNonLocal - 1);
}
template <class ELFT>
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getSymbols() {
if (!this->Symtab)
return this->SymbolBodies;
return makeArrayRef(this->SymbolBodies).slice(1);
}
template <class ELFT> uint32_t elf::ObjectFile<ELFT>::getMipsGp0() const {
if (ELFT::Is64Bits && MipsOptions && MipsOptions->Reginfo)
return MipsOptions->Reginfo->ri_gp_value;
if (!ELFT::Is64Bits && MipsReginfo && MipsReginfo->Reginfo)
return MipsReginfo->Reginfo->ri_gp_value;
return 0;
}
template <class ELFT>
void elf::ObjectFile<ELFT>::parse(DenseSet<StringRef> &ComdatGroups) {
// Read section and symbol tables.
initializeSections(ComdatGroups);
initializeSymbols();
}
// Sections with SHT_GROUP and comdat bits define comdat section groups.
// They are identified and deduplicated by group name. This function
// returns a group name.
template <class ELFT>
StringRef elf::ObjectFile<ELFT>::getShtGroupSignature(const Elf_Shdr &Sec) {
const ELFFile<ELFT> &Obj = this->ELFObj;
const Elf_Shdr *Symtab = check(Obj.getSection(Sec.sh_link));
const Elf_Sym *Sym = Obj.getSymbol(Symtab, Sec.sh_info);
StringRef Strtab = check(Obj.getStringTableForSymtab(*Symtab));
return check(Sym->getName(Strtab));
}
template <class ELFT>
ArrayRef<typename elf::ObjectFile<ELFT>::Elf_Word>
elf::ObjectFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
const ELFFile<ELFT> &Obj = this->ELFObj;
ArrayRef<Elf_Word> Entries =
check(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec));
if (Entries.empty() || Entries[0] != GRP_COMDAT)
fatal(getFilename(this) + ": unsupported SHT_GROUP format");
return Entries.slice(1);
}
template <class ELFT>
bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
// We don't merge sections if -O0 (default is -O1). This makes sometimes
// the linker significantly faster, although the output will be bigger.
if (Config->Optimize == 0)
return false;
// A mergeable section with size 0 is useless because they don't have
// any data to merge. A mergeable string section with size 0 can be
// argued as invalid because it doesn't end with a null character.
// We'll avoid a mess by handling them as if they were non-mergeable.
if (Sec.sh_size == 0)
return false;
// Check for sh_entsize. The ELF spec is not clear about the zero
// sh_entsize. It says that "the member [sh_entsize] contains 0 if
// the section does not hold a table of fixed-size entries". We know
// that Rust 1.13 produces a string mergeable section with a zero
// sh_entsize. Here we just accept it rather than being picky about it.
uintX_t EntSize = Sec.sh_entsize;
if (EntSize == 0)
return false;
if (Sec.sh_size % EntSize)
fatal(getFilename(this) +
": SHF_MERGE section size must be a multiple of sh_entsize");
uintX_t Flags = Sec.sh_flags;
if (!(Flags & SHF_MERGE))
return false;
if (Flags & SHF_WRITE)
fatal(getFilename(this) + ": writable SHF_MERGE section is not supported");
// Don't try to merge if the alignment is larger than the sh_entsize and this
// is not SHF_STRINGS.
//
// Since this is not a SHF_STRINGS, we would need to pad after every entity.
// It would be equivalent for the producer of the .o to just set a larger
// sh_entsize.
if (Flags & SHF_STRINGS)
return true;
return Sec.sh_addralign <= EntSize;
}
template <class ELFT>
void elf::ObjectFile<ELFT>::initializeSections(
DenseSet<StringRef> &ComdatGroups) {
uint64_t Size = this->ELFObj.getNumSections();
Sections.resize(Size);
unsigned I = -1;
const ELFFile<ELFT> &Obj = this->ELFObj;
for (const Elf_Shdr &Sec : Obj.sections()) {
++I;
if (Sections[I] == &InputSection<ELFT>::Discarded)
continue;
if (Sec.sh_flags & SHF_EXCLUDE) {
Sections[I] = &InputSection<ELFT>::Discarded;
continue;
}
switch (Sec.sh_type) {
case SHT_GROUP:
Sections[I] = &InputSection<ELFT>::Discarded;
if (ComdatGroups.insert(getShtGroupSignature(Sec)).second)
continue;
for (uint32_t SecIndex : getShtGroupEntries(Sec)) {
if (SecIndex >= Size)
fatal(getFilename(this) + ": invalid section index in group: " +
Twine(SecIndex));
Sections[SecIndex] = &InputSection<ELFT>::Discarded;
}
break;
case SHT_SYMTAB:
this->Symtab = &Sec;
break;
case SHT_SYMTAB_SHNDX:
this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec));
break;
case SHT_STRTAB:
case SHT_NULL:
break;
default:
Sections[I] = createInputSection(Sec);
}
}
}
template <class ELFT>
InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
uint32_t Idx = Sec.sh_info;
if (Idx >= Sections.size())
fatal(getFilename(this) + ": invalid relocated section index: " +
Twine(Idx));
InputSectionBase<ELFT> *Target = Sections[Idx];
// Strictly speaking, a relocation section must be included in the
// group of the section it relocates. However, LLVM 3.3 and earlier
// would fail to do so, so we gracefully handle that case.
if (Target == &InputSection<ELFT>::Discarded)
return nullptr;
if (!Target)
fatal(getFilename(this) + ": unsupported relocation reference");
return Target;
}
template <class ELFT>
InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec) {
StringRef Name = check(this->ELFObj.getSectionName(&Sec));
switch (Sec.sh_type) {
case SHT_ARM_ATTRIBUTES:
// FIXME: ARM meta-data section. At present attributes are ignored,
// they can be used to reason about object compatibility.
return &InputSection<ELFT>::Discarded;
case SHT_MIPS_REGINFO:
MipsReginfo.reset(new MipsReginfoInputSection<ELFT>(this, &Sec, Name));
return MipsReginfo.get();
case SHT_MIPS_OPTIONS:
MipsOptions.reset(new MipsOptionsInputSection<ELFT>(this, &Sec, Name));
return MipsOptions.get();
case SHT_MIPS_ABIFLAGS:
MipsAbiFlags.reset(new MipsAbiFlagsInputSection<ELFT>(this, &Sec, Name));
return MipsAbiFlags.get();
case SHT_RELA:
case SHT_REL: {
// This section contains relocation information.
// If -r is given, we do not interpret or apply relocation
// but just copy relocation sections to output.
if (Config->Relocatable)
return new (IAlloc.Allocate()) InputSection<ELFT>(this, &Sec, Name);
// Find the relocation target section and associate this
// section with it.
InputSectionBase<ELFT> *Target = getRelocTarget(Sec);
if (!Target)
return nullptr;
if (auto *S = dyn_cast<InputSection<ELFT>>(Target)) {
S->RelocSections.push_back(&Sec);
return nullptr;
}
if (auto *S = dyn_cast<EhInputSection<ELFT>>(Target)) {
if (S->RelocSection)
fatal(getFilename(this) +
": multiple relocation sections to .eh_frame are not supported");
S->RelocSection = &Sec;
return nullptr;
}
fatal(getFilename(this) +
": relocations pointing to SHF_MERGE are not supported");
}
}
// .note.GNU-stack is a marker section to control the presence of
// PT_GNU_STACK segment in outputs. Since the presence of the segment
// is controlled only by the command line option (-z execstack) in LLD,
// .note.GNU-stack is ignored.
if (Name == ".note.GNU-stack")
return &InputSection<ELFT>::Discarded;
if (Name == ".note.GNU-split-stack") {
error("objects using splitstacks are not supported");
return &InputSection<ELFT>::Discarded;
}
if (Config->Strip != StripPolicy::None && Name.startswith(".debug"))
return &InputSection<ELFT>::Discarded;
// The linker merges EH (exception handling) frames and creates a
// .eh_frame_hdr section for runtime. So we handle them with a special
// class. For relocatable outputs, they are just passed through.
if (Name == ".eh_frame" && !Config->Relocatable)
return new (EHAlloc.Allocate()) EhInputSection<ELFT>(this, &Sec, Name);
if (shouldMerge(Sec))
return new (MAlloc.Allocate()) MergeInputSection<ELFT>(this, &Sec, Name);
return new (IAlloc.Allocate()) InputSection<ELFT>(this, &Sec, Name);
}
template <class ELFT> void elf::ObjectFile<ELFT>::initializeSymbols() {
this->initStringTable();
Elf_Sym_Range Syms = this->getElfSymbols(false);
uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end());
SymbolBodies.reserve(NumSymbols);
for (const Elf_Sym &Sym : Syms)
SymbolBodies.push_back(createSymbolBody(&Sym));
}
template <class ELFT>
InputSectionBase<ELFT> *
elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
uint32_t Index = this->getSectionIndex(Sym);
if (Index == 0)
return nullptr;
if (Index >= Sections.size())
fatal(getFilename(this) + ": invalid section index: " + Twine(Index));
InputSectionBase<ELFT> *S = Sections[Index];
// We found that GNU assembler 2.17.50 [FreeBSD] 2007-07-03
// could generate broken objects. STT_SECTION symbols can be
// associated with SHT_REL[A]/SHT_SYMTAB/SHT_STRTAB sections.
// In this case it is fine for section to be null here as we
// do not allocate sections of these types.
if (!S || S == &InputSectionBase<ELFT>::Discarded)
return S;
return S->Repl;
}
template <class ELFT>
SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
int Binding = Sym->getBinding();
InputSectionBase<ELFT> *Sec = getSection(*Sym);
if (Binding == STB_LOCAL) {
if (Sym->st_shndx == SHN_UNDEF)
return new (this->Alloc)
Undefined(Sym->st_name, Sym->st_other, Sym->getType(), this);
return new (this->Alloc) DefinedRegular<ELFT>(*Sym, Sec);
}
StringRef Name = check(Sym->getName(this->StringTable));
switch (Sym->st_shndx) {
case SHN_UNDEF:
return elf::Symtab<ELFT>::X
->addUndefined(Name, Binding, Sym->st_other, Sym->getType(),
- /*CanOmitFromDynSym*/ false, /*HasUnnamedAddr*/ false,
- this)
+ /*CanOmitFromDynSym*/ false, this)
->body();
case SHN_COMMON:
return elf::Symtab<ELFT>::X
->addCommon(Name, Sym->st_size, Sym->st_value, Binding, Sym->st_other,
- Sym->getType(), /*HasUnnamedAddr*/ false, this)
+ Sym->getType(), this)
->body();
}
switch (Binding) {
default:
fatal(getFilename(this) + ": unexpected binding: " + Twine(Binding));
case STB_GLOBAL:
case STB_WEAK:
case STB_GNU_UNIQUE:
if (Sec == &InputSection<ELFT>::Discarded)
return elf::Symtab<ELFT>::X
->addUndefined(Name, Binding, Sym->st_other, Sym->getType(),
- /*CanOmitFromDynSym*/ false,
- /*HasUnnamedAddr*/ false, this)
+ /*CanOmitFromDynSym*/ false, this)
->body();
return elf::Symtab<ELFT>::X->addRegular(Name, *Sym, Sec)->body();
}
}
template <class ELFT> void ArchiveFile::parse() {
File = check(Archive::create(MB), "failed to parse archive");
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &Sym : File->symbols())
Symtab<ELFT>::X->addLazyArchive(this, Sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
MemoryBufferRef ArchiveFile::getMember(const Archive::Symbol *Sym) {
Archive::Child C =
check(Sym->getMember(),
"could not get the member for symbol " + Sym->getName());
if (!Seen.insert(C.getChildOffset()).second)
return MemoryBufferRef();
MemoryBufferRef Ret =
check(C.getMemoryBufferRef(),
"could not get the buffer for the member defining symbol " +
Sym->getName());
if (C.getParent()->isThin() && Driver->Cpio)
Driver->Cpio->append(relativeToRoot(check(C.getFullName())),
Ret.getBuffer());
return Ret;
}
template <class ELFT>
SharedFile<ELFT>::SharedFile(MemoryBufferRef M)
: ELFFileBase<ELFT>(Base::SharedKind, M), AsNeeded(Config->AsNeeded) {}
template <class ELFT>
const typename ELFT::Shdr *
SharedFile<ELFT>::getSection(const Elf_Sym &Sym) const {
uint32_t Index = this->getSectionIndex(Sym);
if (Index == 0)
return nullptr;
return check(this->ELFObj.getSection(Index));
}
// Partially parse the shared object file so that we can call
// getSoName on this object.
template <class ELFT> void SharedFile<ELFT>::parseSoName() {
typedef typename ELFT::Dyn Elf_Dyn;
typedef typename ELFT::uint uintX_t;
const Elf_Shdr *DynamicSec = nullptr;
const ELFFile<ELFT> Obj = this->ELFObj;
for (const Elf_Shdr &Sec : Obj.sections()) {
switch (Sec.sh_type) {
default:
continue;
case SHT_DYNSYM:
this->Symtab = &Sec;
break;
case SHT_DYNAMIC:
DynamicSec = &Sec;
break;
case SHT_SYMTAB_SHNDX:
this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec));
break;
case SHT_GNU_versym:
this->VersymSec = &Sec;
break;
case SHT_GNU_verdef:
this->VerdefSec = &Sec;
break;
}
}
this->initStringTable();
// DSOs are identified by soname, and they usually contain
// DT_SONAME tag in their header. But if they are missing,
// filenames are used as default sonames.
SoName = sys::path::filename(this->getName());
if (!DynamicSec)
return;
auto *Begin =
reinterpret_cast<const Elf_Dyn *>(Obj.base() + DynamicSec->sh_offset);
const Elf_Dyn *End = Begin + DynamicSec->sh_size / sizeof(Elf_Dyn);
for (const Elf_Dyn &Dyn : make_range(Begin, End)) {
if (Dyn.d_tag == DT_SONAME) {
uintX_t Val = Dyn.getVal();
if (Val >= this->StringTable.size())
fatal(getFilename(this) + ": invalid DT_SONAME entry");
SoName = StringRef(this->StringTable.data() + Val);
return;
}
}
}
// Parse the version definitions in the object file if present. Returns a vector
// whose nth element contains a pointer to the Elf_Verdef for version identifier
// n. Version identifiers that are not definitions map to nullptr. The array
// always has at least length 1.
template <class ELFT>
std::vector<const typename ELFT::Verdef *>
SharedFile<ELFT>::parseVerdefs(const Elf_Versym *&Versym) {
std::vector<const Elf_Verdef *> Verdefs(1);
// We only need to process symbol versions for this DSO if it has both a
// versym and a verdef section, which indicates that the DSO contains symbol
// version definitions.
if (!VersymSec || !VerdefSec)
return Verdefs;
// The location of the first global versym entry.
Versym = reinterpret_cast<const Elf_Versym *>(this->ELFObj.base() +
VersymSec->sh_offset) +
this->Symtab->sh_info;
// We cannot determine the largest verdef identifier without inspecting
// every Elf_Verdef, but both bfd and gold assign verdef identifiers
// sequentially starting from 1, so we predict that the largest identifier
// will be VerdefCount.
unsigned VerdefCount = VerdefSec->sh_info;
Verdefs.resize(VerdefCount + 1);
// Build the Verdefs array by following the chain of Elf_Verdef objects
// from the start of the .gnu.version_d section.
const uint8_t *Verdef = this->ELFObj.base() + VerdefSec->sh_offset;
for (unsigned I = 0; I != VerdefCount; ++I) {
auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
Verdef += CurVerdef->vd_next;
unsigned VerdefIndex = CurVerdef->vd_ndx;
if (Verdefs.size() <= VerdefIndex)
Verdefs.resize(VerdefIndex + 1);
Verdefs[VerdefIndex] = CurVerdef;
}
return Verdefs;
}
// Fully parse the shared object file. This must be called after parseSoName().
template <class ELFT> void SharedFile<ELFT>::parseRest() {
// Create mapping from version identifiers to Elf_Verdef entries.
const Elf_Versym *Versym = nullptr;
std::vector<const Elf_Verdef *> Verdefs = parseVerdefs(Versym);
Elf_Sym_Range Syms = this->getElfSymbols(true);
for (const Elf_Sym &Sym : Syms) {
unsigned VersymIndex = 0;
if (Versym) {
VersymIndex = Versym->vs_index;
++Versym;
}
StringRef Name = check(Sym.getName(this->StringTable));
if (Sym.isUndefined()) {
Undefs.push_back(Name);
continue;
}
if (Versym) {
// Ignore local symbols and non-default versions.
if (VersymIndex == VER_NDX_LOCAL || (VersymIndex & VERSYM_HIDDEN))
continue;
}
const Elf_Verdef *V =
VersymIndex == VER_NDX_GLOBAL ? nullptr : Verdefs[VersymIndex];
elf::Symtab<ELFT>::X->addShared(this, Name, Sym, V);
}
}
static ELFKind getBitcodeELFKind(MemoryBufferRef MB) {
Triple T(getBitcodeTargetTriple(MB, Driver->Context));
if (T.isLittleEndian())
return T.isArch64Bit() ? ELF64LEKind : ELF32LEKind;
return T.isArch64Bit() ? ELF64BEKind : ELF32BEKind;
}
static uint8_t getBitcodeMachineKind(MemoryBufferRef MB) {
Triple T(getBitcodeTargetTriple(MB, Driver->Context));
switch (T.getArch()) {
case Triple::aarch64:
return EM_AARCH64;
case Triple::arm:
return EM_ARM;
case Triple::mips:
case Triple::mipsel:
case Triple::mips64:
case Triple::mips64el:
return EM_MIPS;
case Triple::ppc:
return EM_PPC;
case Triple::ppc64:
return EM_PPC64;
case Triple::x86:
return T.isOSIAMCU() ? EM_IAMCU : EM_386;
case Triple::x86_64:
return EM_X86_64;
default:
fatal(MB.getBufferIdentifier() +
": could not infer e_machine from bitcode target triple " + T.str());
}
}
BitcodeFile::BitcodeFile(MemoryBufferRef MB) : InputFile(BitcodeKind, MB) {
EKind = getBitcodeELFKind(MB);
EMachine = getBitcodeMachineKind(MB);
}
-static uint8_t getGvVisibility(const GlobalValue *GV) {
- switch (GV->getVisibility()) {
+static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
+ switch (GvVisibility) {
case GlobalValue::DefaultVisibility:
return STV_DEFAULT;
case GlobalValue::HiddenVisibility:
return STV_HIDDEN;
case GlobalValue::ProtectedVisibility:
return STV_PROTECTED;
}
llvm_unreachable("unknown visibility");
}
template <class ELFT>
-Symbol *BitcodeFile::createSymbol(const DenseSet<const Comdat *> &KeptComdats,
- const IRObjectFile &Obj,
- const BasicSymbolRef &Sym) {
- const GlobalValue *GV = Obj.getSymbolGV(Sym.getRawDataRefImpl());
-
- SmallString<64> Name;
- raw_svector_ostream OS(Name);
- Sym.printName(OS);
- StringRef NameRef = Saver.save(StringRef(Name));
-
- uint32_t Flags = Sym.getFlags();
+static Symbol *createBitcodeSymbol(const DenseSet<const Comdat *> &KeptComdats,
+ const lto::InputFile &Obj,
+ const lto::InputFile::Symbol &ObjSym,
+ llvm::StringSaver &Saver, BitcodeFile *F) {
+ StringRef NameRef = Saver.save(ObjSym.getName());
+ uint32_t Flags = ObjSym.getFlags();
uint32_t Binding = (Flags & BasicSymbolRef::SF_Weak) ? STB_WEAK : STB_GLOBAL;
- uint8_t Type = STT_NOTYPE;
- uint8_t Visibility;
- bool CanOmitFromDynSym = false;
- bool HasUnnamedAddr = false;
-
- // FIXME: Expose a thread-local flag for module asm symbols.
- if (GV) {
- if (GV->isThreadLocal())
- Type = STT_TLS;
- CanOmitFromDynSym = canBeOmittedFromSymbolTable(GV);
- Visibility = getGvVisibility(GV);
- HasUnnamedAddr =
- GV->getUnnamedAddr() == llvm::GlobalValue::UnnamedAddr::Global;
- } else {
- // FIXME: Set SF_Hidden flag correctly for module asm symbols, and expose
- // protected visibility.
- Visibility = STV_DEFAULT;
- }
+ uint8_t Type = (ObjSym.isTLS()) ? STT_TLS : STT_NOTYPE;
+ uint8_t Visibility = mapVisibility(ObjSym.getVisibility());
+ bool CanOmitFromDynSym = ObjSym.canBeOmittedFromSymbolTable();
- if (GV)
- if (const Comdat *C = GV->getComdat())
- if (!KeptComdats.count(C))
- return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
- CanOmitFromDynSym, HasUnnamedAddr,
- this);
+ if (const Comdat *C = check(ObjSym.getComdat()))
+ if (!KeptComdats.count(C))
+ return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
+ CanOmitFromDynSym, F);
- const Module &M = Obj.getModule();
if (Flags & BasicSymbolRef::SF_Undefined)
return Symtab<ELFT>::X->addUndefined(NameRef, Binding, Visibility, Type,
- CanOmitFromDynSym, HasUnnamedAddr,
- this);
- if (Flags & BasicSymbolRef::SF_Common) {
- // FIXME: Set SF_Common flag correctly for module asm symbols, and expose
- // size and alignment.
- assert(GV);
- const DataLayout &DL = M.getDataLayout();
- uint64_t Size = DL.getTypeAllocSize(GV->getValueType());
- return Symtab<ELFT>::X->addCommon(NameRef, Size, GV->getAlignment(),
- Binding, Visibility, STT_OBJECT,
- HasUnnamedAddr, this);
- }
- return Symtab<ELFT>::X->addBitcode(NameRef, Binding, Visibility, Type,
- CanOmitFromDynSym, HasUnnamedAddr, this);
-}
+ CanOmitFromDynSym, F);
+
+ if (Flags & BasicSymbolRef::SF_Common)
+ return Symtab<ELFT>::X->addCommon(NameRef, ObjSym.getCommonSize(),
+ ObjSym.getCommonAlignment(), Binding,
+ Visibility, STT_OBJECT, F);
-bool BitcodeFile::shouldSkip(uint32_t Flags) {
- return !(Flags & BasicSymbolRef::SF_Global) ||
- (Flags & BasicSymbolRef::SF_FormatSpecific);
+ return Symtab<ELFT>::X->addBitcode(NameRef, Binding, Visibility, Type,
+ CanOmitFromDynSym, F);
}
template <class ELFT>
void BitcodeFile::parse(DenseSet<StringRef> &ComdatGroups) {
- Obj = check(IRObjectFile::create(MB, Driver->Context));
- const Module &M = Obj->getModule();
-
+ Obj = check(llvm::lto::InputFile::create(MB));
DenseSet<const Comdat *> KeptComdats;
- for (const auto &P : M.getComdatSymbolTable()) {
+ for (const auto &P : Obj->getComdatSymbolTable()) {
StringRef N = Saver.save(P.first());
if (ComdatGroups.insert(N).second)
KeptComdats.insert(&P.second);
}
- for (const BasicSymbolRef &Sym : Obj->symbols())
- if (!shouldSkip(Sym.getFlags()))
- Symbols.push_back(createSymbol<ELFT>(KeptComdats, *Obj, Sym));
+ for (auto &ObjSym : Obj->symbols())
+ Symbols.push_back(
+ createBitcodeSymbol<ELFT>(KeptComdats, *Obj, ObjSym, Saver, this));
}
template <template <class> class T>
static InputFile *createELFFile(MemoryBufferRef MB) {
unsigned char Size;
unsigned char Endian;
std::tie(Size, Endian) = getElfArchType(MB.getBuffer());
if (Endian != ELFDATA2LSB && Endian != ELFDATA2MSB)
fatal("invalid data encoding: " + MB.getBufferIdentifier());
InputFile *Obj;
if (Size == ELFCLASS32 && Endian == ELFDATA2LSB)
Obj = new T<ELF32LE>(MB);
else if (Size == ELFCLASS32 && Endian == ELFDATA2MSB)
Obj = new T<ELF32BE>(MB);
else if (Size == ELFCLASS64 && Endian == ELFDATA2LSB)
Obj = new T<ELF64LE>(MB);
else if (Size == ELFCLASS64 && Endian == ELFDATA2MSB)
Obj = new T<ELF64BE>(MB);
else
fatal("invalid file class: " + MB.getBufferIdentifier());
if (!Config->FirstElf)
Config->FirstElf = Obj;
return Obj;
}
template <class ELFT> InputFile *BinaryFile::createELF() {
// Wrap the binary blob with an ELF header and footer
// so that we can link it as a regular ELF file.
ELFCreator<ELFT> ELF(ET_REL, Config->EMachine);
auto DataSec = ELF.addSection(".data");
DataSec.Header->sh_flags = SHF_ALLOC;
DataSec.Header->sh_size = MB.getBufferSize();
DataSec.Header->sh_type = SHT_PROGBITS;
DataSec.Header->sh_addralign = 8;
std::string Filepath = MB.getBufferIdentifier();
std::transform(Filepath.begin(), Filepath.end(), Filepath.begin(),
[](char C) { return isalnum(C) ? C : '_'; });
std::string StartSym = "_binary_" + Filepath + "_start";
std::string EndSym = "_binary_" + Filepath + "_end";
std::string SizeSym = "_binary_" + Filepath + "_size";
auto SSym = ELF.addSymbol(StartSym);
SSym.Sym->setBindingAndType(STB_GLOBAL, STT_OBJECT);
SSym.Sym->st_shndx = DataSec.Index;
auto ESym = ELF.addSymbol(EndSym);
ESym.Sym->setBindingAndType(STB_GLOBAL, STT_OBJECT);
ESym.Sym->st_shndx = DataSec.Index;
ESym.Sym->st_value = MB.getBufferSize();
auto SZSym = ELF.addSymbol(SizeSym);
SZSym.Sym->setBindingAndType(STB_GLOBAL, STT_OBJECT);
SZSym.Sym->st_shndx = SHN_ABS;
SZSym.Sym->st_value = MB.getBufferSize();
std::size_t Size = ELF.layout();
ELFData.resize(Size);
ELF.write(ELFData.data());
// .data
std::copy(MB.getBufferStart(), MB.getBufferEnd(),
ELFData.data() + DataSec.Header->sh_offset);
return createELFFile<ObjectFile>(MemoryBufferRef(
StringRef((char *)ELFData.data(), Size), MB.getBufferIdentifier()));
}
static bool isBitcode(MemoryBufferRef MB) {
using namespace sys::fs;
return identify_magic(MB.getBuffer()) == file_magic::bitcode;
}
InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName) {
InputFile *F =
isBitcode(MB) ? new BitcodeFile(MB) : createELFFile<ObjectFile>(MB);
F->ArchiveName = ArchiveName;
return F;
}
InputFile *elf::createSharedFile(MemoryBufferRef MB) {
return createELFFile<SharedFile>(MB);
}
MemoryBufferRef LazyObjectFile::getBuffer() {
if (Seen)
return MemoryBufferRef();
Seen = true;
return MB;
}
template <class ELFT>
void LazyObjectFile::parse() {
for (StringRef Sym : getSymbols())
Symtab<ELFT>::X->addLazyObject(Sym, *this);
}
template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::SymRange Elf_Sym_Range;
const ELFFile<ELFT> Obj = createELFObj<ELFT>(this->MB);
for (const Elf_Shdr &Sec : Obj.sections()) {
if (Sec.sh_type != SHT_SYMTAB)
continue;
Elf_Sym_Range Syms = Obj.symbols(&Sec);
uint32_t FirstNonLocal = Sec.sh_info;
StringRef StringTable = check(Obj.getStringTableForSymtab(Sec));
std::vector<StringRef> V;
for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal))
if (Sym.st_shndx != SHN_UNDEF)
V.push_back(check(Sym.getName(StringTable)));
return V;
}
return {};
}
std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
- LLVMContext Context;
- std::unique_ptr<IRObjectFile> Obj =
- check(IRObjectFile::create(this->MB, Context));
std::vector<StringRef> V;
- for (const BasicSymbolRef &Sym : Obj->symbols()) {
- uint32_t Flags = Sym.getFlags();
- if (BitcodeFile::shouldSkip(Flags))
- continue;
- if (Flags & BasicSymbolRef::SF_Undefined)
+ std::unique_ptr<lto::InputFile> Obj = check(lto::InputFile::create(this->MB));
+ for (auto &ObjSym : Obj->symbols()) {
+ if (ObjSym.getFlags() & BasicSymbolRef::SF_Undefined)
continue;
- SmallString<64> Name;
- raw_svector_ostream OS(Name);
- Sym.printName(OS);
- V.push_back(Saver.save(StringRef(Name)));
+ V.push_back(Saver.save(ObjSym.getName()));
}
return V;
}
// Returns a vector of globally-visible defined symbol names.
std::vector<StringRef> LazyObjectFile::getSymbols() {
if (isBitcode(this->MB))
return getBitcodeSymbols();
unsigned char Size;
unsigned char Endian;
std::tie(Size, Endian) = getElfArchType(this->MB.getBuffer());
if (Size == ELFCLASS32) {
if (Endian == ELFDATA2LSB)
return getElfSymbols<ELF32LE>();
return getElfSymbols<ELF32BE>();
}
if (Endian == ELFDATA2LSB)
return getElfSymbols<ELF64LE>();
return getElfSymbols<ELF64BE>();
}
template void ArchiveFile::parse<ELF32LE>();
template void ArchiveFile::parse<ELF32BE>();
template void ArchiveFile::parse<ELF64LE>();
template void ArchiveFile::parse<ELF64BE>();
template void BitcodeFile::parse<ELF32LE>(DenseSet<StringRef> &);
template void BitcodeFile::parse<ELF32BE>(DenseSet<StringRef> &);
template void BitcodeFile::parse<ELF64LE>(DenseSet<StringRef> &);
template void BitcodeFile::parse<ELF64BE>(DenseSet<StringRef> &);
template void LazyObjectFile::parse<ELF32LE>();
template void LazyObjectFile::parse<ELF32BE>();
template void LazyObjectFile::parse<ELF64LE>();
template void LazyObjectFile::parse<ELF64BE>();
template class elf::ELFFileBase<ELF32LE>;
template class elf::ELFFileBase<ELF32BE>;
template class elf::ELFFileBase<ELF64LE>;
template class elf::ELFFileBase<ELF64BE>;
template class elf::ObjectFile<ELF32LE>;
template class elf::ObjectFile<ELF32BE>;
template class elf::ObjectFile<ELF64LE>;
template class elf::ObjectFile<ELF64BE>;
template class elf::SharedFile<ELF32LE>;
template class elf::SharedFile<ELF32BE>;
template class elf::SharedFile<ELF64LE>;
template class elf::SharedFile<ELF64BE>;
template InputFile *BinaryFile::createELF<ELF32LE>();
template InputFile *BinaryFile::createELF<ELF32BE>();
template InputFile *BinaryFile::createELF<ELF64LE>();
template InputFile *BinaryFile::createELF<ELF64BE>();
diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h
index 4211574..68b31e8 100644
--- a/lld/ELF/InputFiles.h
+++ b/lld/ELF/InputFiles.h
@@ -1,329 +1,330 @@
//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_INPUT_FILES_H
#define LLD_ELF_INPUT_FILES_H
#include "Config.h"
#include "InputSection.h"
#include "Error.h"
#include "Symbols.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/Reproduce.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/Comdat.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/StringSaver.h"
#include <map>
+namespace llvm {
+namespace lto {
+class InputFile;
+}
+}
+
namespace lld {
namespace elf {
using llvm::object::Archive;
class InputFile;
class Lazy;
class SymbolBody;
// The root class of input files.
class InputFile {
public:
virtual ~InputFile() = default;
enum Kind {
ObjectKind,
SharedKind,
LazyObjectKind,
ArchiveKind,
BitcodeKind,
BinaryKind,
};
Kind kind() const { return FileKind; }
StringRef getName() const { return MB.getBufferIdentifier(); }
MemoryBufferRef MB;
// Filename of .a which contained this file. If this file was
// not in an archive file, it is the empty string. We use this
// string for creating error messages.
StringRef ArchiveName;
// If this is an architecture-specific file, the following members
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
ELFKind EKind = ELFNoneKind;
uint16_t EMachine = llvm::ELF::EM_NONE;
static void freePool();
protected:
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {
Pool.push_back(this);
}
private:
const Kind FileKind;
// All InputFile instances are added to the pool
// and freed all at once on exit by freePool().
static std::vector<InputFile *> Pool;
};
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
std::string getFilename(const InputFile *F);
template <typename ELFT> class ELFFileBase : public InputFile {
public:
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Word Elf_Word;
typedef typename ELFT::SymRange Elf_Sym_Range;
ELFFileBase(Kind K, MemoryBufferRef M);
static bool classof(const InputFile *F) {
Kind K = F->kind();
return K == ObjectKind || K == SharedKind;
}
const llvm::object::ELFFile<ELFT> &getObj() const { return ELFObj; }
llvm::object::ELFFile<ELFT> &getObj() { return ELFObj; }
uint8_t getOSABI() const {
return getObj().getHeader()->e_ident[llvm::ELF::EI_OSABI];
}
StringRef getStringTable() const { return StringTable; }
uint32_t getSectionIndex(const Elf_Sym &Sym) const;
Elf_Sym_Range getElfSymbols(bool OnlyGlobals);
protected:
llvm::object::ELFFile<ELFT> ELFObj;
const Elf_Shdr *Symtab = nullptr;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
void initStringTable();
};
// .o file.
template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::SymRange Elf_Sym_Range;
typedef typename ELFT::Word Elf_Word;
typedef typename ELFT::uint uintX_t;
StringRef getShtGroupSignature(const Elf_Shdr &Sec);
ArrayRef<Elf_Word> getShtGroupEntries(const Elf_Shdr &Sec);
public:
static bool classof(const InputFile *F) {
return F->kind() == Base::ObjectKind;
}
ArrayRef<SymbolBody *> getSymbols();
ArrayRef<SymbolBody *> getLocalSymbols();
ArrayRef<SymbolBody *> getNonLocalSymbols();
explicit ObjectFile(MemoryBufferRef M);
void parse(llvm::DenseSet<StringRef> &ComdatGroups);
ArrayRef<InputSectionBase<ELFT> *> getSections() const { return Sections; }
InputSectionBase<ELFT> *getSection(const Elf_Sym &Sym) const;
SymbolBody &getSymbolBody(uint32_t SymbolIndex) const {
return *SymbolBodies[SymbolIndex];
}
template <typename RelT> SymbolBody &getRelocTargetSym(const RelT &Rel) const {
uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL);
return getSymbolBody(SymIndex);
}
const Elf_Shdr *getSymbolTable() const { return this->Symtab; };
// Get MIPS GP0 value defined by this file. This value represents the gp value
// used to create the relocatable object and required to support
// R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations.
uint32_t getMipsGp0() const;
// The number is the offset in the string table. It will be used as the
// st_name of the symbol.
std::vector<std::pair<const DefinedRegular<ELFT> *, unsigned>> KeptLocalSyms;
// SymbolBodies and Thunks for sections in this file are allocated
// using this buffer.
llvm::BumpPtrAllocator Alloc;
private:
void initializeSections(llvm::DenseSet<StringRef> &ComdatGroups);
void initializeSymbols();
InputSectionBase<ELFT> *getRelocTarget(const Elf_Shdr &Sec);
InputSectionBase<ELFT> *createInputSection(const Elf_Shdr &Sec);
bool shouldMerge(const Elf_Shdr &Sec);
SymbolBody *createSymbolBody(const Elf_Sym *Sym);
// List of all sections defined by this file.
std::vector<InputSectionBase<ELFT> *> Sections;
// List of all symbols referenced or defined by this file.
std::vector<SymbolBody *> SymbolBodies;
// MIPS .reginfo section defined by this file.
std::unique_ptr<MipsReginfoInputSection<ELFT>> MipsReginfo;
// MIPS .MIPS.options section defined by this file.
std::unique_ptr<MipsOptionsInputSection<ELFT>> MipsOptions;
// MIPS .MIPS.abiflags section defined by this file.
std::unique_ptr<MipsAbiFlagsInputSection<ELFT>> MipsAbiFlags;
llvm::SpecificBumpPtrAllocator<InputSection<ELFT>> IAlloc;
llvm::SpecificBumpPtrAllocator<MergeInputSection<ELFT>> MAlloc;
llvm::SpecificBumpPtrAllocator<EhInputSection<ELFT>> EHAlloc;
};
// LazyObjectFile is analogous to ArchiveFile in the sense that
// the file contains lazy symbols. The difference is that
// LazyObjectFile wraps a single file instead of multiple files.
//
// This class is used for --start-lib and --end-lib options which
// instruct the linker to link object files between them with the
// archive file semantics.
class LazyObjectFile : public InputFile {
public:
explicit LazyObjectFile(MemoryBufferRef M) : InputFile(LazyObjectKind, M) {}
static bool classof(const InputFile *F) {
return F->kind() == LazyObjectKind;
}
template <class ELFT> void parse();
MemoryBufferRef getBuffer();
private:
std::vector<StringRef> getSymbols();
template <class ELFT> std::vector<StringRef> getElfSymbols();
std::vector<StringRef> getBitcodeSymbols();
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver Saver{Alloc};
bool Seen = false;
};
// An ArchiveFile object represents a .a file.
class ArchiveFile : public InputFile {
public:
explicit ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == ArchiveKind; }
template <class ELFT> void parse();
// 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);
private:
std::unique_ptr<Archive> File;
llvm::DenseSet<uint64_t> Seen;
};
class BitcodeFile : public InputFile {
public:
explicit BitcodeFile(MemoryBufferRef M);
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
template <class ELFT>
void parse(llvm::DenseSet<StringRef> &ComdatGroups);
ArrayRef<Symbol *> getSymbols() { return Symbols; }
- static bool shouldSkip(uint32_t Flags);
- std::unique_ptr<llvm::object::IRObjectFile> Obj;
+ std::unique_ptr<llvm::lto::InputFile> Obj;
private:
std::vector<Symbol *> Symbols;
llvm::BumpPtrAllocator Alloc;
llvm::StringSaver Saver{Alloc};
- template <class ELFT>
- Symbol *createSymbol(const llvm::DenseSet<const llvm::Comdat *> &KeptComdats,
- const llvm::object::IRObjectFile &Obj,
- const llvm::object::BasicSymbolRef &Sym);
};
// .so file.
template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
typedef ELFFileBase<ELFT> Base;
typedef typename ELFT::Shdr Elf_Shdr;
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Word Elf_Word;
typedef typename ELFT::SymRange Elf_Sym_Range;
typedef typename ELFT::Versym Elf_Versym;
typedef typename ELFT::Verdef Elf_Verdef;
std::vector<StringRef> Undefs;
StringRef SoName;
const Elf_Shdr *VersymSec = nullptr;
const Elf_Shdr *VerdefSec = nullptr;
public:
StringRef getSoName() const { return SoName; }
const Elf_Shdr *getSection(const Elf_Sym &Sym) const;
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
static bool classof(const InputFile *F) {
return F->kind() == Base::SharedKind;
}
explicit SharedFile(MemoryBufferRef M);
void parseSoName();
void parseRest();
std::vector<const Elf_Verdef *> parseVerdefs(const Elf_Versym *&Versym);
struct NeededVer {
// The string table offset of the version name in the output file.
size_t StrTab;
// The version identifier for this version name.
uint16_t Index;
};
// Mapping from Elf_Verdef data structures to information about Elf_Vernaux
// data structures in the output file.
std::map<const Elf_Verdef *, NeededVer> VerdefMap;
// Used for --as-needed
bool AsNeeded = false;
bool IsUsed = false;
bool isNeeded() const { return !AsNeeded || IsUsed; }
};
class BinaryFile : public InputFile {
public:
explicit BinaryFile(MemoryBufferRef M) : InputFile(BinaryKind, M) {}
static bool classof(const InputFile *F) { return F->kind() == BinaryKind; }
template <class ELFT> InputFile *createELF();
private:
std::vector<uint8_t> ELFData;
};
InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "");
InputFile *createSharedFile(MemoryBufferRef MB);
} // namespace elf
} // namespace lld
#endif
diff --git a/lld/ELF/LTO.cpp b/lld/ELF/LTO.cpp
index 2334db3..f37b35c 100644
--- a/lld/ELF/LTO.cpp
+++ b/lld/ELF/LTO.cpp
@@ -1,333 +1,180 @@
//===- LTO.cpp ------------------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "LTO.h"
#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LoopPassManager.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/CodeGen/CommandFlags.h"
#include "llvm/CodeGen/ParallelCG.h"
#include "llvm/IR/AutoUpgrade.h"
+#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Verifier.h"
+#include "llvm/LTO/LTO.h"
#include "llvm/LTO/legacy/UpdateCompilerUsed.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
// This is for use when debugging LTO.
static void saveBuffer(StringRef Buffer, const Twine &Path) {
std::error_code EC;
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
if (EC)
error(EC, "cannot create " + Path);
OS << Buffer;
}
-// This is for use when debugging LTO.
-static void saveBCFile(Module &M, const Twine &Path) {
- std::error_code EC;
- raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
- if (EC)
- error(EC, "cannot create " + Path);
- WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true);
+static void diagnosticHandler(const DiagnosticInfo &DI) {
+ SmallString<128> ErrStorage;
+ raw_svector_ostream OS(ErrStorage);
+ DiagnosticPrinterRawOStream DP(OS);
+ DI.print(DP);
+ warning(ErrStorage);
}
-static void runNewCustomLtoPasses(Module &M, TargetMachine &TM) {
- PassBuilder PB(&TM);
-
- AAManager AA;
-
- // Parse a custom AA pipeline if asked to.
- if (!PB.parseAAPipeline(AA, Config->LtoAAPipeline)) {
- error("unable to parse AA pipeline description: " + Config->LtoAAPipeline);
- return;
- }
-
- LoopAnalysisManager LAM;
- FunctionAnalysisManager FAM;
- CGSCCAnalysisManager CGAM;
- ModuleAnalysisManager MAM;
-
- // Register the AA manager first so that our version is the one used.
- FAM.registerPass([&] { return std::move(AA); });
-
- // Register all the basic analyses with the managers.
- PB.registerModuleAnalyses(MAM);
- PB.registerCGSCCAnalyses(CGAM);
- PB.registerFunctionAnalyses(FAM);
- PB.registerLoopAnalyses(LAM);
- PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
-
- ModulePassManager MPM;
- if (!Config->DisableVerify)
- MPM.addPass(VerifierPass());
-
- // Now, add all the passes we've been requested to.
- if (!PB.parsePassPipeline(MPM, Config->LtoNewPmPasses)) {
- error("unable to parse pass pipeline description: " +
- Config->LtoNewPmPasses);
- return;
- }
+static std::unique_ptr<lto::LTO> createLTO() {
+ lto::Config Conf;
+ lto::ThinBackend Backend;
+ unsigned ParallelCodeGenParallelismLevel = Config->LtoJobs;
- if (!Config->DisableVerify)
- MPM.addPass(VerifierPass());
- MPM.run(M, MAM);
-}
+ // LLD supports the new relocations.
+ Conf.Options = InitTargetOptionsFromCodeGenFlags();
+ Conf.Options.RelaxELFRelocations = true;
-static void runOldLtoPasses(Module &M, TargetMachine &TM) {
- // Note that the gold plugin has a similar piece of code, so
- // it is probably better to move this code to a common place.
- legacy::PassManager LtoPasses;
- LtoPasses.add(createTargetTransformInfoWrapperPass(TM.getTargetIRAnalysis()));
- PassManagerBuilder PMB;
- PMB.LibraryInfo = new TargetLibraryInfoImpl(Triple(TM.getTargetTriple()));
- PMB.Inliner = createFunctionInliningPass();
- PMB.VerifyInput = PMB.VerifyOutput = !Config->DisableVerify;
- PMB.LoopVectorize = true;
- PMB.SLPVectorize = true;
- PMB.OptLevel = Config->LtoO;
- PMB.populateLTOPassManager(LtoPasses);
- LtoPasses.run(M);
-}
+ Conf.RelocModel = Config->Pic ? Reloc::PIC_ : Reloc::Static;
+ Conf.DisableVerify = Config->DisableVerify;
+ Conf.DiagHandler = diagnosticHandler;
+ Conf.OptLevel = Config->LtoO;
-static void runLTOPasses(Module &M, TargetMachine &TM) {
- if (!Config->LtoNewPmPasses.empty()) {
- // The user explicitly asked for a set of passes to be run.
- // This needs the new PM to work as there's no clean way to
- // pass a set of passes to run in the legacy PM.
- runNewCustomLtoPasses(M, TM);
- if (HasError)
- return;
- } else {
- // Run the 'default' set of LTO passes. This code still uses
- // the legacy PM as the new one is not the default.
- runOldLtoPasses(M, TM);
- }
+ // Set up a custom pipeline if we've been asked to.
+ if (!Config->LtoNewPmPasses.empty())
+ Conf.OptPipeline = Config->LtoNewPmPasses;
+ if (!Config->LtoAAPipeline.empty())
+ Conf.AAPipeline = Config->LtoAAPipeline;
if (Config->SaveTemps)
- saveBCFile(M, Config->OutputFile + ".lto.opt.bc");
-}
+ check(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
+ /*UseInputModulePath*/ true));
-static bool shouldInternalize(const SmallPtrSet<GlobalValue *, 8> &Used,
- Symbol *S, GlobalValue *GV) {
- if (S->IsUsedInRegularObj || Used.count(GV))
- return false;
- return !S->includeInDynsym();
+ return (HasError)
+ ? nullptr
+ : llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
+ ParallelCodeGenParallelismLevel);
}
-BitcodeCompiler::BitcodeCompiler()
- : Combined(new Module("ld-temp.o", Driver->Context)) {}
+BitcodeCompiler::BitcodeCompiler() : LtoObj(createLTO()) {}
+
+BitcodeCompiler::~BitcodeCompiler() {}
static void undefine(Symbol *S) {
replaceBody<Undefined>(S, S->body()->getName(), STV_DEFAULT, S->body()->Type,
nullptr);
}
-static void handleUndefinedAsmRefs(const BasicSymbolRef &Sym, GlobalValue *GV,
- StringSet<> &AsmUndefinedRefs) {
- // GV associated => not an assembly symbol, bail out.
- if (GV)
- return;
-
- // This is an undefined reference to a symbol in asm. We put that in
- // compiler.used, so that we can preserve it from being dropped from
- // the output, without necessarily preventing its internalization.
- SmallString<64> Name;
- raw_svector_ostream OS(Name);
- Sym.printName(OS);
- AsmUndefinedRefs.insert(Name.str());
-}
-
void BitcodeCompiler::add(BitcodeFile &F) {
- std::unique_ptr<IRObjectFile> Obj = std::move(F.Obj);
- std::vector<GlobalValue *> Keep;
- unsigned BodyIndex = 0;
- ArrayRef<Symbol *> Syms = F.getSymbols();
+ if (HasError)
+ return;
- Module &M = Obj->getModule();
- if (M.getDataLayoutStr().empty())
+ lto::InputFile &Obj = *F.Obj;
+ if (Obj.getDataLayoutStr().empty())
fatal("invalid bitcode file: " + F.getName() + " has no datalayout");
- // Discard non-compatible debug infos if necessary.
- M.materializeMetadata();
- UpgradeDebugInfo(M);
-
- // If a symbol appears in @llvm.used, the linker is required
- // to treat the symbol as there is a reference to the symbol
- // that it cannot see. Therefore, we can't internalize.
- SmallPtrSet<GlobalValue *, 8> Used;
- collectUsedGlobalVariables(M, Used, /* CompilerUsed */ false);
-
- for (const BasicSymbolRef &Sym : Obj->symbols()) {
- uint32_t Flags = Sym.getFlags();
- GlobalValue *GV = Obj->getSymbolGV(Sym.getRawDataRefImpl());
- if (GV && GV->hasAppendingLinkage())
- Keep.push_back(GV);
- if (BitcodeFile::shouldSkip(Flags))
- continue;
- Symbol *S = Syms[BodyIndex++];
- if (GV)
- GV->setUnnamedAddr(S->HasUnnamedAddr ? GlobalValue::UnnamedAddr::Global
- : GlobalValue::UnnamedAddr::None);
- if (Flags & BasicSymbolRef::SF_Undefined) {
- handleUndefinedAsmRefs(Sym, GV, AsmUndefinedRefs);
- continue;
- }
- SymbolBody *B = S->body();
- if (B->File != &F)
- continue;
-
- // We collect the set of symbols we want to internalize here
- // and change the linkage after the IRMover executed, i.e. after
- // we imported the symbols and satisfied undefined references
- // to it. We can't just change linkage here because otherwise
- // the IRMover will just rename the symbol.
- if (GV && shouldInternalize(Used, S, GV))
- InternalizedSyms.insert(GV->getName());
-
- // At this point we know that either the combined LTO object will provide a
- // definition of a symbol, or we will internalize it. In either case, we
- // need to undefine the symbol. In the former case, the real definition
- // needs to be able to replace the original definition without conflicting.
- // In the latter case, we need to allow the combined LTO object to provide a
- // definition with the same name, for example when doing parallel codegen.
- if (auto *C = dyn_cast<DefinedCommon>(B)) {
- if (auto *GO = dyn_cast<GlobalObject>(GV))
- GO->setAlignment(C->Alignment);
- } else {
- undefine(S);
- }
-
- if (!GV)
- // Module asm symbol.
- continue;
-
- switch (GV->getLinkage()) {
- default:
- break;
- case GlobalValue::LinkOnceAnyLinkage:
- GV->setLinkage(GlobalValue::WeakAnyLinkage);
- break;
- case GlobalValue::LinkOnceODRLinkage:
- GV->setLinkage(GlobalValue::WeakODRLinkage);
- break;
+ unsigned SymNum = 0;
+ std::vector<Symbol *> Syms = F.getSymbols();
+ std::vector<lto::SymbolResolution> Resols(Syms.size());
+
+ // Provide a resolution to the LTO API for each symbol.
+ for (auto &ObjSym : Obj.symbols()) {
+ Symbol *Sym = Syms[SymNum];
+ lto::SymbolResolution &R = Resols[SymNum];
+ ++SymNum;
+ SymbolBody *B = Sym->body();
+ R.Prevailing =
+ !(ObjSym.getFlags() & object::BasicSymbolRef::SF_Undefined) &&
+ B->File == &F;
+ R.VisibleToRegularObj =
+ Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
+ if (R.Prevailing)
+ undefine(Sym);
}
-
- Keep.push_back(GV);
- }
-
- IRMover Mover(*Combined);
- if (Error E = Mover.move(Obj->takeModule(), Keep,
- [](GlobalValue &, IRMover::ValueAdder) {})) {
- handleAllErrors(std::move(E), [&](const ErrorInfoBase &EIB) {
- fatal("failed to link module " + F.getName() + ": " + EIB.message());
- });
- }
+ check(LtoObj->add(std::move(F.Obj), Resols));
}
-static void internalize(GlobalValue &GV) {
- assert(!GV.hasLocalLinkage() &&
- "Trying to internalize a symbol with local linkage!");
- GV.setLinkage(GlobalValue::InternalLinkage);
-}
-
-std::vector<InputFile *> BitcodeCompiler::runSplitCodegen(
- const std::function<std::unique_ptr<TargetMachine>()> &TMFactory) {
- unsigned NumThreads = Config->LtoJobs;
- OwningData.resize(NumThreads);
-
- std::list<raw_svector_ostream> OSs;
- std::vector<raw_pwrite_stream *> OSPtrs;
- for (SmallString<0> &Obj : OwningData) {
- OSs.emplace_back(Obj);
- OSPtrs.push_back(&OSs.back());
- }
-
- splitCodeGen(std::move(Combined), OSPtrs, {}, TMFactory);
-
- std::vector<InputFile *> ObjFiles;
- for (SmallString<0> &Obj : OwningData)
- ObjFiles.push_back(createObjectFile(
- MemoryBufferRef(Obj, "LLD-INTERNAL-combined-lto-object")));
-
- // If -save-temps is given, we need to save temporary objects to files.
- // This is for debugging.
- if (Config->SaveTemps) {
- if (NumThreads == 1) {
- saveBuffer(OwningData[0], Config->OutputFile + ".lto.o");
- } else {
- for (unsigned I = 0; I < NumThreads; ++I)
- saveBuffer(OwningData[I], Config->OutputFile + Twine(I) + ".lto.o");
- }
- }
-
- return ObjFiles;
+/// Return the desired output filename given a base input name, a flag
+/// indicating whether a temp file should be generated, and an optional task id.
+/// The new filename generated is returned in \p NewFilename.
+static void getOutputFileName(SmallString<128> InFilename,
+ SmallString<128> &NewFilename, int TaskID = -1) {
+ NewFilename = InFilename;
+ if (TaskID >= 0)
+ NewFilename += utostr(TaskID);
}
// Merge all the bitcode files we have seen, codegen the result
-// and return the resulting ObjectFile.
+// and return the resulting ObjectFile(s).
std::vector<InputFile *> BitcodeCompiler::compile() {
- for (const auto &Name : InternalizedSyms) {
- GlobalValue *GV = Combined->getNamedValue(Name.first());
- assert(GV);
- internalize(*GV);
- }
-
- std::string TheTriple = Combined->getTargetTriple();
- std::string Msg;
- const Target *T = TargetRegistry::lookupTarget(TheTriple, Msg);
- if (!T)
- fatal("target not found: " + Msg);
-
- // LLD supports the new relocations.
- TargetOptions Options = InitTargetOptionsFromCodeGenFlags();
- Options.RelaxELFRelocations = true;
-
- auto CreateTargetMachine = [&]() {
- return std::unique_ptr<TargetMachine>(T->createTargetMachine(
- TheTriple, "", "", Options, Config->Pic ? Reloc::PIC_ : Reloc::Static));
+ std::vector<InputFile *> Ret;
+
+ SmallString<128> Filename;
+ Filename = Config->OutputFile;
+ unsigned MaxTasks = LtoObj->getMaxTasks();
+ std::vector<SmallString<128>> Filenames(MaxTasks);
+ Buff.resize(MaxTasks);
+
+ auto AddOutput =
+ [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> {
+ auto &OutputName = Filenames[Task];
+ getOutputFileName(Filename, OutputName, MaxTasks > 1 ? Task : -1);
+ return llvm::make_unique<lto::NativeObjectStream>(
+ llvm::make_unique<llvm::raw_svector_ostream>(Buff[Task]));
};
- std::unique_ptr<TargetMachine> TM = CreateTargetMachine();
-
- // Update llvm.compiler.used so that optimizations won't strip
- // off AsmUndefinedReferences.
- updateCompilerUsed(*Combined, *TM, AsmUndefinedRefs);
-
- if (Config->SaveTemps)
- saveBCFile(*Combined, Config->OutputFile + ".lto.bc");
-
- runLTOPasses(*Combined, *TM);
+ check(LtoObj->run(AddOutput));
if (HasError)
- return {};
-
- return runSplitCodegen(CreateTargetMachine);
+ return Ret;
+
+ for (unsigned I = 0; I != MaxTasks; ++I) {
+ if (!Filenames[I].empty()) {
+ // Do we want to move this to the LTO API?
+ if (Config->SaveTemps) {
+ if (MaxTasks == 1)
+ saveBuffer(Buff[I], Config->OutputFile + ".lto.o");
+ else
+ saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o");
+ }
+ MemoryBufferRef CompiledObjRef(Buff[I], "lto.tmp");
+ InputFile *Obj = createObjectFile(CompiledObjRef);
+ Ret.push_back(Obj);
+ }
+ }
+ return Ret;
}
diff --git a/lld/ELF/LTO.h b/lld/ELF/LTO.h
index ec81fc8..916c018 100644
--- a/lld/ELF/LTO.h
+++ b/lld/ELF/LTO.h
@@ -1,54 +1,57 @@
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file provides a way to combine bitcode files into one ELF
// file by compiling them using LLVM.
//
// If LTO is in use, your input files are not in regular ELF files
// but instead LLVM bitcode files. In that case, the linker has to
// convert bitcode files into the native format so that we can create
// an ELF file that contains native code. This file provides that
// functionality.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_LTO_H
#define LLD_ELF_LTO_H
#include "lld/Core/LLVM.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/IR/Module.h"
#include "llvm/Linker/IRMover.h"
+namespace llvm {
+namespace lto {
+class LTO;
+}
+}
+
namespace lld {
namespace elf {
class BitcodeFile;
class InputFile;
class BitcodeCompiler {
public:
BitcodeCompiler();
+ ~BitcodeCompiler();
void add(BitcodeFile &F);
std::vector<InputFile *> compile();
-private:
- std::vector<InputFile *> runSplitCodegen(
- const std::function<std::unique_ptr<llvm::TargetMachine>()> &TMFactory);
+ std::unique_ptr<llvm::lto::LTO> LtoObj;
- std::unique_ptr<llvm::Module> Combined;
- std::vector<SmallString<0>> OwningData;
- llvm::StringSet<> InternalizedSyms;
- llvm::StringSet<> AsmUndefinedRefs;
+private:
+ std::vector<SmallString<0>> Buff;
};
}
}
#endif
diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp
index e72f7ef..ac6a91a 100644
--- a/lld/ELF/SymbolTable.cpp
+++ b/lld/ELF/SymbolTable.cpp
@@ -1,728 +1,718 @@
//===- SymbolTable.cpp ----------------------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Symbol table is a bag of all known symbols. We put all symbols of
// all input files to the symbol table. The symbol table is basically
// a hash table with the logic to resolve symbol name conflicts using
// the symbol types.
//
//===----------------------------------------------------------------------===//
#include "SymbolTable.h"
#include "Config.h"
#include "Error.h"
#include "LinkerScript.h"
#include "SymbolListFile.h"
#include "Symbols.h"
#include "llvm/Bitcode/ReaderWriter.h"
#include "llvm/Support/StringSaver.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
// All input object files must be for the same architecture
// (e.g. it does not make sense to link x86 object files with
// MIPS object files.) This function checks for that error.
template <class ELFT> static bool isCompatible(InputFile *F) {
if (!isa<ELFFileBase<ELFT>>(F) && !isa<BitcodeFile>(F))
return true;
if (F->EKind == Config->EKind && F->EMachine == Config->EMachine)
return true;
StringRef A = F->getName();
StringRef B = Config->Emulation;
if (B.empty())
B = Config->FirstElf->getName();
error(A + " is incompatible with " + B);
return false;
}
// Add symbols in File to the symbol table.
template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
if (!isCompatible<ELFT>(File))
return;
// Binary file
if (auto *F = dyn_cast<BinaryFile>(File)) {
addFile(F->createELF<ELFT>());
return;
}
// .a file
if (auto *F = dyn_cast<ArchiveFile>(File)) {
F->parse<ELFT>();
return;
}
// Lazy object file
if (auto *F = dyn_cast<LazyObjectFile>(File)) {
F->parse<ELFT>();
return;
}
if (Config->Trace)
outs() << getFilename(File) << "\n";
// .so file
if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) {
// DSOs are uniquified not by filename but by soname.
F->parseSoName();
if (!SoNames.insert(F->getSoName()).second)
return;
SharedFiles.push_back(F);
F->parseRest();
return;
}
// LLVM bitcode file
if (auto *F = dyn_cast<BitcodeFile>(File)) {
BitcodeFiles.push_back(F);
F->parse<ELFT>(ComdatGroups);
return;
}
// Regular object file
auto *F = cast<ObjectFile<ELFT>>(File);
ObjectFiles.push_back(F);
F->parse(ComdatGroups);
}
// This function is where all the optimizations of link-time
// optimization happens. When LTO is in use, some input files are
// not in native object file format but in the LLVM bitcode format.
// This function compiles bitcode files into a few big native files
// using LLVM functions and replaces bitcode symbols with the results.
// Because all bitcode files that consist of a program are passed
// to the compiler at once, it can do whole-program optimization.
template <class ELFT> void SymbolTable<ELFT>::addCombinedLtoObject() {
if (BitcodeFiles.empty())
return;
// Compile bitcode files and replace bitcode symbols.
Lto.reset(new BitcodeCompiler);
for (BitcodeFile *F : BitcodeFiles)
Lto->add(*F);
for (InputFile *File : Lto->compile()) {
ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(File);
DenseSet<StringRef> DummyGroups;
Obj->parse(DummyGroups);
ObjectFiles.push_back(Obj);
}
}
template <class ELFT>
DefinedRegular<ELFT> *SymbolTable<ELFT>::addAbsolute(StringRef Name,
uint8_t Visibility) {
return cast<DefinedRegular<ELFT>>(
addRegular(Name, STB_GLOBAL, Visibility)->body());
}
// Add Name as an "ignored" symbol. An ignored symbol is a regular
// linker-synthesized defined symbol, but is only defined if needed.
template <class ELFT>
DefinedRegular<ELFT> *SymbolTable<ELFT>::addIgnored(StringRef Name,
uint8_t Visibility) {
if (!find(Name))
return nullptr;
return addAbsolute(Name, Visibility);
}
// Set a flag for --trace-symbol so that we can print out a log message
// if a new symbol with the same name is inserted into the symbol table.
template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) {
Symtab.insert({Name, {-1, true}});
}
// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM.
// Used to implement --wrap.
template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) {
SymbolBody *B = find(Name);
if (!B)
return;
StringSaver Saver(Alloc);
Symbol *Sym = B->symbol();
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
// We rename symbols by replacing the old symbol's SymbolBody with the new
// symbol's SymbolBody. This causes all SymbolBody pointers referring to the
// old symbol to instead refer to the new symbol.
memcpy(Real->Body.buffer, Sym->Body.buffer, sizeof(Sym->Body));
memcpy(Sym->Body.buffer, Wrap->Body.buffer, sizeof(Wrap->Body));
}
static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) {
if (VA == STV_DEFAULT)
return VB;
if (VB == STV_DEFAULT)
return VA;
return std::min(VA, VB);
}
// Parses a symbol in the form of <name>@<version> or <name>@@<version>.
static std::pair<StringRef, uint16_t> getSymbolVersion(StringRef S) {
if (Config->VersionDefinitions.empty())
return {S, Config->DefaultSymbolVersion};
size_t Pos = S.find('@');
if (Pos == 0 || Pos == StringRef::npos)
return {S, Config->DefaultSymbolVersion};
StringRef Name = S.substr(0, Pos);
StringRef Verstr = S.substr(Pos + 1);
if (Verstr.empty())
return {S, Config->DefaultSymbolVersion};
// '@@' in a symbol name means the default version.
// It is usually the most recent one.
bool IsDefault = (Verstr[0] == '@');
if (IsDefault)
Verstr = Verstr.substr(1);
for (VersionDefinition &V : Config->VersionDefinitions) {
if (V.Name == Verstr)
return {Name, IsDefault ? V.Id : (V.Id | VERSYM_HIDDEN)};
}
// It is an error if the specified version was not defined.
error("symbol " + S + " has undefined version " + Verstr);
return {S, Config->DefaultSymbolVersion};
}
// Find an existing symbol or create and insert a new one.
template <class ELFT>
std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef &Name) {
auto P = Symtab.insert({Name, SymIndex((int)SymVector.size(), false)});
SymIndex &V = P.first->second;
bool IsNew = P.second;
if (V.Idx == -1) {
IsNew = true;
V = SymIndex((int)SymVector.size(), true);
}
Symbol *Sym;
if (IsNew) {
Sym = new (Alloc) Symbol;
Sym->Binding = STB_WEAK;
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
- Sym->HasUnnamedAddr = true;
Sym->ExportDynamic = false;
Sym->Traced = V.Traced;
std::tie(Name, Sym->VersionId) = getSymbolVersion(Name);
SymVector.push_back(Sym);
} else {
Sym = SymVector[V.Idx];
}
return {Sym, IsNew};
}
// Find an existing symbol or create and insert a new one, then apply the given
// attributes.
template <class ELFT>
std::pair<Symbol *, bool>
SymbolTable<ELFT>::insert(StringRef &Name, uint8_t Type, uint8_t Visibility,
- bool CanOmitFromDynSym, bool HasUnnamedAddr,
- InputFile *File) {
+ bool CanOmitFromDynSym, InputFile *File) {
bool IsUsedInRegularObj = !File || File->kind() == InputFile::ObjectKind;
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
- // Merge in the new unnamed_addr attribute.
- S->HasUnnamedAddr &= HasUnnamedAddr;
// Merge in the new symbol's visibility.
S->Visibility = getMinVisibility(S->Visibility, Visibility);
if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
S->ExportDynamic = true;
if (IsUsedInRegularObj)
S->IsUsedInRegularObj = true;
if (!WasInserted && S->body()->Type != SymbolBody::UnknownType &&
((Type == STT_TLS) != S->body()->isTls()))
error("TLS attribute mismatch for symbol: " +
conflictMsg(S->body(), File));
return {S, WasInserted};
}
// Construct a string in the form of "Sym in File1 and File2".
// Used to construct an error message.
template <typename ELFT>
std::string SymbolTable<ELFT>::conflictMsg(SymbolBody *Existing,
InputFile *NewFile) {
std::string Sym = Existing->getName();
if (Config->Demangle)
Sym = demangle(Sym);
return Sym + " in " + getFilename(Existing->File) + " and " +
getFilename(NewFile);
}
template <class ELFT> Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name) {
return addUndefined(Name, STB_GLOBAL, STV_DEFAULT, /*Type*/ 0,
- /*CanOmitFromDynSym*/ false, /*HasUnnamedAddr*/ false,
- /*File*/ nullptr);
+ /*CanOmitFromDynSym*/ false, /*File*/ nullptr);
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, uint8_t Binding,
uint8_t StOther, uint8_t Type,
bool CanOmitFromDynSym,
- bool HasUnnamedAddr, InputFile *File) {
+ InputFile *File) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
- insert(Name, Type, StOther & 3, CanOmitFromDynSym, HasUnnamedAddr, File);
+ insert(Name, Type, StOther & 3, CanOmitFromDynSym, File);
if (WasInserted) {
S->Binding = Binding;
replaceBody<Undefined>(S, Name, StOther, Type, File);
return S;
}
if (Binding != STB_WEAK) {
if (S->body()->isShared() || S->body()->isLazy())
S->Binding = Binding;
if (auto *SS = dyn_cast<SharedSymbol<ELFT>>(S->body()))
SS->file()->IsUsed = true;
}
if (auto *L = dyn_cast<Lazy>(S->body())) {
// An undefined weak will not fetch archive members, but we have to remember
// its type. See also comment in addLazyArchive.
if (S->isWeak())
L->Type = Type;
else if (InputFile *F = L->fetch())
addFile(F);
}
return S;
}
// We have a new defined symbol with the specified binding. Return 1 if the new
// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are
// strong defined symbols.
static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding) {
if (WasInserted)
return 1;
SymbolBody *Body = S->body();
if (Body->isLazy() || Body->isUndefined() || Body->isShared())
return 1;
if (Binding == STB_WEAK)
return -1;
if (S->isWeak())
return 1;
return 0;
}
// We have a new non-common defined symbol with the specified binding. Return 1
// if the new symbol should win, -1 if the new symbol should lose, or 0 if there
// is a conflict. If the new symbol wins, also update the binding.
static int compareDefinedNonCommon(Symbol *S, bool WasInserted,
uint8_t Binding) {
if (int Cmp = compareDefined(S, WasInserted, Binding)) {
if (Cmp > 0)
S->Binding = Binding;
return Cmp;
}
if (isa<DefinedCommon>(S->body())) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
warning("common " + S->body()->getName() + " is overridden");
return 1;
}
return 0;
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
uint64_t Alignment, uint8_t Binding,
uint8_t StOther, uint8_t Type,
- bool HasUnnamedAddr, InputFile *File) {
+ InputFile *File) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(
- N, Type, StOther & 3, /*CanOmitFromDynSym*/ false, HasUnnamedAddr, File);
+ std::tie(S, WasInserted) =
+ insert(N, Type, StOther & 3, /*CanOmitFromDynSym*/ false, File);
int Cmp = compareDefined(S, WasInserted, Binding);
if (Cmp > 0) {
S->Binding = Binding;
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
} else if (Cmp == 0) {
auto *C = dyn_cast<DefinedCommon>(S->body());
if (!C) {
// Non-common symbols take precedence over common symbols.
if (Config->WarnCommon)
warning("common " + S->body()->getName() + " is overridden");
return S;
}
if (Config->WarnCommon)
warning("multiple common of " + S->body()->getName());
Alignment = C->Alignment = std::max(C->Alignment, Alignment);
if (Size > C->Size)
replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File);
}
return S;
}
template <class ELFT>
void SymbolTable<ELFT>::reportDuplicate(SymbolBody *Existing,
InputFile *NewFile) {
std::string Msg = "duplicate symbol: " + conflictMsg(Existing, NewFile);
if (Config->AllowMultipleDefinition)
warning(Msg);
else
error(Msg);
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, const Elf_Sym &Sym,
InputSectionBase<ELFT> *Section) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) =
- insert(Name, Sym.getType(), Sym.getVisibility(),
- /*CanOmitFromDynSym*/ false, /*HasUnnamedAddr*/ false,
- Section ? Section->getFile() : nullptr);
+ std::tie(S, WasInserted) = insert(Name, Sym.getType(), Sym.getVisibility(),
+ /*CanOmitFromDynSym*/ false,
+ Section ? Section->getFile() : nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, Sym.getBinding());
if (Cmp > 0)
replaceBody<DefinedRegular<ELFT>>(S, Name, Sym, Section);
else if (Cmp == 0)
reportDuplicate(S->body(), Section->getFile());
return S;
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t Binding,
uint8_t StOther) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) =
- insert(Name, STT_NOTYPE, StOther & 3, /*CanOmitFromDynSym*/ false,
- /*HasUnnamedAddr*/ false, nullptr);
+ std::tie(S, WasInserted) = insert(Name, STT_NOTYPE, StOther & 3,
+ /*CanOmitFromDynSym*/ false, nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding);
if (Cmp > 0)
replaceBody<DefinedRegular<ELFT>>(S, Name, StOther);
else if (Cmp == 0)
reportDuplicate(S->body(), nullptr);
return S;
}
template <typename ELFT>
Symbol *SymbolTable<ELFT>::addSynthetic(StringRef N,
OutputSectionBase<ELFT> *Section,
uintX_t Value, uint8_t StOther) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(N, STT_NOTYPE, /*Visibility*/ StOther & 0x3,
- /*CanOmitFromDynSym*/ false,
- /*HasUnnamedAddr*/ false, nullptr);
+ /*CanOmitFromDynSym*/ false, nullptr);
int Cmp = compareDefinedNonCommon(S, WasInserted, STB_GLOBAL);
if (Cmp > 0)
replaceBody<DefinedSynthetic<ELFT>>(S, N, Value, Section);
else if (Cmp == 0)
reportDuplicate(S->body(), nullptr);
return S;
}
template <typename ELFT>
void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *F, StringRef Name,
const Elf_Sym &Sym,
const typename ELFT::Verdef *Verdef) {
// DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
// as the visibility, which will leave the visibility in the symbol table
// unchanged.
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
- insert(Name, Sym.getType(), STV_DEFAULT, /*CanOmitFromDynSym*/ true,
- /*HasUnnamedAddr*/ false, F);
+ insert(Name, Sym.getType(), STV_DEFAULT, /*CanOmitFromDynSym*/ true, F);
// Make sure we preempt DSO symbols with default visibility.
if (Sym.getVisibility() == STV_DEFAULT)
S->ExportDynamic = true;
if (WasInserted || isa<Undefined>(S->body())) {
replaceBody<SharedSymbol<ELFT>>(S, F, Name, Sym, Verdef);
if (!S->isWeak())
F->IsUsed = true;
}
}
template <class ELFT>
Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, uint8_t Binding,
uint8_t StOther, uint8_t Type,
- bool CanOmitFromDynSym,
- bool HasUnnamedAddr, BitcodeFile *F) {
+ bool CanOmitFromDynSym, BitcodeFile *F) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) =
- insert(Name, Type, StOther & 3, CanOmitFromDynSym, HasUnnamedAddr, F);
+ insert(Name, Type, StOther & 3, CanOmitFromDynSym, F);
int Cmp = compareDefinedNonCommon(S, WasInserted, Binding);
if (Cmp > 0)
replaceBody<DefinedRegular<ELFT>>(S, Name, StOther, Type, F);
else if (Cmp == 0)
reportDuplicate(S->body(), F);
return S;
}
template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) {
auto It = Symtab.find(Name);
if (It == Symtab.end())
return nullptr;
SymIndex V = It->second;
if (V.Idx == -1)
return nullptr;
return SymVector[V.Idx]->body();
}
// Returns a list of defined symbols that match with a given regex.
template <class ELFT>
std::vector<SymbolBody *> SymbolTable<ELFT>::findAll(const Regex &Re) {
std::vector<SymbolBody *> Res;
for (Symbol *Sym : SymVector) {
SymbolBody *B = Sym->body();
StringRef Name = B->getName();
if (!B->isUndefined() && const_cast<Regex &>(Re).match(Name))
Res.push_back(B);
}
return Res;
}
template <class ELFT>
void SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F,
const object::Archive::Symbol Sym) {
Symbol *S;
bool WasInserted;
StringRef Name = Sym.getName();
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
replaceBody<LazyArchive>(S, *F, Sym, SymbolBody::UnknownType);
return;
}
if (!S->body()->isUndefined())
return;
// Weak undefined symbols should not fetch members from archives. If we were
// to keep old symbol we would not know that an archive member was available
// if a strong undefined symbol shows up afterwards in the link. If a strong
// undefined symbol never shows up, this lazy symbol will get to the end of
// the link and must be treated as the weak undefined one. We already marked
// this symbol as used when we added it to the symbol table, but we also need
// to preserve its type. FIXME: Move the Type field to Symbol.
if (S->isWeak()) {
replaceBody<LazyArchive>(S, *F, Sym, S->body()->Type);
return;
}
MemoryBufferRef MBRef = F->getMember(&Sym);
if (!MBRef.getBuffer().empty())
addFile(createObjectFile(MBRef, F->getName()));
}
template <class ELFT>
void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(Name);
if (WasInserted) {
replaceBody<LazyObject>(S, Name, Obj, SymbolBody::UnknownType);
return;
}
if (!S->body()->isUndefined())
return;
// See comment for addLazyArchive above.
if (S->isWeak()) {
replaceBody<LazyObject>(S, Name, Obj, S->body()->Type);
} else {
MemoryBufferRef MBRef = Obj.getBuffer();
if (!MBRef.getBuffer().empty())
addFile(createObjectFile(MBRef));
}
}
// Process undefined (-u) flags by loading lazy symbols named by those flags.
template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() {
for (StringRef S : Config->Undefined)
if (auto *L = dyn_cast_or_null<Lazy>(find(S)))
if (InputFile *File = L->fetch())
addFile(File);
}
// This function takes care of the case in which shared libraries depend on
// the user program (not the other way, which is usual). Shared libraries
// may have undefined symbols, expecting that the user program provides
// the definitions for them. An example is BSD's __progname symbol.
// We need to put such symbols to the main program's .dynsym so that
// shared libraries can find them.
// Except this, we ignore undefined symbols in DSOs.
template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() {
for (SharedFile<ELFT> *File : SharedFiles)
for (StringRef U : File->getUndefinedSymbols())
if (SymbolBody *Sym = find(U))
if (Sym->isDefined())
Sym->symbol()->ExportDynamic = true;
}
// This function processes --export-dynamic-symbol and --dynamic-list.
template <class ELFT> void SymbolTable<ELFT>::scanDynamicList() {
for (StringRef S : Config->DynamicList)
if (SymbolBody *B = find(S))
B->symbol()->ExportDynamic = true;
}
static void setVersionId(SymbolBody *Body, StringRef VersionName,
StringRef Name, uint16_t Version) {
if (!Body || Body->isUndefined()) {
if (Config->NoUndefinedVersion)
error("version script assignment of " + VersionName + " to symbol " +
Name + " failed: symbol not defined");
return;
}
Symbol *Sym = Body->symbol();
if (Sym->VersionId != Config->DefaultSymbolVersion)
warning("duplicate symbol " + Name + " in version script");
Sym->VersionId = Version;
}
// Returns a map from demangled symbols to symbol objects.
// The relationship is 1:N instead of 1:1 because with the symbol
// versioning, more than one symbol may have the same name.
template <class ELFT>
std::map<std::string, std::vector<SymbolBody *>>
SymbolTable<ELFT>::getDemangledSyms() {
std::map<std::string, std::vector<SymbolBody *>> Result;
for (Symbol *Sym : SymVector) {
SymbolBody *B = Sym->body();
Result[demangle(B->getName())].push_back(B);
}
return Result;
}
static bool hasExternCpp() {
for (VersionDefinition &V : Config->VersionDefinitions)
for (SymbolVersion Sym : V.Globals)
if (Sym.IsExternCpp)
return true;
return false;
}
static ArrayRef<SymbolBody *>
findDemangled(std::map<std::string, std::vector<SymbolBody *>> &D,
StringRef Name) {
auto I = D.find(Name);
if (I != D.end())
return I->second;
return {};
}
static std::vector<SymbolBody *>
findAllDemangled(const std::map<std::string, std::vector<SymbolBody *>> &D,
const Regex &Re) {
std::vector<SymbolBody *> Res;
for (auto &P : D) {
if (const_cast<Regex &>(Re).match(P.first))
for (SymbolBody *Body : P.second)
if (!Body->isUndefined())
Res.push_back(Body);
}
return Res;
}
// If there's only one anonymous version definition in a version
// script file, the script does not actullay define any symbol version,
// but just specifies symbols visibilities. We assume that the script was
// in the form of { global: foo; bar; local *; }. So, local is default.
// In this function, we make specified symbols global.
template <class ELFT> void SymbolTable<ELFT>::handleAnonymousVersion() {
std::vector<StringRef> Patterns;
for (SymbolVersion &Sym : Config->VersionScriptGlobals) {
if (hasWildcard(Sym.Name)) {
Patterns.push_back(Sym.Name);
continue;
}
if (SymbolBody *B = find(Sym.Name))
B->symbol()->VersionId = VER_NDX_GLOBAL;
}
if (Patterns.empty())
return;
Regex Re = compileGlobPatterns(Patterns);
std::vector<SymbolBody *> Syms = findAll(Re);
for (SymbolBody *B : Syms)
B->symbol()->VersionId = VER_NDX_GLOBAL;
}
// This function processes version scripts by updating VersionId
// member of symbols.
template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() {
// Handle edge cases first.
if (!Config->VersionScriptGlobals.empty()) {
handleAnonymousVersion();
return;
}
if (Config->VersionDefinitions.empty())
return;
// Now we have version definitions, so we need to set version ids to symbols.
// Each version definition has a glob pattern, and all symbols that match
// with the pattern get that version.
// Users can use "extern C++ {}" directive to match against demangled
// C++ symbols. For example, you can write a pattern such as
// "llvm::*::foo(int, ?)". Obviously, there's no way to handle this
// other than trying to match a regexp against all demangled symbols.
// So, if "extern C++" feature is used, we demangle all known symbols.
std::map<std::string, std::vector<SymbolBody *>> Demangled;
if (hasExternCpp())
Demangled = getDemangledSyms();
// First, we assign versions to exact matching symbols,
// i.e. version definitions not containing any glob meta-characters.
for (VersionDefinition &V : Config->VersionDefinitions) {
for (SymbolVersion Sym : V.Globals) {
if (Sym.HasWildcards)
continue;
StringRef N = Sym.Name;
if (Sym.IsExternCpp) {
for (SymbolBody *B : findDemangled(Demangled, N))
setVersionId(B, V.Name, N, V.Id);
continue;
}
setVersionId(find(N), V.Name, N, V.Id);
}
}
// Next, we assign versions to fuzzy matching symbols,
// i.e. version definitions containing glob meta-characters.
// Note that because the last match takes precedence over previous matches,
// we iterate over the definitions in the reverse order.
for (size_t I = Config->VersionDefinitions.size() - 1; I != (size_t)-1; --I) {
VersionDefinition &V = Config->VersionDefinitions[I];
for (SymbolVersion &Sym : V.Globals) {
if (!Sym.HasWildcards)
continue;
Regex Re = compileGlobPatterns({Sym.Name});
std::vector<SymbolBody *> Syms =
Sym.IsExternCpp ? findAllDemangled(Demangled, Re) : findAll(Re);
// Exact matching takes precendence over fuzzy matching,
// so we set a version to a symbol only if no version has been assigned
// to the symbol. This behavior is compatible with GNU.
for (SymbolBody *B : Syms)
if (B->symbol()->VersionId == Config->DefaultSymbolVersion)
B->symbol()->VersionId = V.Id;
}
}
}
template class elf::SymbolTable<ELF32LE>;
template class elf::SymbolTable<ELF32BE>;
template class elf::SymbolTable<ELF64LE>;
template class elf::SymbolTable<ELF64BE>;
diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h
index 8059e61..9048062 100644
--- a/lld/ELF/SymbolTable.h
+++ b/lld/ELF/SymbolTable.h
@@ -1,145 +1,143 @@
//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_SYMBOL_TABLE_H
#define LLD_ELF_SYMBOL_TABLE_H
#include "InputFiles.h"
#include "LTO.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Regex.h"
namespace lld {
namespace elf {
class Lazy;
template <class ELFT> class OutputSectionBase;
struct Symbol;
typedef llvm::CachedHash<StringRef> SymName;
// SymbolTable is a bucket of all known symbols, including defined,
// undefined, or lazy symbols (the last one is symbols in archive
// files whose archive members are not yet loaded).
//
// We put all symbols of all files to a SymbolTable, and the
// SymbolTable selects the "best" symbols if there are name
// conflicts. For example, obviously, a defined symbol is better than
// an undefined symbol. Or, if there's a conflict between a lazy and a
// undefined, it'll read an archive member to read a real definition
// to replace the lazy symbol. The logic is implemented in the
// add*() functions, which are called by input files as they are parsed. There
// is one add* function per symbol type.
template <class ELFT> class SymbolTable {
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::uint uintX_t;
public:
void addFile(InputFile *File);
void addCombinedLtoObject();
llvm::ArrayRef<Symbol *> getSymbols() const { return SymVector; }
const std::vector<ObjectFile<ELFT> *> &getObjectFiles() const {
return ObjectFiles;
}
const std::vector<SharedFile<ELFT> *> &getSharedFiles() const {
return SharedFiles;
}
DefinedRegular<ELFT> *addAbsolute(StringRef Name,
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
DefinedRegular<ELFT> *addIgnored(StringRef Name,
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
Symbol *addUndefined(StringRef Name);
Symbol *addUndefined(StringRef Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, bool CanOmitFromDynSym,
- bool HasUnnamedAddr, InputFile *File);
+ uint8_t Type, bool CanOmitFromDynSym, InputFile *File);
Symbol *addRegular(StringRef Name, const Elf_Sym &Sym,
InputSectionBase<ELFT> *Section);
Symbol *addRegular(StringRef Name, uint8_t Binding, uint8_t StOther);
Symbol *addSynthetic(StringRef N, OutputSectionBase<ELFT> *Section,
uintX_t Value, uint8_t StOther);
void addShared(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
const typename ELFT::Verdef *Verdef);
void addLazyArchive(ArchiveFile *F, const llvm::object::Archive::Symbol S);
void addLazyObject(StringRef Name, LazyObjectFile &Obj);
Symbol *addBitcode(StringRef Name, uint8_t Binding, uint8_t StOther,
- uint8_t Type, bool CanOmitFromDynSym, bool HasUnnamedAddr,
- BitcodeFile *File);
+ uint8_t Type, bool CanOmitFromDynSym, BitcodeFile *File);
Symbol *addCommon(StringRef N, uint64_t Size, uint64_t Alignment,
uint8_t Binding, uint8_t StOther, uint8_t Type,
- bool HasUnnamedAddr, InputFile *File);
+ InputFile *File);
void scanUndefinedFlags();
void scanShlibUndefined();
void scanDynamicList();
void scanVersionScript();
SymbolBody *find(StringRef Name);
void trace(StringRef Name);
void wrap(StringRef Name);
private:
std::vector<SymbolBody *> findAll(const llvm::Regex &Re);
std::pair<Symbol *, bool> insert(StringRef &Name);
std::pair<Symbol *, bool> insert(StringRef &Name, uint8_t Type,
uint8_t Visibility, bool CanOmitFromDynSym,
- bool HasUnnamedAddr, InputFile *File);
+ InputFile *File);
std::string conflictMsg(SymbolBody *Existing, InputFile *NewFile);
void reportDuplicate(SymbolBody *Existing, InputFile *NewFile);
std::map<std::string, std::vector<SymbolBody *>> getDemangledSyms();
void handleAnonymousVersion();
struct SymIndex {
SymIndex(int Idx, bool Traced) : Idx(Idx), Traced(Traced) {}
int Idx : 31;
unsigned Traced : 1;
};
// The order the global symbols are in is not defined. We can use an arbitrary
// order, but it has to be reproducible. That is true even when cross linking.
// The default hashing of StringRef produces different results on 32 and 64
// bit systems so we use a map to a vector. That is arbitrary, deterministic
// but a bit inefficient.
// FIXME: Experiment with passing in a custom hashing or sorting the symbols
// once symbol resolution is finished.
llvm::DenseMap<SymName, SymIndex> Symtab;
std::vector<Symbol *> SymVector;
llvm::BumpPtrAllocator Alloc;
// Comdat groups define "link once" sections. If two comdat groups have the
// same name, only one of them is linked, and the other is ignored. This set
// is used to uniquify them.
llvm::DenseSet<StringRef> ComdatGroups;
std::vector<ObjectFile<ELFT> *> ObjectFiles;
std::vector<SharedFile<ELFT> *> SharedFiles;
std::vector<BitcodeFile *> BitcodeFiles;
// Set of .so files to not link the same shared object file more than once.
llvm::DenseSet<StringRef> SoNames;
std::unique_ptr<BitcodeCompiler> Lto;
};
template <class ELFT> struct Symtab { static SymbolTable<ELFT> *X; };
template <class ELFT> SymbolTable<ELFT> *Symtab<ELFT>::X;
} // namespace elf
} // namespace lld
#endif
diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index 52c658c..c067e9c 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -1,477 +1,474 @@
//===- Symbols.h ------------------------------------------------*- C++ -*-===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// All symbols are handled as SymbolBodies regardless of their types.
// This file defines various types of SymbolBodies.
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_SYMBOLS_H
#define LLD_ELF_SYMBOLS_H
#include "InputSection.h"
#include "lld/Core/LLVM.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
#include "llvm/Support/AlignOf.h"
namespace lld {
namespace elf {
class ArchiveFile;
class BitcodeFile;
class InputFile;
class LazyObjectFile;
class SymbolBody;
template <class ELFT> class ObjectFile;
template <class ELFT> class OutputSection;
template <class ELFT> class OutputSectionBase;
template <class ELFT> class SharedFile;
struct Symbol;
// The base class for real symbol classes.
class SymbolBody {
public:
enum Kind {
DefinedFirst,
DefinedRegularKind = DefinedFirst,
SharedKind,
DefinedCommonKind,
DefinedSyntheticKind,
DefinedLast = DefinedSyntheticKind,
UndefinedKind,
LazyArchiveKind,
LazyObjectKind,
};
SymbolBody(Kind K) : SymbolKind(K) {}
Symbol *symbol();
const Symbol *symbol() const {
return const_cast<SymbolBody *>(this)->symbol();
}
Kind kind() const { return static_cast<Kind>(SymbolKind); }
bool isUndefined() const { return SymbolKind == UndefinedKind; }
bool isDefined() const { return SymbolKind <= DefinedLast; }
bool isCommon() const { return SymbolKind == DefinedCommonKind; }
bool isLazy() const {
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
}
bool isShared() const { return SymbolKind == SharedKind; }
bool isLocal() const { return IsLocal; }
bool isPreemptible() const;
StringRef getName() const;
uint32_t getNameOffset() const {
assert(isLocal());
return NameOffset;
}
uint8_t getVisibility() const { return StOther & 0x3; }
unsigned DynsymIndex = 0;
uint32_t GotIndex = -1;
uint32_t GotPltIndex = -1;
uint32_t PltIndex = -1;
uint32_t GlobalDynIndex = -1;
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
template <class ELFT> bool hasThunk() const;
template <class ELFT>
typename ELFT::uint getVA(typename ELFT::uint Addend = 0) const;
template <class ELFT> typename ELFT::uint getGotOffset() const;
template <class ELFT> typename ELFT::uint getGotVA() const;
template <class ELFT> typename ELFT::uint getGotPltOffset() const;
template <class ELFT> typename ELFT::uint getGotPltVA() const;
template <class ELFT> typename ELFT::uint getPltVA() const;
template <class ELFT> typename ELFT::uint getThunkVA() const;
template <class ELFT> typename ELFT::uint getSize() const;
// The file from which this symbol was created.
InputFile *File = nullptr;
protected:
SymbolBody(Kind K, StringRef Name, uint8_t StOther, uint8_t Type);
SymbolBody(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type);
const unsigned SymbolKind : 8;
public:
// True if the linker has to generate a copy relocation for this shared
// symbol or if the symbol should point to its plt entry.
unsigned NeedsCopyOrPltAddr : 1;
// True if this is a local symbol.
unsigned IsLocal : 1;
// True if this symbol has an entry in the global part of MIPS GOT.
unsigned IsInGlobalMipsGot : 1;
// The following fields have the same meaning as the ELF symbol attributes.
uint8_t Type; // symbol type
uint8_t StOther; // st_other field value
// The Type field may also have this value. It means that we have not yet seen
// a non-Lazy symbol with this name, so we don't know what its type is. The
// Type field is normally set to this value for Lazy symbols unless we saw a
// weak undefined symbol first, in which case we need to remember the original
// symbol's type in order to check for TLS mismatches.
enum { UnknownType = 255 };
bool isSection() const { return Type == llvm::ELF::STT_SECTION; }
bool isTls() const { return Type == llvm::ELF::STT_TLS; }
bool isFunc() const { return Type == llvm::ELF::STT_FUNC; }
bool isGnuIFunc() const { return Type == llvm::ELF::STT_GNU_IFUNC; }
bool isObject() const { return Type == llvm::ELF::STT_OBJECT; }
bool isFile() const { return Type == llvm::ELF::STT_FILE; }
protected:
struct Str {
const char *S;
size_t Len;
};
union {
Str Name;
uint32_t NameOffset;
};
};
// The base class for any defined symbols.
class Defined : public SymbolBody {
public:
Defined(Kind K, StringRef Name, uint8_t StOther, uint8_t Type);
Defined(Kind K, uint32_t NameOffset, uint8_t StOther, uint8_t Type);
static bool classof(const SymbolBody *S) { return S->isDefined(); }
};
class DefinedCommon : public Defined {
public:
DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, uint8_t StOther,
uint8_t Type, InputFile *File);
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedCommonKind;
}
// The output offset of this common symbol in the output bss. Computed by the
// writer.
uint64_t Offset;
// The maximum alignment we have seen for this symbol.
uint64_t Alignment;
uint64_t Size;
};
// Regular defined symbols read from object file symbol tables.
template <class ELFT> class DefinedRegular : public Defined {
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::uint uintX_t;
public:
DefinedRegular(StringRef Name, const Elf_Sym &Sym,
InputSectionBase<ELFT> *Section)
: Defined(SymbolBody::DefinedRegularKind, Name, Sym.st_other,
Sym.getType()),
Value(Sym.st_value), Size(Sym.st_size),
Section(Section ? Section->Repl : NullInputSection) {
if (Section)
this->File = Section->getFile();
}
DefinedRegular(const Elf_Sym &Sym, InputSectionBase<ELFT> *Section)
: Defined(SymbolBody::DefinedRegularKind, Sym.st_name, Sym.st_other,
Sym.getType()),
Value(Sym.st_value), Size(Sym.st_size),
Section(Section ? Section->Repl : NullInputSection) {
assert(isLocal());
if (Section)
this->File = Section->getFile();
}
DefinedRegular(StringRef Name, uint8_t StOther)
: Defined(SymbolBody::DefinedRegularKind, Name, StOther,
llvm::ELF::STT_NOTYPE),
Value(0), Size(0), Section(NullInputSection) {}
DefinedRegular(StringRef Name, uint8_t StOther, uint8_t Type, BitcodeFile *F)
: Defined(SymbolBody::DefinedRegularKind, Name, StOther, Type), Value(0),
Size(0), Section(NullInputSection) {
this->File = F;
}
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedRegularKind;
}
uintX_t Value;
uintX_t Size;
// The input section this symbol belongs to. Notice that this is
// a reference to a pointer. We are using two levels of indirections
// because of ICF. If ICF decides two sections need to be merged, it
// manipulates this Section pointers so that they point to the same
// section. This is a bit tricky, so be careful to not be confused.
// If this is null, the symbol is an absolute symbol.
InputSectionBase<ELFT> *&Section;
// If non-null the symbol has a Thunk that may be used as an alternative
// destination for callers of this Symbol.
Thunk<ELFT> *ThunkData = nullptr;
private:
static InputSectionBase<ELFT> *NullInputSection;
};
template <class ELFT>
InputSectionBase<ELFT> *DefinedRegular<ELFT>::NullInputSection;
// DefinedSynthetic is a class to represent linker-generated ELF symbols.
// The difference from the regular symbol is that DefinedSynthetic symbols
// don't belong to any input files or sections. Thus, its constructor
// takes an output section to calculate output VA, etc.
// If Section is null, this symbol is relative to the image base.
template <class ELFT> class DefinedSynthetic : public Defined {
public:
typedef typename ELFT::uint uintX_t;
DefinedSynthetic(StringRef N, uintX_t Value,
OutputSectionBase<ELFT> *Section);
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::DefinedSyntheticKind;
}
// Special value designates that the symbol 'points'
// to the end of the section.
static const uintX_t SectionEnd = uintX_t(-1);
uintX_t Value;
const OutputSectionBase<ELFT> *Section;
};
class Undefined : public SymbolBody {
public:
Undefined(StringRef Name, uint8_t StOther, uint8_t Type, InputFile *F);
Undefined(uint32_t NameOffset, uint8_t StOther, uint8_t Type, InputFile *F);
static bool classof(const SymbolBody *S) {
return S->kind() == UndefinedKind;
}
InputFile *file() { return this->File; }
};
template <class ELFT> class SharedSymbol : public Defined {
typedef typename ELFT::Sym Elf_Sym;
typedef typename ELFT::Verdef Elf_Verdef;
typedef typename ELFT::uint uintX_t;
public:
static bool classof(const SymbolBody *S) {
return S->kind() == SymbolBody::SharedKind;
}
SharedSymbol(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
const Elf_Verdef *Verdef)
: Defined(SymbolBody::SharedKind, Name, Sym.st_other, Sym.getType()),
Sym(Sym), Verdef(Verdef) {
// IFuncs defined in DSOs are treated as functions by the static linker.
if (isGnuIFunc())
Type = llvm::ELF::STT_FUNC;
this->File = F;
}
SharedFile<ELFT> *file() { return (SharedFile<ELFT> *)this->File; }
const Elf_Sym &Sym;
// This field is a pointer to the symbol's version definition.
const Elf_Verdef *Verdef;
// OffsetInBss is significant only when needsCopy() is true.
uintX_t OffsetInBss = 0;
// If non-null the symbol has a Thunk that may be used as an alternative
// destination for callers of this Symbol.
Thunk<ELFT> *ThunkData = nullptr;
bool needsCopy() const { return this->NeedsCopyOrPltAddr && !this->isFunc(); }
};
// 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:
static bool classof(const SymbolBody *S) { return S->isLazy(); }
// Returns an object file for this symbol, or a nullptr if the file
// was already returned.
InputFile *fetch();
protected:
Lazy(SymbolBody::Kind K, StringRef Name, uint8_t Type)
: SymbolBody(K, Name, llvm::ELF::STV_DEFAULT, Type) {}
};
// LazyArchive symbols represents symbols in archive files.
class LazyArchive : public Lazy {
public:
LazyArchive(ArchiveFile &File, const llvm::object::Archive::Symbol S,
uint8_t Type);
static bool classof(const SymbolBody *S) {
return S->kind() == LazyArchiveKind;
}
ArchiveFile *file() { return (ArchiveFile *)this->File; }
InputFile *fetch();
private:
const llvm::object::Archive::Symbol Sym;
};
// LazyObject symbols represents symbols in object files between
// --start-lib and --end-lib options.
class LazyObject : public Lazy {
public:
LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type);
static bool classof(const SymbolBody *S) {
return S->kind() == LazyObjectKind;
}
LazyObjectFile *file() { return (LazyObjectFile *)this->File; }
InputFile *fetch();
};
// Some linker-generated symbols need to be created as
// DefinedRegular symbols.
template <class ELFT> struct ElfSym {
// The content for __ehdr_start symbol.
static DefinedRegular<ELFT> *EhdrStart;
// The content for _etext and etext symbols.
static DefinedRegular<ELFT> *Etext;
static DefinedRegular<ELFT> *Etext2;
// The content for _edata and edata symbols.
static DefinedRegular<ELFT> *Edata;
static DefinedRegular<ELFT> *Edata2;
// The content for _end and end symbols.
static DefinedRegular<ELFT> *End;
static DefinedRegular<ELFT> *End2;
// The content for _gp_disp symbol for MIPS target.
static SymbolBody *MipsGpDisp;
};
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::EhdrStart;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Etext;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Etext2;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata2;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End;
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End2;
template <class ELFT> SymbolBody *ElfSym<ELFT>::MipsGpDisp;
// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
// always one Symbol for each symbol name. The resolver updates the SymbolBody
// stored in the Body field of this object as it resolves symbols. Symbol also
// holds computed properties of symbol names.
struct Symbol {
// Symbol binding. This is on the Symbol to track changes during resolution.
// In particular:
// An undefined weak is still weak when it resolves to a shared library.
// An undefined weak will not fetch archive members, but we have to remember
// it is weak.
uint8_t Binding;
// Version definition index.
uint16_t VersionId;
// Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols.
unsigned Visibility : 2;
- // True if the symbol has unnamed_addr.
- unsigned HasUnnamedAddr : 1;
-
// True if the symbol was used for linking and thus need to be added to the
// output file's symbol table. This is true for all symbols except for
// unreferenced DSO symbols and bitcode symbols that are unreferenced except
// by other bitcode objects.
unsigned IsUsedInRegularObj : 1;
// If this flag is true and the symbol has protected or default visibility, it
// will appear in .dynsym. This flag is set by interposable DSO symbols in
// executables, by most symbols in DSOs and executables built with
// --export-dynamic, and by dynamic lists.
unsigned ExportDynamic : 1;
// True if this symbol is specified by --trace-symbol option.
unsigned Traced : 1;
bool includeInDynsym() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
// This field is used to store the Symbol's SymbolBody. This instantiation of
// AlignedCharArrayUnion gives us a struct with a char array field that is
// large and aligned enough to store any derived class of SymbolBody. We
// assume that the size and alignment of ELF64LE symbols is sufficient for any
// ELFT, and we verify this with the static_asserts in replaceBody.
llvm::AlignedCharArrayUnion<
DefinedCommon, DefinedRegular<llvm::object::ELF64LE>,
DefinedSynthetic<llvm::object::ELF64LE>, Undefined,
SharedSymbol<llvm::object::ELF64LE>, LazyArchive, LazyObject>
Body;
SymbolBody *body() { return reinterpret_cast<SymbolBody *>(Body.buffer); }
const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
};
void printTraceSymbol(Symbol *Sym);
template <typename T, typename... ArgT>
void replaceBody(Symbol *S, ArgT &&... Arg) {
static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
static_assert(llvm::AlignOf<T>::Alignment <=
llvm::AlignOf<decltype(S->Body)>::Alignment,
"Body not aligned enough");
assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
"Not a SymbolBody");
new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
// Print out a log message if --trace-symbol was specified.
// This is for debugging.
if (S->Traced)
printTraceSymbol(S);
}
inline Symbol *SymbolBody::symbol() {
assert(!isLocal());
return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
offsetof(Symbol, Body));
}
} // namespace elf
} // namespace lld
#endif
diff --git a/lld/test/ELF/lto/archive-3.ll b/lld/test/ELF/lto/archive-3.ll
index 350c892..0322e41 100644
--- a/lld/test/ELF/lto/archive-3.ll
+++ b/lld/test/ELF/lto/archive-3.ll
@@ -1,19 +1,19 @@
; REQUIRES: x86
; RUN: llvm-as %S/Inputs/archive-3.ll -o %t1.o
; RUN: llvm-as %s -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t3 -save-temps
-; RUN: llvm-dis %t3.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t3.0.2.internalize.bc -o - | FileCheck %s
; RUN: rm -f %t.a
; RUN: llvm-ar rcs %t.a %t1.o
; RUN: ld.lld -m elf_x86_64 %t.a %t1.o %t2.o -o %t3 -save-temps
-; RUN: llvm-dis %t3.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t3.0.2.internalize.bc -o - | FileCheck %s
; CHECK: define internal void @foo() {
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define void @_start() {
ret void
}
diff --git a/lld/test/ELF/lto/asmundef.ll b/lld/test/ELF/lto/asmundef.ll
index d76e418..1c87cd0 100644
--- a/lld/test/ELF/lto/asmundef.ll
+++ b/lld/test/ELF/lto/asmundef.ll
@@ -1,25 +1,25 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t -save-temps
-; RUN: llvm-dis %t.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.0.4.opt.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
module asm ".weak patatino"
module asm ".equ patatino, foo"
declare void @patatino()
define void @foo() {
ret void
}
define void @_start() {
call void @patatino()
ret void
}
; CHECK: @llvm.compiler.used = appending global [1 x i8*] [i8* bitcast (void ()* @foo to i8*)], section "llvm.metadata"
; CHECK: define internal void @foo
diff --git a/lld/test/ELF/lto/available-externally.ll b/lld/test/ELF/lto/available-externally.ll
index 74aa860..181042b 100644
--- a/lld/test/ELF/lto/available-externally.ll
+++ b/lld/test/ELF/lto/available-externally.ll
@@ -1,22 +1,22 @@
; RUN: llvm-as %s -o %t1.o
; RUN: llvm-as %p/Inputs/available-externally.ll -o %t2.o
; RUN: ld.lld %t1.o %t2.o -m elf_x86_64 -o %t.so -shared -save-temps
-; RUN: llvm-dis < %t.so.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.so.0.2.internalize.bc | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define void @foo() {
call void @bar()
call void @zed()
ret void
}
define available_externally void @bar() {
ret void
}
define available_externally void @zed() {
ret void
}
; CHECK: define available_externally void @bar() {
; CHECK: define void @zed() {
diff --git a/lld/test/ELF/lto/common2.ll b/lld/test/ELF/lto/common2.ll
index 6b740c4..b44bbac 100644
--- a/lld/test/ELF/lto/common2.ll
+++ b/lld/test/ELF/lto/common2.ll
@@ -1,27 +1,27 @@
; RUN: llvm-as %s -o %t1.o
; RUN: ld.lld -m elf_x86_64 %t1.o -o %t -shared -save-temps
-; RUN: llvm-dis < %t.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.0.2.internalize.bc | FileCheck %s
; RUN: llvm-readobj -t %t | FileCheck %s --check-prefix=SHARED
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@a = common global i8 0, align 8
; CHECK-DAG: @a = common global i8 0, align 8
@b = common hidden global i32 0, align 4
define i32 @f() {
%t = load i32, i32* @b, align 4
ret i32 %t
}
; CHECK-DAG: @b = internal global i32 0, align 4
; SHARED: Symbol {
; SHARED: Name: a
; SHARED-NEXT: Value:
; SHARED-NEXT: Size: 1
; SHARED-NEXT: Binding: Global
; SHARED-NEXT: Type: Object
; SHARED-NEXT: Other: 0
; SHARED-NEXT: Section: .bss
; SHARED-NEXT: }
diff --git a/lld/test/ELF/lto/common3.ll b/lld/test/ELF/lto/common3.ll
index a6020ca..6d40de5 100644
--- a/lld/test/ELF/lto/common3.ll
+++ b/lld/test/ELF/lto/common3.ll
@@ -1,14 +1,14 @@
; RUN: llvm-as %s -o %t1.o
; RUN: llvm-as %S/Inputs/common3.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t -shared -save-temps
-; RUN: llvm-dis < %t.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.0.2.internalize.bc | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@a = common hidden global i32 0, align 8
define i32 @f() {
%t = load i32, i32* @a, align 4
ret i32 %t
}
; CHECK: @a = internal global i64 0, align 8
diff --git a/lld/test/ELF/lto/discard-value-names.ll b/lld/test/ELF/lto/discard-value-names.ll
index c6cd94f..c71dc38 100644
--- a/lld/test/ELF/lto/discard-value-names.ll
+++ b/lld/test/ELF/lto/discard-value-names.ll
@@ -1,23 +1,23 @@
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 -shared -save-temps %t.o -o %t2.o
-; RUN: llvm-dis < %t2.o.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.o.0.0.preopt.bc | FileCheck %s
; CHECK: @GlobalValueName
; CHECK: @foo(i32 %in)
; CHECK: somelabel:
; CHECK: %GV = load i32, i32* @GlobalValueName
; CHECK: %add = add i32 %in, %GV
; CHECK: ret i32 %add
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@GlobalValueName = global i32 0
define i32 @foo(i32 %in) {
somelabel:
%GV = load i32, i32* @GlobalValueName
%add = add i32 %in, %GV
ret i32 %add
}
diff --git a/lld/test/ELF/lto/drop-debug-info.ll b/lld/test/ELF/lto/drop-debug-info.ll
index 7a7ed5e..27c0260 100644
--- a/lld/test/ELF/lto/drop-debug-info.ll
+++ b/lld/test/ELF/lto/drop-debug-info.ll
@@ -1,9 +1,9 @@
; REQUIRES: x86
;
; drop-debug-info.bc was created from "void f(void) {}" with clang 3.5 and
; -gline-tables-only, so it contains old debug info.
;
; RUN: ld.lld -m elf_x86_64 -shared %p/Inputs/drop-debug-info.bc \
; RUN: -disable-verify 2>&1 | FileCheck %s
-; CHECK: warning: ignoring debug info with an invalid version (1) in {{.*}}drop-debug-info.bc
+; CHECK: ignoring debug info with an invalid version (1) in {{.*}}drop-debug-info.bc
diff --git a/lld/test/ELF/lto/drop-linkage.ll b/lld/test/ELF/lto/drop-linkage.ll
index fd111c1..1ff1796 100644
--- a/lld/test/ELF/lto/drop-linkage.ll
+++ b/lld/test/ELF/lto/drop-linkage.ll
@@ -1,14 +1,14 @@
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
; REQUIRES: x86
; RUN: llc %s -o %t.o -filetype=obj
; RUN: llvm-as %p/Inputs/drop-linkage.ll -o %t2.o
; RUN: ld.lld %t.o %t2.o -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
define void @foo() {
ret void
}
; CHECK: declare void @foo()
diff --git a/lld/test/ELF/lto/internalize-basic.ll b/lld/test/ELF/lto/internalize-basic.ll
index 396b9cb..43c1837 100644
--- a/lld/test/ELF/lto/internalize-basic.ll
+++ b/lld/test/ELF/lto/internalize-basic.ll
@@ -1,21 +1,21 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @_start() {
ret void
}
define hidden void @foo() {
ret void
}
; Check that _start is not internalized.
; CHECK: define void @_start()
; Check that foo function is correctly internalized.
; CHECK: define internal void @foo()
diff --git a/lld/test/ELF/lto/internalize-exportdyn.ll b/lld/test/ELF/lto/internalize-exportdyn.ll
index bee9a1e..2034a2b 100644
--- a/lld/test/ELF/lto/internalize-exportdyn.ll
+++ b/lld/test/ELF/lto/internalize-exportdyn.ll
@@ -1,47 +1,47 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/internalize-exportdyn.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t2 --export-dynamic -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @_start() {
ret void
}
define void @foo() {
ret void
}
define hidden void @bar() {
ret void
}
define linkonce_odr void @zed() local_unnamed_addr {
ret void
}
define linkonce_odr void @zed2() unnamed_addr {
ret void
}
define linkonce_odr void @bah() {
ret void
}
define linkonce_odr void @baz() {
ret void
}
@use_baz = global void ()* @baz
; Check what gets internalized.
; CHECK: define void @_start()
; CHECK: define void @foo()
; CHECK: define internal void @bar()
; CHECK: define internal void @zed()
; CHECK: define internal void @zed2()
; CHECK: define weak_odr void @bah()
; CHECK: define weak_odr void @baz()
diff --git a/lld/test/ELF/lto/internalize-llvmused.ll b/lld/test/ELF/lto/internalize-llvmused.ll
index 46c90a6..253dcb2 100644
--- a/lld/test/ELF/lto/internalize-llvmused.ll
+++ b/lld/test/ELF/lto/internalize-llvmused.ll
@@ -1,20 +1,20 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @_start() {
ret void
}
define hidden void @f() {
ret void
}
@llvm.used = appending global [1 x i8*] [ i8* bitcast (void ()* @f to i8*)]
; Check that f is not internalized.
; CHECK: define hidden void @f()
diff --git a/lld/test/ELF/lto/internalize-undef.ll b/lld/test/ELF/lto/internalize-undef.ll
index 5d74c31..f76528b 100644
--- a/lld/test/ELF/lto/internalize-undef.ll
+++ b/lld/test/ELF/lto/internalize-undef.ll
@@ -1,16 +1,16 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/internalize-undef.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t -save-temps
-; RUN: llvm-dis < %t.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.0.2.internalize.bc | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
declare void @f()
define void @_start() {
call void @f()
ret void
}
; CHECK: define internal void @f()
diff --git a/lld/test/ELF/lto/internalize-version-script.ll b/lld/test/ELF/lto/internalize-version-script.ll
index c25328f..c577e43 100644
--- a/lld/test/ELF/lto/internalize-version-script.ll
+++ b/lld/test/ELF/lto/internalize-version-script.ll
@@ -1,22 +1,22 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: echo "{ global: foo; local: *; };" > %t.script
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -shared --version-script %t.script -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.2.internalize.bc | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @foo() {
ret void
}
define void @bar() {
ret void
}
; Check that foo is not internalized.
; CHECK: define void @foo()
; Check that bar is correctly internalized.
; CHECK: define internal void @bar()
diff --git a/lld/test/ELF/lto/irmover-error.ll b/lld/test/ELF/lto/irmover-error.ll
index aee4114..8b9836d 100644
--- a/lld/test/ELF/lto/irmover-error.ll
+++ b/lld/test/ELF/lto/irmover-error.ll
@@ -1,12 +1,12 @@
; RUN: llvm-as -o %t1.bc %s
; RUN: llvm-as -o %t2.bc %S/Inputs/irmover-error.ll
; RUN: not ld.lld -m elf_x86_64 %t1.bc %t2.bc -o %t 2>&1 | FileCheck %s
-; CHECK: failed to link module {{.*}}2.bc: linking module flags 'foo': IDs have conflicting values
+; CHECK: linking module flags 'foo': IDs have conflicting values
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
!0 = !{ i32 1, !"foo", i32 1 }
!llvm.module.flags = !{ !0 }
diff --git a/lld/test/ELF/lto/linkonce-odr.ll b/lld/test/ELF/lto/linkonce-odr.ll
index 569e271..4423351 100644
--- a/lld/test/ELF/lto/linkonce-odr.ll
+++ b/lld/test/ELF/lto/linkonce-odr.ll
@@ -1,17 +1,17 @@
; REQUIRES: x86
; RUN: llvm-as %p/Inputs/linkonce-odr.ll -o %t1.o
; RUN: llc -relocation-model=pic %s -o %t2.o -filetype=obj
; RUN: ld.lld %t1.o %t2.o -o %t.so -shared -save-temps
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
declare void @f()
define void @g() {
call void @f()
ret void
}
; Be sure that 'f' is kept and has weak_odr linkage.
; CHECK: define weak_odr void @f()
diff --git a/lld/test/ELF/lto/linkonce.ll b/lld/test/ELF/lto/linkonce.ll
index f980eff..6dba6a3 100644
--- a/lld/test/ELF/lto/linkonce.ll
+++ b/lld/test/ELF/lto/linkonce.ll
@@ -1,17 +1,17 @@
; REQUIRES: x86
; RUN: llvm-as %p/Inputs/linkonce.ll -o %t1.o
; RUN: llc -relocation-model=pic %s -o %t2.o -filetype=obj
; RUN: ld.lld %t1.o %t2.o -o %t.so -shared -save-temps
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
declare void @f()
define void @g() {
call void @f()
ret void
}
; Be sure that 'f' is kept and has weak linkage.
; CHECK: define weak void @f()
diff --git a/lld/test/ELF/lto/ltopasses-basic.ll b/lld/test/ELF/lto/ltopasses-basic.ll
index 5bd5f41..0c4ad8b 100644
--- a/lld/test/ELF/lto/ltopasses-basic.ll
+++ b/lld/test/ELF/lto/ltopasses-basic.ll
@@ -1,18 +1,17 @@
; REQUIRES: x86
-; RUN: rm -f %t.so.lto.bc %t.so.lto.opt.bc %t.so.lto.o
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t.so -save-temps -mllvm -debug-pass=Arguments -shared 2>&1 | FileCheck %s --check-prefix=MLLVM
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 65535, void ()* @ctor, i8* null }]
define void @ctor() {
ret void
}
; `@ctor` doesn't do anything and so the optimizer should kill it, leaving no ctors
; CHECK: @llvm.global_ctors = appending global [0 x { i32, void ()*, i8* }] zeroinitializer
; MLLVM: Pass Arguments:
diff --git a/lld/test/ELF/lto/ltopasses-custom.ll b/lld/test/ELF/lto/ltopasses-custom.ll
index 147411a..a48959a 100644
--- a/lld/test/ELF/lto/ltopasses-custom.ll
+++ b/lld/test/ELF/lto/ltopasses-custom.ll
@@ -1,37 +1,37 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t.so -save-temps --lto-aa-pipeline=basic-aa \
; RUN: --lto-newpm-passes=ipsccp -shared
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2.so -save-temps --lto-newpm-passes=loweratomic -shared
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
-; RUN: llvm-dis %t2.so.lto.opt.bc -o - | FileCheck %s --check-prefix=ATOMIC
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t2.so.0.4.opt.bc -o - | FileCheck %s --check-prefix=ATOMIC
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define void @barrier() {
fence seq_cst
ret void
}
; IPSCCP won't remove the fence.
; CHECK: define void @barrier() {
; CHECK-NEXT: fence seq_cst
; CHECK-NEXT: ret void
; LowerAtomic will remove the fence.
; ATOMIC: define void @barrier() {
; ATOMIC-NEXT: ret void
; Check that invalid passes are rejected gracefully.
; RUN: not ld.lld -m elf_x86_64 %t.o -o %t2.so \
; RUN: --lto-newpm-passes=iamnotapass -shared 2>&1 | \
; RUN: FileCheck %s --check-prefix=INVALID
; INVALID: unable to parse pass pipeline description: iamnotapass
; Check that invalid AA pipelines are rejected gracefully.
; RUN: not ld.lld -m elf_x86_64 %t.o -o %t2.so \
; RUN: --lto-newpm-passes=globaldce --lto-aa-pipeline=patatino \
; RUN: -shared 2>&1 | \
; RUN: FileCheck %s --check-prefix=INVALIDAA
; INVALIDAA: unable to parse AA pipeline description: patatino
diff --git a/lld/test/ELF/lto/save-temps.ll b/lld/test/ELF/lto/save-temps.ll
index 0b0f939..f7af99e 100644
--- a/lld/test/ELF/lto/save-temps.ll
+++ b/lld/test/ELF/lto/save-temps.ll
@@ -1,20 +1,20 @@
; REQUIRES: x86
; RUN: cd %T
; RUN: rm -f a.out a.out.lto.bc a.out.lto.o
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/save-temps.ll -o %t2.o
; RUN: ld.lld -shared -m elf_x86_64 %t.o %t2.o -save-temps
; RUN: llvm-nm a.out | FileCheck %s
-; RUN: llvm-nm a.out.lto.bc | FileCheck %s
+; RUN: llvm-nm a.out.0.0.preopt.bc | FileCheck %s
; RUN: llvm-nm a.out.lto.o | FileCheck %s
-; RUN: llvm-dis a.out.lto.bc
+; RUN: llvm-dis a.out.0.0.preopt.bc
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @foo() {
ret void
}
; CHECK: T bar
; CHECK: T foo
diff --git a/lld/test/ELF/lto/type-merge.ll b/lld/test/ELF/lto/type-merge.ll
index 98db539..d6f196d 100644
--- a/lld/test/ELF/lto/type-merge.ll
+++ b/lld/test/ELF/lto/type-merge.ll
@@ -1,26 +1,26 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/type-merge.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t -shared -save-temps
-; RUN: llvm-dis < %t.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t.0.0.preopt.bc | FileCheck %s
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define void @foo() {
call void @bar(i8* null)
ret void
}
declare void @bar(i8*)
; CHECK: define void @foo() {
; CHECK-NEXT: call void @bar(i8* null)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
; CHECK: declare void @bar(i8*)
; CHECK: define void @zed() {
; CHECK-NEXT: call void bitcast (void (i8*)* @bar to void ()*)()
; CHECK-NEXT: ret void
; CHECK-NEXT: }
diff --git a/lld/test/ELF/lto/type-merge2.ll b/lld/test/ELF/lto/type-merge2.ll
index f0931dd..45777a7 100644
--- a/lld/test/ELF/lto/type-merge2.ll
+++ b/lld/test/ELF/lto/type-merge2.ll
@@ -1,27 +1,27 @@
; RUN: llvm-as %s -o %t.o
; RUN: llvm-as %p/Inputs/type-merge2.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t.o %t2.o -o %t.so -shared -save-temps
-; RUN: llvm-dis %t.so.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.0.preopt.bc -o - | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
%zed = type { i8 }
define void @foo() {
call void @bar(%zed* null)
ret void
}
declare void @bar(%zed*)
; CHECK: %zed = type { i8 }
; CHECK-NEXT: %zed.0 = type { i16 }
; CHECK: define void @foo() {
; CHECK-NEXT: call void bitcast (void (%zed.0*)* @bar to void (%zed*)*)(%zed* null)
; CHECK-NEXT: ret void
; CHECK-NEXT: }
; CHECK: define void @bar(%zed.0* %this) {
; CHECK-NEXT: store %zed.0* %this, %zed.0** null
; CHECK-NEXT: ret void
; CHECK-NEXT: }
diff --git a/lld/test/ELF/lto/unnamed-addr-comdat.ll b/lld/test/ELF/lto/unnamed-addr-comdat.ll
index c8c36de..ed48f3b 100644
--- a/lld/test/ELF/lto/unnamed-addr-comdat.ll
+++ b/lld/test/ELF/lto/unnamed-addr-comdat.ll
@@ -1,11 +1,11 @@
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o %t.o -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.2.internalize.bc -o - | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
$foo = comdat any
@foo = linkonce_odr unnamed_addr constant i32 42, comdat
; CHECK: @foo = internal unnamed_addr constant i32 42, comdat
diff --git a/lld/test/ELF/lto/unnamed-addr-drop.ll b/lld/test/ELF/lto/unnamed-addr-drop.ll
index 09bfd11..9142537 100644
--- a/lld/test/ELF/lto/unnamed-addr-drop.ll
+++ b/lld/test/ELF/lto/unnamed-addr-drop.ll
@@ -1,12 +1,12 @@
; RUN: llvm-as %s -o %t1.o
; RUN: llvm-as %S/Inputs/unnamed-addr-drop.ll -o %t2.o
; RUN: ld.lld -m elf_x86_64 %t1.o %t2.o -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.2.internalize.bc -o - | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
@foo = weak constant i32 41
; Check that unnamed_addr is dropped during the merge.
; CHECK: @foo = constant i32 42
diff --git a/lld/test/ELF/lto/unnamed-addr-lib.ll b/lld/test/ELF/lto/unnamed-addr-lib.ll
index fa0fe33..c2bc601 100644
--- a/lld/test/ELF/lto/unnamed-addr-lib.ll
+++ b/lld/test/ELF/lto/unnamed-addr-lib.ll
@@ -1,21 +1,21 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: llvm-mc %p/Inputs/unnamed-addr-lib.s -o %t2.o -filetype=obj -triple=x86_64-pc-linux
; RUN: ld.lld %t2.o -shared -o %t2.so
; RUN: ld.lld -m elf_x86_64 %t.o %t2.so -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.2.internalize.bc -o - | FileCheck %s
; This documents a small limitation of lld's internalization logic. We decide
; that bar should be in the symbol table because if it is it will preempt the
; one in the shared library.
; We could add one extra bit for ODR so that we know that preemption is not
; necessary, but that is probably not worth it.
-; CHECK: @foo = internal constant i8 42
-; CHECK: @bar = weak_odr constant i8 42
+; CHECK: @foo = internal unnamed_addr constant i8 42
+; CHECK: @bar = weak_odr unnamed_addr constant i8 42
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@foo = linkonce_odr unnamed_addr constant i8 42
@bar = linkonce_odr unnamed_addr constant i8 42
diff --git a/lld/test/ELF/lto/unnamed-addr.ll b/lld/test/ELF/lto/unnamed-addr.ll
index a2c0105..6a6dd73 100644
--- a/lld/test/ELF/lto/unnamed-addr.ll
+++ b/lld/test/ELF/lto/unnamed-addr.ll
@@ -1,14 +1,14 @@
; RUN: llvm-as %s -o %t.o
; RUN: ld.lld -m elf_x86_64 %t.o -o %t.so -save-temps -shared
-; RUN: llvm-dis %t.so.lto.opt.bc -o - | FileCheck %s
+; RUN: llvm-dis %t.so.0.4.opt.bc -o - | FileCheck %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
@a = internal unnamed_addr constant i8 42
define i8* @f() {
ret i8* @a
}
; CHECK: @a = internal unnamed_addr constant i8 42
diff --git a/lld/test/ELF/lto/version-script.ll b/lld/test/ELF/lto/version-script.ll
index 11a7f07..c43b443 100644
--- a/lld/test/ELF/lto/version-script.ll
+++ b/lld/test/ELF/lto/version-script.ll
@@ -1,50 +1,50 @@
; REQUIRES: x86
; RUN: llvm-as %s -o %t.o
; RUN: echo "VERSION_1.0{ global: foo; local: *; }; VERSION_2.0{ global: bar; local: *; };" > %t.script
; RUN: ld.lld -m elf_x86_64 %t.o -o %t2 -shared --version-script %t.script -save-temps
-; RUN: llvm-dis < %t2.lto.bc | FileCheck %s
+; RUN: llvm-dis < %t2.0.0.preopt.bc | FileCheck %s
; RUN: llvm-readobj -V -dyn-symbols %t2 | FileCheck --check-prefix=DSO %s
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
define void @foo() {
ret void
}
define void @bar() {
ret void
}
; CHECK: define void @foo()
; CHECK: define void @bar()
; DSO: DynamicSymbols [
; DSO: Symbol {
; DSO: Name: @ (0)
; DSO: Value: 0x0
; DSO: Size: 0
; DSO: Binding: Local
; DSO: Type: None
; DSO: Other: 0
; DSO: Section: Undefined
; DSO: }
; DSO: Symbol {
; DSO: Name: foo@@VERSION_1.0
; DSO: Value: 0x1000
; DSO: Size: 1
; DSO: Binding: Global
; DSO: Type: Function
; DSO: Other: 0
; DSO: Section: .text
; DSO: }
; DSO: Symbol {
; DSO: Name: bar@@VERSION_2.0
; DSO: Value: 0x1010
; DSO: Size: 1
; DSO: Binding: Global
; DSO: Type: Function
; DSO: Other: 0
; DSO: Section: .text
; DSO: }
; DSO: ]
diff --git a/llvm/include/llvm/LTO/LTO.h b/llvm/include/llvm/LTO/LTO.h
index b836ddb4..e0f581e 100644
--- a/llvm/include/llvm/LTO/LTO.h
+++ b/llvm/include/llvm/LTO/LTO.h
@@ -1,468 +1,472 @@
//===-LTO.h - LLVM Link Time Optimizer ------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares functions and classes used to support LTO. It is intended
// to be used both by LTO classes as well as by clients (gold-plugin) that
// don't utilize the LTO code generator interfaces.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LTO_LTO_H
#define LLVM_LTO_LTO_H
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/CodeGen/Analysis.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/LTO/Config.h"
#include "llvm/Linker/IRMover.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Support/thread.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
namespace llvm {
class Error;
class LLVMContext;
class MemoryBufferRef;
class Module;
class Target;
class raw_pwrite_stream;
/// Helper to load a module from bitcode.
std::unique_ptr<Module> loadModuleFromBuffer(const MemoryBufferRef &Buffer,
LLVMContext &Context, bool Lazy);
/// Provide a "loader" for the FunctionImporter to access function from other
/// modules.
class ModuleLoader {
/// The context that will be used for importing.
LLVMContext &Context;
/// Map from Module identifier to MemoryBuffer. Used by clients like the
/// FunctionImported to request loading a Module.
StringMap<MemoryBufferRef> &ModuleMap;
public:
ModuleLoader(LLVMContext &Context, StringMap<MemoryBufferRef> &ModuleMap)
: Context(Context), ModuleMap(ModuleMap) {}
/// Load a module on demand.
std::unique_ptr<Module> operator()(StringRef Identifier) {
return loadModuleFromBuffer(ModuleMap[Identifier], Context, /*Lazy*/ true);
}
};
/// Resolve Weak and LinkOnce values in the \p Index. Linkage changes recorded
/// in the index and the ThinLTO backends must apply the changes to the Module
/// via thinLTOResolveWeakForLinkerModule.
///
/// This is done for correctness (if value exported, ensure we always
/// emit a copy), and compile-time optimization (allow drop of duplicates).
void thinLTOResolveWeakForLinkerInIndex(
ModuleSummaryIndex &Index,
function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
isPrevailing,
function_ref<void(StringRef, GlobalValue::GUID, GlobalValue::LinkageTypes)>
recordNewLinkage);
/// Update the linkages in the given \p Index to mark exported values
/// as external and non-exported values as internal. The ThinLTO backends
/// must apply the changes to the Module via thinLTOInternalizeModule.
void thinLTOInternalizeAndPromoteInIndex(
ModuleSummaryIndex &Index,
function_ref<bool(StringRef, GlobalValue::GUID)> isExported);
namespace lto {
/// Given the original \p Path to an output file, replace any path
/// prefix matching \p OldPrefix with \p NewPrefix. Also, create the
/// resulting directory if it does not yet exist.
std::string getThinLTOOutputFile(const std::string &Path,
const std::string &OldPrefix,
const std::string &NewPrefix);
class LTO;
struct SymbolResolution;
class ThinBackendProc;
/// An input file. This is a wrapper for IRObjectFile that exposes only the
/// information that an LTO client should need in order to do symbol resolution.
class InputFile {
// FIXME: Remove LTO class friendship once we have bitcode symbol tables.
friend LTO;
InputFile() = default;
// FIXME: Remove the LLVMContext once we have bitcode symbol tables.
LLVMContext Ctx;
std::unique_ptr<object::IRObjectFile> Obj;
public:
/// Create an InputFile.
static Expected<std::unique_ptr<InputFile>> create(MemoryBufferRef Object);
class symbol_iterator;
/// This is a wrapper for object::basic_symbol_iterator that exposes only the
/// information that an LTO client should need in order to do symbol
/// resolution.
///
/// This object is ephemeral; it is only valid as long as an iterator obtained
/// from symbols() refers to it.
class Symbol {
friend symbol_iterator;
friend LTO;
object::basic_symbol_iterator I;
const GlobalValue *GV;
uint32_t Flags;
SmallString<64> Name;
bool shouldSkip() {
return !(Flags & object::BasicSymbolRef::SF_Global) ||
(Flags & object::BasicSymbolRef::SF_FormatSpecific);
}
void skip() {
const object::SymbolicFile *Obj = I->getObject();
auto E = Obj->symbol_end();
while (I != E) {
Flags = I->getFlags();
if (!shouldSkip())
break;
++I;
}
if (I == E)
return;
Name.clear();
{
raw_svector_ostream OS(Name);
I->printName(OS);
}
GV = cast<object::IRObjectFile>(Obj)->getSymbolGV(I->getRawDataRefImpl());
}
public:
Symbol(object::basic_symbol_iterator I) : I(I) { skip(); }
StringRef getName() const { return Name; }
StringRef getIRName() const {
if (GV)
return GV->getName();
return StringRef();
}
uint32_t getFlags() const { return Flags; }
GlobalValue::VisibilityTypes getVisibility() const {
if (GV)
return GV->getVisibility();
return GlobalValue::DefaultVisibility;
}
bool canBeOmittedFromSymbolTable() const {
return GV && llvm::canBeOmittedFromSymbolTable(GV);
}
bool isTLS() const {
// FIXME: Expose a thread-local flag for module asm symbols.
return GV && GV->isThreadLocal();
}
Expected<const Comdat *> getComdat() const {
if (!GV)
return nullptr;
const GlobalObject *GO;
if (auto *GA = dyn_cast<GlobalAlias>(GV)) {
GO = GA->getBaseObject();
if (!GO)
return make_error<StringError>("Unable to determine comdat of alias!",
inconvertibleErrorCode());
} else {
GO = cast<GlobalObject>(GV);
}
if (GO)
return GO->getComdat();
return nullptr;
}
uint64_t getCommonSize() const {
assert(Flags & object::BasicSymbolRef::SF_Common);
if (!GV)
return 0;
return GV->getParent()->getDataLayout().getTypeAllocSize(
GV->getType()->getElementType());
}
unsigned getCommonAlignment() const {
assert(Flags & object::BasicSymbolRef::SF_Common);
if (!GV)
return 0;
return GV->getAlignment();
}
};
class symbol_iterator {
Symbol Sym;
public:
symbol_iterator(object::basic_symbol_iterator I) : Sym(I) {}
symbol_iterator &operator++() {
++Sym.I;
Sym.skip();
return *this;
}
symbol_iterator operator++(int) {
symbol_iterator I = *this;
++*this;
return I;
}
const Symbol &operator*() const { return Sym; }
const Symbol *operator->() const { return &Sym; }
bool operator!=(const symbol_iterator &Other) const {
return Sym.I != Other.Sym.I;
}
};
/// A range over the symbols in this InputFile.
iterator_range<symbol_iterator> symbols() {
return llvm::make_range(symbol_iterator(Obj->symbol_begin()),
symbol_iterator(Obj->symbol_end()));
}
StringRef getDataLayoutStr() const {
return Obj->getModule().getDataLayoutStr();
}
StringRef getSourceFileName() const {
return Obj->getModule().getSourceFileName();
}
MemoryBufferRef getMemoryBufferRef() const {
return Obj->getMemoryBufferRef();
}
+
+ StringMap<Comdat> &getComdatSymbolTable() {
+ return Obj->getModule().getComdatSymbolTable();
+ }
};
/// This class wraps an output stream for a native object. Most clients should
/// just be able to return an instance of this base class from the stream
/// callback, but if a client needs to perform some action after the stream is
/// written to, that can be done by deriving from this class and overriding the
/// destructor.
class NativeObjectStream {
public:
NativeObjectStream(std::unique_ptr<raw_pwrite_stream> OS) : OS(std::move(OS)) {}
std::unique_ptr<raw_pwrite_stream> OS;
virtual ~NativeObjectStream() = default;
};
/// This type defines the callback to add a native object that is generated on
/// the fly.
///
/// Stream callbacks must be thread safe.
typedef std::function<std::unique_ptr<NativeObjectStream>(unsigned Task)>
AddStreamFn;
/// This is the type of a native object cache. To request an item from the
/// cache, pass a unique string as the Key. For hits, the cached file will be
/// added to the link and this function will return AddStreamFn(). For misses,
/// the cache will return a stream callback which must be called at most once to
/// produce content for the stream. The native object stream produced by the
/// stream callback will add the file to the link after the stream is written
/// to.
///
/// Clients generally look like this:
///
/// if (AddStreamFn AddStream = Cache(Task, Key))
/// ProduceContent(AddStream);
typedef std::function<AddStreamFn(unsigned Task, StringRef Key)>
NativeObjectCache;
/// A ThinBackend defines what happens after the thin-link phase during ThinLTO.
/// The details of this type definition aren't important; clients can only
/// create a ThinBackend using one of the create*ThinBackend() functions below.
typedef std::function<std::unique_ptr<ThinBackendProc>(
Config &C, ModuleSummaryIndex &CombinedIndex,
StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries,
AddStreamFn AddStream, NativeObjectCache Cache)>
ThinBackend;
/// This ThinBackend runs the individual backend jobs in-process.
ThinBackend createInProcessThinBackend(unsigned ParallelismLevel);
/// This ThinBackend writes individual module indexes to files, instead of
/// running the individual backend jobs. This backend is for distributed builds
/// where separate processes will invoke the real backends.
///
/// To find the path to write the index to, the backend checks if the path has a
/// prefix of OldPrefix; if so, it replaces that prefix with NewPrefix. It then
/// appends ".thinlto.bc" and writes the index to that path. If
/// ShouldEmitImportsFiles is true it also writes a list of imported files to a
/// similar path with ".imports" appended instead.
ThinBackend createWriteIndexesThinBackend(std::string OldPrefix,
std::string NewPrefix,
bool ShouldEmitImportsFiles,
std::string LinkedObjectsFile);
/// This class implements a resolution-based interface to LLVM's LTO
/// functionality. It supports regular LTO, parallel LTO code generation and
/// ThinLTO. You can use it from a linker in the following way:
/// - Set hooks and code generation options (see lto::Config struct defined in
/// Config.h), and use the lto::Config object to create an lto::LTO object.
/// - Create lto::InputFile objects using lto::InputFile::create(), then use
/// the symbols() function to enumerate its symbols and compute a resolution
/// for each symbol (see SymbolResolution below).
/// - After the linker has visited each input file (and each regular object
/// file) and computed a resolution for each symbol, take each lto::InputFile
/// and pass it and an array of symbol resolutions to the add() function.
/// - Call the getMaxTasks() function to get an upper bound on the number of
/// native object files that LTO may add to the link.
/// - Call the run() function. This function will use the supplied AddStream
/// and Cache functions to add up to getMaxTasks() native object files to
/// the link.
class LTO {
friend InputFile;
public:
/// Create an LTO object. A default constructed LTO object has a reasonable
/// production configuration, but you can customize it by passing arguments to
/// this constructor.
/// FIXME: We do currently require the DiagHandler field to be set in Conf.
/// Until that is fixed, a Config argument is required.
LTO(Config Conf, ThinBackend Backend = nullptr,
unsigned ParallelCodeGenParallelismLevel = 1);
/// Add an input file to the LTO link, using the provided symbol resolutions.
/// The symbol resolutions must appear in the enumeration order given by
/// InputFile::symbols().
Error add(std::unique_ptr<InputFile> Obj, ArrayRef<SymbolResolution> Res);
/// Returns an upper bound on the number of tasks that the client may expect.
/// This may only be called after all IR object files have been added. For a
/// full description of tasks see LTOBackend.h.
unsigned getMaxTasks() const;
/// Runs the LTO pipeline. This function calls the supplied AddStream
/// function to add native object files to the link.
///
/// The Cache parameter is optional. If supplied, it will be used to cache
/// native object files and add them to the link.
///
/// The client will receive at most one callback (via either AddStream or
/// Cache) for each task identifier.
Error run(AddStreamFn AddStream, NativeObjectCache Cache = nullptr);
private:
Config Conf;
struct RegularLTOState {
RegularLTOState(unsigned ParallelCodeGenParallelismLevel, Config &Conf);
struct CommonResolution {
uint64_t Size = 0;
unsigned Align = 0;
/// Record if at least one instance of the common was marked as prevailing
bool Prevailing = false;
};
std::map<std::string, CommonResolution> Commons;
unsigned ParallelCodeGenParallelismLevel;
LTOLLVMContext Ctx;
bool HasModule = false;
std::unique_ptr<Module> CombinedModule;
std::unique_ptr<IRMover> Mover;
} RegularLTO;
struct ThinLTOState {
ThinLTOState(ThinBackend Backend);
ThinBackend Backend;
ModuleSummaryIndex CombinedIndex;
MapVector<StringRef, MemoryBufferRef> ModuleMap;
DenseMap<GlobalValue::GUID, StringRef> PrevailingModuleForGUID;
} ThinLTO;
// The global resolution for a particular (mangled) symbol name. This is in
// particular necessary to track whether each symbol can be internalized.
// Because any input file may introduce a new cross-partition reference, we
// cannot make any final internalization decisions until all input files have
// been added and the client has called run(). During run() we apply
// internalization decisions either directly to the module (for regular LTO)
// or to the combined index (for ThinLTO).
struct GlobalResolution {
/// The unmangled name of the global.
std::string IRName;
bool UnnamedAddr = true;
/// This field keeps track of the partition number of this global. The
/// regular LTO object is partition 0, while each ThinLTO object has its own
/// partition number from 1 onwards.
///
/// Any global that is defined or used by more than one partition, or that
/// is referenced externally, may not be internalized.
///
/// Partitions generally have a one-to-one correspondence with tasks, except
/// that we use partition 0 for all parallel LTO code generation partitions.
/// Any partitioning of the combined LTO object is done internally by the
/// LTO backend.
unsigned Partition = Unknown;
/// Special partition numbers.
enum : unsigned {
/// A partition number has not yet been assigned to this global.
Unknown = -1u,
/// This global is either used by more than one partition or has an
/// external reference, and therefore cannot be internalized.
External = -2u,
};
};
// Global mapping from mangled symbol names to resolutions.
StringMap<GlobalResolution> GlobalResolutions;
void addSymbolToGlobalRes(object::IRObjectFile *Obj,
SmallPtrSet<GlobalValue *, 8> &Used,
const InputFile::Symbol &Sym, SymbolResolution Res,
unsigned Partition);
Error addRegularLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res);
Error addThinLTO(std::unique_ptr<InputFile> Input,
ArrayRef<SymbolResolution> Res);
Error runRegularLTO(AddStreamFn AddStream);
Error runThinLTO(AddStreamFn AddStream, NativeObjectCache Cache,
bool HasRegularLTO);
mutable bool CalledGetMaxTasks = false;
};
/// The resolution for a symbol. The linker must provide a SymbolResolution for
/// each global symbol based on its internal resolution of that symbol.
struct SymbolResolution {
SymbolResolution()
: Prevailing(0), FinalDefinitionInLinkageUnit(0), VisibleToRegularObj(0) {
}
/// The linker has chosen this definition of the symbol.
unsigned Prevailing : 1;
/// The definition of this symbol is unpreemptable at runtime and is known to
/// be in this linkage unit.
unsigned FinalDefinitionInLinkageUnit : 1;
/// The definition of this symbol is visible outside of the LTO unit.
unsigned VisibleToRegularObj : 1;
};
} // namespace lto
} // namespace llvm
#endif
More information about the llvm-commits
mailing list