Merge commit 500a98301c1b
- upstream svn@328902
Test: git diff 500a98301c1b == git diff dccbfb97022 84071b532ebf
Change-Id: I187317e286d2598489813a281482c62dec4326f5
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 6da121e..451e18a 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -571,5 +571,47 @@
}
}
+std::map<uint32_t, MergeChunk *> MergeChunk::Instances;
+
+MergeChunk::MergeChunk(uint32_t Alignment)
+ : Builder(StringTableBuilder::RAW, Alignment) {
+ this->Alignment = Alignment;
+}
+
+void MergeChunk::addSection(SectionChunk *C) {
+ auto *&MC = Instances[C->Alignment];
+ if (!MC)
+ MC = make<MergeChunk>(C->Alignment);
+ MC->Sections.push_back(C);
+}
+
+void MergeChunk::finalizeContents() {
+ for (SectionChunk *C : Sections)
+ if (C->isLive())
+ Builder.add(toStringRef(C->getContents()));
+ Builder.finalize();
+
+ for (SectionChunk *C : Sections) {
+ if (!C->isLive())
+ continue;
+ size_t Off = Builder.getOffset(toStringRef(C->getContents()));
+ C->setOutputSection(Out);
+ C->setRVA(RVA + Off);
+ C->OutputSectionOff = OutputSectionOff + Off;
+ }
+}
+
+uint32_t MergeChunk::getPermissions() const {
+ return IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA;
+}
+
+size_t MergeChunk::getSize() const {
+ return Builder.getSize();
+}
+
+void MergeChunk::writeTo(uint8_t *Buf) const {
+ Builder.write(Buf + OutputSectionOff);
+}
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Chunks.h b/COFF/Chunks.h
index b4b2299..b95869a 100644
--- a/COFF/Chunks.h
+++ b/COFF/Chunks.h
@@ -16,6 +16,7 @@
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/iterator.h"
#include "llvm/ADT/iterator_range.h"
+#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/COFF.h"
#include <utility>
#include <vector>
@@ -60,6 +61,10 @@
// before calling this function.
virtual void writeTo(uint8_t *Buf) const {}
+ // Called by the writer after an RVA is assigned, but before calling
+ // getSize().
+ virtual void finalizeContents() {}
+
// The writer sets and uses the addresses.
uint64_t getRVA() const { return RVA; }
void setRVA(uint64_t V) { RVA = V; }
@@ -222,6 +227,33 @@
uint32_t Class[2] = {0, 0};
};
+// This class is used to implement an lld-specific feature (not implemented in
+// MSVC) that minimizes the output size by finding string literals sharing tail
+// parts and merging them.
+//
+// If string tail merging is enabled and a section is identified as containing a
+// string literal, it is added to a MergeChunk with an appropriate alignment.
+// The MergeChunk then tail merges the strings using the StringTableBuilder
+// class and assigns RVAs and section offsets to each of the member chunks based
+// on the offsets assigned by the StringTableBuilder.
+class MergeChunk : public Chunk {
+public:
+ MergeChunk(uint32_t Alignment);
+ static void addSection(SectionChunk *C);
+ void finalizeContents() override;
+
+ uint32_t getPermissions() const override;
+ StringRef getSectionName() const override { return ".rdata"; }
+ size_t getSize() const override;
+ void writeTo(uint8_t *Buf) const override;
+
+ static std::map<uint32_t, MergeChunk *> Instances;
+ std::vector<SectionChunk *> Sections;
+
+private:
+ llvm::StringTableBuilder Builder;
+};
+
// A chunk for common symbols. Common chunks don't have actual data.
class CommonChunk : public Chunk {
public:
diff --git a/COFF/Config.h b/COFF/Config.h
index c3e9730..1a03a24 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -99,6 +99,7 @@
bool DebugGHashes = false;
bool ShowTiming = false;
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
+ std::vector<std::string> NatvisFiles;
llvm::SmallString<128> PDBPath;
std::vector<llvm::StringRef> Argv;
@@ -187,8 +188,10 @@
bool HighEntropyVA = false;
bool AppContainer = false;
bool MinGW = false;
+ bool WarnMissingOrderSymbol = true;
bool WarnLocallyDefinedImported = true;
bool Incremental = true;
+ bool KillAt = false;
};
extern Configuration *Config;
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index e493bde..02b2208 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -61,7 +61,7 @@
errorHandler().ColorDiagnostics = Diag.has_colors();
errorHandler().ErrorLimitExceededMsg =
"too many errors emitted, stopping now"
- " (use /ERRORLIMIT:0 to see all errors)";
+ " (use /errorlimit:0 to see all errors)";
errorHandler().ExitEarly = CanExitEarly;
Config = make<Configuration>();
Config->Argv = {Args.begin(), Args.end()};
@@ -804,8 +804,10 @@
if (Config->Machine == I386 && !isDecorated(S))
S = "_" + S;
- if (Set.count(S) == 0)
- warn("/order:" + Arg + ": missing symbol: " + S);
+ if (Set.count(S) == 0) {
+ if (Config->WarnMissingOrderSymbol)
+ warn("/order:" + Arg + ": missing symbol: " + S + " [LNK4037]");
+ }
else
Config->Order[S] = INT_MIN + Config->Order.size();
}
@@ -899,7 +901,9 @@
// Handle /ignore
for (auto *Arg : Args.filtered(OPT_ignore)) {
- if (StringRef(Arg->getValue()) == "4217")
+ if (StringRef(Arg->getValue()) == "4037")
+ Config->WarnMissingOrderSymbol = false;
+ else if (StringRef(Arg->getValue()) == "4217")
Config->WarnLocallyDefinedImported = false;
// Other warning numbers are ignored.
}
@@ -929,9 +933,12 @@
// Handle /pdb
bool ShouldCreatePDB = Args.hasArg(OPT_debug, OPT_debug_ghash);
- if (ShouldCreatePDB)
+ if (ShouldCreatePDB) {
if (auto *Arg = Args.getLastArg(OPT_pdb))
Config->PDBPath = Arg->getValue();
+ if (Args.hasArg(OPT_natvis))
+ Config->NatvisFiles = Args.getAllArgValues(OPT_natvis);
+ }
// Handle /noentry
if (Args.hasArg(OPT_noentry)) {
@@ -955,6 +962,9 @@
DynamicBaseArg->getOption().getID() == OPT_dynamicbase_no)
Config->DynamicBase = false;
+ // MSDN claims "/FIXED:NO is the default setting for a DLL, and /FIXED is the
+ // default setting for any other project type.", but link.exe defaults to
+ // /FIXED:NO for exe outputs as well. Match behavior, not docs.
bool Fixed = Args.hasFlag(OPT_fixed, OPT_fixed_no, false);
if (Fixed) {
if (DynamicBaseArg &&
@@ -1021,8 +1031,9 @@
Config->Implib = Arg->getValue();
// Handle /opt.
- bool DoGC = !Args.hasArg(OPT_debug);
- unsigned ICFLevel = 1; // 0: off, 1: limited, 2: on
+ bool DoGC = !Args.hasArg(OPT_debug) || Args.hasArg(OPT_profile);
+ unsigned ICFLevel =
+ Args.hasArg(OPT_profile) ? 0 : 1; // 0: off, 1: limited, 2: on
for (auto *Arg : Args.filtered(OPT_opt)) {
std::string Str = StringRef(Arg->getValue()).lower();
SmallVector<StringRef, 1> Vec;
@@ -1069,6 +1080,10 @@
if (Args.hasArg(OPT_lldsavetemps))
Config->SaveTemps = true;
+ // Handle /kill-at
+ if (Args.hasArg(OPT_kill_at))
+ Config->KillAt = true;
+
// Handle /lldltocache
if (auto *Arg = Args.getLastArg(OPT_lldltocache))
Config->LTOCache = Arg->getValue();
@@ -1124,7 +1139,7 @@
if (!Config->ManifestInput.empty() &&
Config->Manifest != Configuration::Embed) {
- fatal("/MANIFESTINPUT: requires /MANIFEST:EMBED");
+ fatal("/manifestinput: requires /manifest:embed");
}
// Handle miscellaneous boolean flags.
@@ -1133,7 +1148,8 @@
Args.hasFlag(OPT_allowisolation, OPT_allowisolation_no, true);
Config->Incremental =
Args.hasFlag(OPT_incremental, OPT_incremental_no,
- !Config->DoGC && !Config->DoICF && !Args.hasArg(OPT_order));
+ !Config->DoGC && !Config->DoICF && !Args.hasArg(OPT_order) &&
+ !Args.hasArg(OPT_profile));
Config->NxCompat = Args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
Config->TerminalServerAware = Args.hasFlag(OPT_tsaware, OPT_tsaware_no, true);
Config->DebugDwarf = Args.hasArg(OPT_debug_dwarf);
@@ -1141,23 +1157,28 @@
Config->MapFile = getMapFile(Args);
+ if (Config->Incremental && Args.hasArg(OPT_profile)) {
+ warn("ignoring '/incremental' due to '/profile' specification");
+ Config->Incremental = false;
+ }
+
+ if (Config->Incremental && Args.hasArg(OPT_order)) {
+ warn("ignoring '/incremental' due to '/order' specification");
+ Config->Incremental = false;
+ }
+
if (Config->Incremental && Config->DoGC) {
- warn("ignoring '/INCREMENTAL' because REF is enabled; use '/OPT:NOREF' to "
+ warn("ignoring '/incremental' because REF is enabled; use '/opt:noref' to "
"disable");
Config->Incremental = false;
}
if (Config->Incremental && Config->DoICF) {
- warn("ignoring '/INCREMENTAL' because ICF is enabled; use '/OPT:NOICF' to "
+ warn("ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to "
"disable");
Config->Incremental = false;
}
- if (Config->Incremental && Args.hasArg(OPT_order)) {
- warn("ignoring '/INCREMENTAL' due to '/ORDER' specification");
- Config->Incremental = false;
- }
-
if (errorCount())
return;
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 1191530..0b705f4 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -139,7 +139,7 @@
else if (Arg.equals_lower("cf") || Arg.equals_lower("longjmp"))
Config->GuardCF = GuardCFLevel::Full;
else
- fatal("invalid argument to /GUARD: " + Arg);
+ fatal("invalid argument to /guard: " + Arg);
}
}
@@ -582,6 +582,26 @@
return Sym.startswith("_") ? Sym.substr(1) : Sym;
}
+// Convert stdcall/fastcall style symbols into unsuffixed symbols,
+// with or without a leading underscore. (MinGW specific.)
+static StringRef killAt(StringRef Sym, bool Prefix) {
+ if (Sym.empty())
+ return Sym;
+ // Strip any trailing stdcall suffix
+ Sym = Sym.substr(0, Sym.find('@', 1));
+ if (!Sym.startswith("@")) {
+ if (Prefix && !Sym.startswith("_"))
+ return Saver.save("_" + Sym);
+ return Sym;
+ }
+ // For fastcall, remove the leading @ and replace it with an
+ // underscore, if prefixes are used.
+ Sym = Sym.substr(1);
+ if (Prefix)
+ Sym = Saver.save("_" + Sym);
+ return Sym;
+}
+
// Performs error checking on all /export arguments.
// It also sets ordinals.
void fixupExports() {
@@ -614,6 +634,15 @@
}
}
+ if (Config->KillAt && Config->Machine == I386) {
+ for (Export &E : Config->Exports) {
+ E.Name = killAt(E.Name, true);
+ E.ExportName = killAt(E.ExportName, false);
+ E.ExtName = killAt(E.ExtName, true);
+ E.SymbolName = killAt(E.SymbolName, true);
+ }
+ }
+
// Uniquefy by name.
DenseMap<StringRef, Export *> Map(Config->Exports.size());
std::vector<Export> V;
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index c063ab2..77c05b7 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -168,6 +168,7 @@
return std::equal(A->Relocs.begin(), A->Relocs.end(), B->Relocs.begin(), Eq);
}
+// Find the first Chunk after Begin that has a different class from Begin.
size_t ICF::findBoundary(size_t Begin, size_t End) {
for (size_t I = Begin + 1; I < End; ++I)
if (Chunks[Begin]->Class[Cnt % 2] != Chunks[I]->Class[Cnt % 2])
@@ -177,11 +178,8 @@
void ICF::forEachClassRange(size_t Begin, size_t End,
std::function<void(size_t, size_t)> Fn) {
- if (Begin > 0)
- Begin = findBoundary(Begin - 1, End);
-
while (Begin < End) {
- size_t Mid = findBoundary(Begin, Chunks.size());
+ size_t Mid = findBoundary(Begin, End);
Fn(Begin, Mid);
Begin = Mid;
}
@@ -197,12 +195,22 @@
return;
}
- // Split sections into 256 shards and call Fn in parallel.
- size_t NumShards = 256;
+ // Shard into non-overlapping intervals, and call Fn in parallel.
+ // The sharding must be completed before any calls to Fn are made
+ // so that Fn can modify the Chunks in its shard without causing data
+ // races.
+ const size_t NumShards = 256;
size_t Step = Chunks.size() / NumShards;
- for_each_n(parallel::par, size_t(0), NumShards, [&](size_t I) {
- size_t End = (I == NumShards - 1) ? Chunks.size() : (I + 1) * Step;
- forEachClassRange(I * Step, End, Fn);
+ size_t Boundaries[NumShards + 1];
+ Boundaries[0] = 0;
+ Boundaries[NumShards] = Chunks.size();
+ for_each_n(parallel::par, size_t(1), NumShards, [&](size_t I) {
+ Boundaries[I] = findBoundary((I - 1) * Step, Chunks.size());
+ });
+ for_each_n(parallel::par, size_t(1), NumShards + 1, [&](size_t I) {
+ if (Boundaries[I - 1] < Boundaries[I]) {
+ forEachClassRange(Boundaries[I - 1], Boundaries[I], Fn);
+ }
});
++Cnt;
}
@@ -224,6 +232,12 @@
}
}
+ // Make sure that ICF doesn't merge sections that are being handled by string
+ // tail merging.
+ for (auto &P : MergeChunk::Instances)
+ for (SectionChunk *SC : P.second->Sections)
+ SC->Class[0] = NextId++;
+
// Initially, we use hash values to partition sections.
for_each(parallel::par, Chunks.begin(), Chunks.end(), [&](SectionChunk *SC) {
// Set MSB to 1 to avoid collisions with non-hash classs.
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index b60e8c8..78bfe34 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -138,12 +138,13 @@
if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
SparseChunks[I] = PendingComdat;
else
- SparseChunks[I] = readSection(I, nullptr);
+ SparseChunks[I] = readSection(I, nullptr, "");
}
}
SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
- const coff_aux_section_definition *Def) {
+ const coff_aux_section_definition *Def,
+ StringRef LeaderName) {
const coff_section *Sec;
StringRef Name;
if (auto EC = COFFObj->getSection(SectionNumber, Sec))
@@ -189,6 +190,12 @@
GuardLJmpChunks.push_back(C);
else if (Name == ".sxdata")
SXDataChunks.push_back(C);
+ else if (Config->DoICF && Sec->NumberOfRelocations == 0 && Name == ".rdata" &&
+ LeaderName.startswith("??_C@"))
+ // COFF sections that look like string literal sections (i.e. no
+ // relocations, in .rdata, leader symbol name matches the MSVC name mangling
+ // for string literals) are subject to string tail merging.
+ MergeChunk::addSection(C);
else
Chunks.push_back(C);
@@ -209,7 +216,7 @@
// the section; otherwise mark it as discarded.
int32_t SectionNumber = Sym.getSectionNumber();
if (Parent) {
- SparseChunks[SectionNumber] = readSection(SectionNumber, Def);
+ SparseChunks[SectionNumber] = readSection(SectionNumber, Def, "");
if (SparseChunks[SectionNumber])
Parent->addAssociative(SparseChunks[SectionNumber]);
} else {
@@ -343,7 +350,7 @@
Prevailing = true;
}
if (Prevailing) {
- SectionChunk *C = readSection(SectionNumber, Def);
+ SectionChunk *C = readSection(SectionNumber, Def, Name);
SparseChunks[SectionNumber] = C;
C->Sym = cast<DefinedRegular>(Leader);
cast<DefinedRegular>(Leader)->Data = &C->Repl;
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 000fdc6..3ee5780 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -150,7 +150,8 @@
SectionChunk *
readSection(uint32_t SectionNumber,
- const llvm::object::coff_aux_section_definition *Def);
+ const llvm::object::coff_aux_section_definition *Def,
+ StringRef LeaderName);
void readAssociativeDefinition(
COFFSymbolRef COFFSym,
diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp
index 8a1c82c..56ca8c0 100644
--- a/COFF/LTO.cpp
+++ b/COFF/LTO.cpp
@@ -64,6 +64,12 @@
static std::unique_ptr<lto::LTO> createLTO() {
lto::Config Conf;
Conf.Options = InitTargetOptionsFromCodeGenFlags();
+
+ // Always emit a section per function/datum with LTO. LLVM LTO should get most
+ // of the benefit of linker GC, but there are still opportunities for ICF.
+ Conf.Options.FunctionSections = true;
+ Conf.Options.DataSections = true;
+
// Use static reloc model on 32-bit x86 because it usually results in more
// compact code, and because there are also known code generation bugs when
// using the PIC model (see PR34306).
diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp
index d475641..6ca1b66 100644
--- a/COFF/MapFile.cpp
+++ b/COFF/MapFile.cpp
@@ -36,14 +36,15 @@
typedef DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>
SymbolMapTy;
+static const std::string Indent8 = " "; // 8 spaces
+static const std::string Indent16 = " "; // 16 spaces
+
// Print out the first three columns of a line.
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
uint64_t Align) {
OS << format("%08llx %08llx %5lld ", Addr, Size, Align);
}
-static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
-
// Returns a list of all symbols that we want to print out.
static std::vector<DefinedRegular *> getSymbols() {
std::vector<DefinedRegular *> V;
@@ -78,7 +79,7 @@
for_each_n(parallel::par, (size_t)0, Syms.size(), [&](size_t I) {
raw_string_ostream OS(Str[I]);
writeHeader(OS, Syms[I]->getRVA(), 0, 0);
- OS << indent(2) << toString(*Syms[I]);
+ OS << Indent16 << toString(*Syms[I]);
});
DenseMap<DefinedRegular *, std::string> Ret;
@@ -107,7 +108,7 @@
// Print out file contents.
for (OutputSection *Sec : OutputSections) {
writeHeader(OS, Sec->getRVA(), Sec->getVirtualSize(), /*Align=*/PageSize);
- OS << Sec->getName() << '\n';
+ OS << Sec->Name << '\n';
for (Chunk *C : Sec->getChunks()) {
auto *SC = dyn_cast<SectionChunk>(C);
@@ -115,7 +116,7 @@
continue;
writeHeader(OS, SC->getRVA(), SC->getSize(), SC->Alignment);
- OS << indent(1) << SC->File->getName() << ":(" << SC->getSectionName()
+ OS << Indent8 << SC->File->getName() << ":(" << SC->getSectionName()
<< ")\n";
for (DefinedRegular *Sym : SectionSyms[SC])
OS << SymStr[Sym] << '\n';
diff --git a/COFF/Options.td b/COFF/Options.td
index d9531b5..243671d 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -45,6 +45,7 @@
def opt : P<"opt", "Control optimizations">;
def order : P<"order", "Put functions in order">;
def out : P<"out", "Path to file to write output">;
+def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
def pdb : P<"pdb", "PDB file path">;
def section : P<"section", "Specify section attributes">;
def stack : P<"stack", "Size of the stack">;
@@ -126,6 +127,7 @@
def debug_ghash : F<"debug:ghash">;
def debug_dwarf : F<"debug:dwarf">;
def export_all_symbols : F<"export-all-symbols">;
+def kill_at : F<"kill-at">;
def lldmingw : F<"lldmingw">;
def msvclto : F<"msvclto">;
def output_def : Joined<["/", "-"], "output-def:">;
@@ -161,7 +163,6 @@
def errorreport : QF<"errorreport">;
def idlout : QF<"idlout">;
def maxilksize : QF<"maxilksize">;
-def natvis : QF<"natvis">;
def pdbaltpath : QF<"pdbaltpath">;
def tlbid : QF<"tlbid">;
def tlbout : QF<"tlbout">;
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index de6aa64..91131c6 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -16,7 +16,6 @@
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Timer.h"
-#include "llvm/DebugInfo/CodeView/CVDebugRecord.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/GlobalTypeTableBuilder.h"
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
@@ -46,6 +45,7 @@
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
#include "llvm/DebugInfo/PDB/PDB.h"
#include "llvm/Object/COFF.h"
+#include "llvm/Object/CVDebugRecord.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FormatVariadic.h"
@@ -85,11 +85,19 @@
public:
PDBLinker(SymbolTable *Symtab)
: Alloc(), Symtab(Symtab), Builder(Alloc), TypeTable(Alloc),
- IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {}
+ IDTable(Alloc), GlobalTypeTable(Alloc), GlobalIDTable(Alloc) {
+ // This isn't strictly necessary, but link.exe usually puts an empty string
+ // as the first "valid" string in the string table, so we do the same in
+ // order to maintain as much byte-for-byte compatibility as possible.
+ PDBStrTab.insert("");
+ }
/// Emit the basic PDB structure: initial streams, headers, etc.
void initialize(const llvm::codeview::DebugInfo &BuildId);
+ /// Add natvis files specified on the command line.
+ void addNatvisFiles();
+
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();
@@ -961,6 +969,18 @@
}
}
+void PDBLinker::addNatvisFiles() {
+ for (StringRef File : Config->NatvisFiles) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> DataOrErr =
+ MemoryBuffer::getFile(File);
+ if (!DataOrErr) {
+ warn("Cannot open input file: " + File);
+ continue;
+ }
+ Builder.addInjectedSource(File, std::move(*DataOrErr));
+ }
+}
+
static void addCommonLinkerModuleSymbols(StringRef Path,
pdb::DbiModuleDescriptorBuilder &Mod,
BumpPtrAllocator &Allocator) {
@@ -1024,7 +1044,7 @@
Sym.Alignment = 12; // 2^12 = 4KB
Sym.Characteristics = OS.getCharacteristics();
Sym.Length = OS.getVirtualSize();
- Sym.Name = OS.getName();
+ Sym.Name = OS.Name;
Sym.Rva = OS.getRVA();
Sym.SectionNumber = OS.SectionIndex;
Mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
@@ -1038,9 +1058,11 @@
const llvm::codeview::DebugInfo &BuildId) {
ScopedTimer T1(TotalPdbLinkTimer);
PDBLinker PDB(Symtab);
+
PDB.initialize(BuildId);
PDB.addObjectsToPDB();
PDB.addSections(OutputSections, SectionTable);
+ PDB.addNatvisFiles();
ScopedTimer T2(DiskCommitTimer);
PDB.commit();
@@ -1066,7 +1088,6 @@
pdb::DbiStreamBuilder &DbiBuilder = Builder.getDbiBuilder();
DbiBuilder.setAge(BuildId.PDB70.Age);
DbiBuilder.setVersionHeader(pdb::PdbDbiV70);
- ExitOnErr(DbiBuilder.addDbgStream(pdb::DbgHeaderType::NewFPO, {}));
}
void PDBLinker::addSectionContrib(pdb::DbiModuleDescriptorBuilder &LinkerModule,
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index 73d5407..32420df 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -123,7 +123,7 @@
if (Config->WarnLocallyDefinedImported)
if (Symbol *Imp = LocalImports.lookup(B))
warn("<root>: locally defined symbol imported: " + Imp->getName() +
- " (defined in " + toString(Imp->getFile()) + ")");
+ " (defined in " + toString(Imp->getFile()) + ") [LNK4217]");
}
for (ObjFile *File : ObjFile::Instances) {
@@ -136,7 +136,7 @@
if (Symbol *Imp = LocalImports.lookup(Sym))
warn(toString(File) + ": locally defined symbol imported: " +
Imp->getName() + " (defined in " + toString(Imp->getFile()) +
- ")");
+ ") [LNK4217]");
}
}
}
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index bb621eb..6f173de 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -28,6 +28,7 @@
#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Support/xxhash.h"
#include <algorithm>
#include <cstdio>
#include <map>
@@ -42,8 +43,40 @@
using namespace lld;
using namespace lld::coff;
+/* To re-generate DOSProgram:
+$ cat > /tmp/DOSProgram.asm
+org 0
+ ; Copy cs to ds.
+ push cs
+ pop ds
+ ; Point ds:dx at the $-terminated string.
+ mov dx, str
+ ; Int 21/AH=09h: Write string to standard output.
+ mov ah, 0x9
+ int 0x21
+ ; Int 21/AH=4Ch: Exit with return code (in AL).
+ mov ax, 0x4C01
+ int 0x21
+str:
+ db 'This program cannot be run in DOS mode.$'
+align 8, db 0
+$ nasm -fbin /tmp/DOSProgram.asm -o /tmp/DOSProgram.bin
+$ xxd -i /tmp/DOSProgram.bin
+*/
+static unsigned char DOSProgram[] = {
+ 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c,
+ 0xcd, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65,
+ 0x20, 0x72, 0x75, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20,
+ 0x6d, 0x6f, 0x64, 0x65, 0x2e, 0x24, 0x00, 0x00
+};
+static_assert(sizeof(DOSProgram) % 8 == 0,
+ "DOSProgram size must be multiple of 8");
+
static const int SectorSize = 512;
-static const int DOSStubSize = 64;
+static const int DOSStubSize = sizeof(dos_header) + sizeof(DOSProgram);
+static_assert(DOSStubSize % 8 == 0, "DOSStub size must be multiple of 8");
+
static const int NumberfOfDataDirectory = 16;
namespace {
@@ -71,11 +104,18 @@
uint64_t Offs = OS->getFileOff() + (Record->getRVA() - OS->getRVA());
D->PointerToRawData = Offs;
+ TimeDateStamps.push_back(&D->TimeDateStamp);
++D;
}
}
+ void setTimeDateStamp(uint32_t TimeDateStamp) {
+ for (support::ulittle32_t *TDS : TimeDateStamps)
+ *TDS = TimeDateStamp;
+ }
+
private:
+ mutable std::vector<support::ulittle32_t *> TimeDateStamps;
const std::vector<Chunk *> &Records;
};
@@ -157,7 +197,7 @@
RVATableChunk *GuardFidsTable = nullptr;
RVATableChunk *SEHTable = nullptr;
- Chunk *DebugDirectory = nullptr;
+ DebugDirectoryChunk *DebugDirectory = nullptr;
std::vector<Chunk *> DebugRecords;
CVDebugRecordChunk *BuildId = nullptr;
Optional<codeview::DebugInfo> PreviousBuildId;
@@ -178,37 +218,9 @@
void writeResult() { Writer().run(); }
-void OutputSection::setRVA(uint64_t RVA) {
- Header.VirtualAddress = RVA;
- for (Chunk *C : Chunks)
- C->setRVA(C->getRVA() + RVA);
-}
-
-void OutputSection::setFileOffset(uint64_t Off) {
- // If a section has no actual data (i.e. BSS section), we want to
- // set 0 to its PointerToRawData. Otherwise the output is rejected
- // by the loader.
- if (Header.SizeOfRawData == 0)
- return;
-
- // It is possible that this assignment could cause an overflow of the u32,
- // but that should be caught by the FileSize check in OutputSection::run().
- Header.PointerToRawData = Off;
-}
-
void OutputSection::addChunk(Chunk *C) {
Chunks.push_back(C);
C->setOutputSection(this);
- uint64_t Off = Header.VirtualSize;
- Off = alignTo(Off, C->Alignment);
- C->setRVA(Off);
- C->OutputSectionOff = Off;
- Off += C->getSize();
- if (Off > UINT32_MAX)
- error("section larger than 4 GiB: " + Name);
- Header.VirtualSize = Off;
- if (C->hasData())
- Header.SizeOfRawData = alignTo(Off, SectorSize);
}
void OutputSection::addPermissions(uint32_t C) {
@@ -414,6 +426,9 @@
void Writer::createMiscChunks() {
OutputSection *RData = createSection(".rdata");
+ for (auto &P : MergeChunk::Instances)
+ RData->addChunk(P.second);
+
// Create thunks for locally-dllimported symbols.
if (!Symtab->LocalImportChunks.empty()) {
for (Chunk *C : Symtab->LocalImportChunks)
@@ -588,19 +603,20 @@
}
void Writer::createSymbolAndStringTable() {
- // Name field in the section table is 8 byte long. Longer names need
- // to be written to the string table. First, construct string table.
+ // PE/COFF images are limited to 8 byte section names. Longer names can be
+ // supported by writing a non-standard string table, but this string table is
+ // not mapped at runtime and the long names will therefore be inaccessible.
+ // link.exe always truncates section names to 8 bytes, whereas binutils always
+ // preserves long section names via the string table. LLD adopts a hybrid
+ // solution where discardable sections have long names preserved and
+ // non-discardable sections have their names truncated, to ensure that any
+ // section which is mapped at runtime also has its name mapped at runtime.
for (OutputSection *Sec : OutputSections) {
- StringRef Name = Sec->getName();
- if (Name.size() <= COFF::NameSize)
+ if (Sec->Name.size() <= COFF::NameSize)
continue;
- // If a section isn't discardable (i.e. will be mapped at runtime),
- // prefer a truncated section name over a long section name in
- // the string table that is unavailable at runtime. Note that link.exe
- // always truncates, even for discardable sections.
if ((Sec->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0)
continue;
- Sec->setStringTableOff(addEntryToStringTable(Name));
+ Sec->setStringTableOff(addEntryToStringTable(Sec->Name));
}
if (Config->DebugDwarf) {
@@ -646,26 +662,53 @@
return (S->getPermissions() & IMAGE_SCN_MEM_DISCARDABLE) == 0;
});
for (OutputSection *Sec : OutputSections) {
- if (Sec->getName() == ".reloc")
+ if (Sec->Name == ".reloc")
addBaserels(Sec);
- Sec->setRVA(RVA);
- Sec->setFileOffset(FileSize);
- RVA += alignTo(Sec->getVirtualSize(), PageSize);
- FileSize += alignTo(Sec->getRawSize(), SectorSize);
+ uint64_t RawSize = 0, VirtualSize = 0;
+ Sec->Header.VirtualAddress = RVA;
+ for (Chunk *C : Sec->getChunks()) {
+ VirtualSize = alignTo(VirtualSize, C->Alignment);
+ C->setRVA(RVA + VirtualSize);
+ C->OutputSectionOff = VirtualSize;
+ C->finalizeContents();
+ VirtualSize += C->getSize();
+ if (C->hasData())
+ RawSize = alignTo(VirtualSize, SectorSize);
+ }
+ if (VirtualSize > UINT32_MAX)
+ error("section larger than 4 GiB: " + Sec->Name);
+ Sec->Header.VirtualSize = VirtualSize;
+ Sec->Header.SizeOfRawData = RawSize;
+ if (RawSize != 0)
+ Sec->Header.PointerToRawData = FileSize;
+ RVA += alignTo(VirtualSize, PageSize);
+ FileSize += alignTo(RawSize, SectorSize);
}
SizeOfImage = alignTo(RVA, PageSize);
}
template <typename PEHeaderTy> void Writer::writeHeader() {
- // Write DOS stub
+ // Write DOS header. For backwards compatibility, the first part of a PE/COFF
+ // executable consists of an MS-DOS MZ executable. If the executable is run
+ // under DOS, that program gets run (usually to just print an error message).
+ // When run under Windows, the loader looks at AddressOfNewExeHeader and uses
+ // the PE header instead.
uint8_t *Buf = Buffer->getBufferStart();
auto *DOS = reinterpret_cast<dos_header *>(Buf);
- Buf += DOSStubSize;
+ Buf += sizeof(dos_header);
DOS->Magic[0] = 'M';
DOS->Magic[1] = 'Z';
+ DOS->UsedBytesInTheLastPage = DOSStubSize % 512;
+ DOS->FileSizeInPages = divideCeil(DOSStubSize, 512);
+ DOS->HeaderSizeInParagraphs = sizeof(dos_header) / 16;
+
DOS->AddressOfRelocationTable = sizeof(dos_header);
DOS->AddressOfNewExeHeader = DOSStubSize;
+ // Write DOS program.
+ memcpy(Buf, DOSProgram, sizeof(DOSProgram));
+ Buf += sizeof(DOSProgram);
+
// Write PE magic
memcpy(Buf, PEMagic, sizeof(PEMagic));
Buf += sizeof(PEMagic);
@@ -1026,22 +1069,50 @@
}
void Writer::writeBuildId() {
- // If we're not writing a build id (e.g. because /debug is not specified),
- // then just return;
- if (!Config->Debug)
- return;
-
- assert(BuildId && "BuildId is not set!");
-
- if (PreviousBuildId.hasValue()) {
- *BuildId->BuildId = *PreviousBuildId;
- BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
- return;
+ // There are two important parts to the build ID.
+ // 1) If building with debug info, the COFF debug directory contains a
+ // timestamp as well as a Guid and Age of the PDB.
+ // 2) In all cases, the PE COFF file header also contains a timestamp.
+ // For reproducibility, instead of a timestamp we want to use a hash of the
+ // binary, however when building with debug info the hash needs to take into
+ // account the debug info, since it's possible to add blank lines to a file
+ // which causes the debug info to change but not the generated code.
+ //
+ // To handle this, we first set the Guid and Age in the debug directory (but
+ // only if we're doing a debug build). Then, we hash the binary (thus causing
+ // the hash to change if only the debug info changes, since the Age will be
+ // different). Finally, we write that hash into the debug directory (if
+ // present) as well as the COFF file header (always).
+ if (Config->Debug) {
+ assert(BuildId && "BuildId is not set!");
+ if (PreviousBuildId.hasValue()) {
+ *BuildId->BuildId = *PreviousBuildId;
+ BuildId->BuildId->PDB70.Age = BuildId->BuildId->PDB70.Age + 1;
+ } else {
+ BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
+ BuildId->BuildId->PDB70.Age = 1;
+ llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
+ }
}
- BuildId->BuildId->Signature.CVSignature = OMF::Signature::PDB70;
- BuildId->BuildId->PDB70.Age = 1;
- llvm::getRandomBytes(BuildId->BuildId->PDB70.Signature, 16);
+ // At this point the only fields in the COFF file which remain unset are the
+ // "timestamp" in the COFF file header, and the ones in the coff debug
+ // directory. Now we can hash the file and write that hash to the various
+ // timestamp fields in the file.
+ StringRef OutputFileData(
+ reinterpret_cast<const char *>(Buffer->getBufferStart()),
+ Buffer->getBufferSize());
+
+ uint32_t Hash = static_cast<uint32_t>(xxHash64(OutputFileData));
+
+ if (DebugDirectory)
+ DebugDirectory->setTimeDateStamp(Hash);
+
+ uint8_t *Buf = Buffer->getBufferStart();
+ Buf += DOSStubSize + sizeof(PEMagic);
+ object::coff_file_header *CoffHeader =
+ reinterpret_cast<coff_file_header *>(Buf);
+ CoffHeader->TimeDateStamp = Hash;
}
// Sort .pdata section contents according to PE/COFF spec 5.5.
@@ -1069,7 +1140,7 @@
OutputSection *Writer::findSection(StringRef Name) {
for (OutputSection *Sec : OutputSections)
- if (Sec->getName() == Name)
+ if (Sec->Name == Name)
return Sec;
return nullptr;
}
diff --git a/COFF/Writer.h b/COFF/Writer.h
index c8e8dc8..1dc1e61 100644
--- a/COFF/Writer.h
+++ b/COFF/Writer.h
@@ -31,10 +31,7 @@
class OutputSection {
public:
OutputSection(llvm::StringRef N) : Name(N), Header({}) {}
- void setRVA(uint64_t);
- void setFileOffset(uint64_t);
void addChunk(Chunk *C);
- llvm::StringRef getName() { return Name; }
ArrayRef<Chunk *> getChunks() { return Chunks; }
void addPermissions(uint32_t C);
void setPermissions(uint32_t C);
@@ -61,9 +58,10 @@
// N.B. The section index is one based.
uint32_t SectionIndex = 0;
-private:
llvm::StringRef Name;
llvm::object::coff_section Header;
+
+private:
uint32_t StringTableOff = 0;
std::vector<Chunk *> Chunks;
};
diff --git a/ELF/AArch64ErrataFix.cpp b/ELF/AArch64ErrataFix.cpp
index 2924b19..7551919 100644
--- a/ELF/AArch64ErrataFix.cpp
+++ b/ELF/AArch64ErrataFix.cpp
@@ -341,7 +341,7 @@
// patch or 0 if no patch required.
static uint64_t scanCortexA53Errata843419(InputSection *IS, uint64_t &Off,
uint64_t Limit) {
- uint64_t ISAddr = IS->getParent()->Addr + IS->OutSecOff;
+ uint64_t ISAddr = IS->getVA(0);
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
uint64_t InitialPageOff = (ISAddr + Off) & 0xfff;
@@ -405,7 +405,7 @@
}
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
- return Patchee->getParent()->Addr + Patchee->OutSecOff + PatcheeOffset;
+ return Patchee->getVA(PatcheeOffset);
}
void lld::elf::Patch843419Section::writeTo(uint8_t *Buf) {
@@ -601,7 +601,7 @@
(DataSym == MapSyms.end()) ? IS->Data.size() : (*DataSym)->Value;
while (Off < Limit) {
- uint64_t StartAddr = IS->getParent()->Addr + IS->OutSecOff + Off;
+ uint64_t StartAddr = IS->getVA(Off);
if (uint64_t PatcheeOffset = scanCortexA53Errata843419(IS, Off, Limit))
implementPatch(StartAddr, PatcheeOffset, IS, Patches);
}
diff --git a/ELF/Arch/AArch64.cpp b/ELF/Arch/AArch64.cpp
index 99e9879..2307ed1 100644
--- a/ELF/Arch/AArch64.cpp
+++ b/ELF/Arch/AArch64.cpp
@@ -240,12 +240,12 @@
switch (Type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
- checkIntUInt<16>(Loc, Val, Type);
+ checkIntUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_AARCH64_ABS32:
case R_AARCH64_PREL32:
- checkIntUInt<32>(Loc, Val, Type);
+ checkIntUInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
case R_AARCH64_ABS64:
@@ -260,11 +260,11 @@
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSDESC_ADR_PAGE21:
- checkInt<33>(Loc, Val, Type);
+ checkInt(Loc, Val, 33, Type);
write32AArch64Addr(Loc, Val >> 12);
break;
case R_AARCH64_ADR_PREL_LO21:
- checkInt<21>(Loc, Val, Type);
+ checkInt(Loc, Val, 21, Type);
write32AArch64Addr(Loc, Val);
break;
case R_AARCH64_JUMP26:
@@ -278,38 +278,38 @@
write32le(Loc, 0x14000000);
LLVM_FALLTHROUGH;
case R_AARCH64_CALL26:
- checkInt<28>(Loc, Val, Type);
+ checkInt(Loc, Val, 28, Type);
or32le(Loc, (Val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
- checkAlignment<4>(Loc, Val, Type);
- checkInt<21>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
+ checkInt(Loc, Val, 21, Type);
or32le(Loc, (Val & 0x1FFFFC) << 3);
break;
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case R_AARCH64_TLSDESC_LD64_LO12:
- checkAlignment<8>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 8, Type);
or32le(Loc, (Val & 0xFF8) << 7);
break;
case R_AARCH64_LDST8_ABS_LO12_NC:
or32AArch64Imm(Loc, getBits(Val, 0, 11));
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
- checkAlignment<2>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 2, Type);
or32AArch64Imm(Loc, getBits(Val, 1, 11));
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
- checkAlignment<4>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
or32AArch64Imm(Loc, getBits(Val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
- checkAlignment<8>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 8, Type);
or32AArch64Imm(Loc, getBits(Val, 3, 11));
break;
case R_AARCH64_LDST128_ABS_LO12_NC:
- checkAlignment<16>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 16, Type);
or32AArch64Imm(Loc, getBits(Val, 4, 11));
break;
case R_AARCH64_MOVW_UABS_G0_NC:
@@ -325,11 +325,11 @@
or32le(Loc, (Val & 0xFFFF000000000000) >> 43);
break;
case R_AARCH64_TSTBR14:
- checkInt<16>(Loc, Val, Type);
+ checkInt(Loc, Val, 16, Type);
or32le(Loc, (Val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- checkInt<24>(Loc, Val, Type);
+ checkInt(Loc, Val, 24, Type);
or32AArch64Imm(Loc, Val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
@@ -353,7 +353,7 @@
// movk x0, #0x10
// nop
// nop
- checkUInt<32>(Loc, Val, Type);
+ checkUInt(Loc, Val, 32, Type);
switch (Type) {
case R_AARCH64_TLSDESC_ADD_LO12:
@@ -403,7 +403,7 @@
}
void AArch64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
- checkUInt<32>(Loc, Val, Type);
+ checkUInt(Loc, Val, 32, Type);
if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
// Generate MOVZ.
diff --git a/ELF/Arch/ARM.cpp b/ELF/Arch/ARM.cpp
index b9f551e..9927430 100644
--- a/ELF/Arch/ARM.cpp
+++ b/ELF/Arch/ARM.cpp
@@ -55,6 +55,7 @@
TlsGotRel = R_ARM_TLS_TPOFF32;
TlsModuleIndexRel = R_ARM_TLS_DTPMOD32;
TlsOffsetRel = R_ARM_TLS_DTPOFF32;
+ GotBaseSymInGotPlt = false;
GotEntrySize = 4;
GotPltEntrySize = 4;
PltEntrySize = 16;
@@ -392,7 +393,7 @@
write32le(Loc, 1);
break;
case R_ARM_PREL31:
- checkInt<31>(Loc, Val, Type);
+ checkInt(Loc, Val, 31, Type);
write32le(Loc, (read32le(Loc) & 0x80000000) | (Val & ~0x80000000));
break;
case R_ARM_CALL:
@@ -401,7 +402,7 @@
if (Val & 1) {
// If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
- checkInt<26>(Loc, Val, Type);
+ checkInt(Loc, Val, 26, Type);
write32le(Loc, 0xfa000000 | // opcode
((Val & 2) << 23) | // H
((Val >> 2) & 0x00ffffff)); // imm24
@@ -416,16 +417,16 @@
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
- checkInt<26>(Loc, Val, Type);
+ checkInt(Loc, Val, 26, Type);
write32le(Loc, (read32le(Loc) & ~0x00ffffff) | ((Val >> 2) & 0x00ffffff));
break;
case R_ARM_THM_JUMP11:
- checkInt<12>(Loc, Val, Type);
+ checkInt(Loc, Val, 12, Type);
write16le(Loc, (read32le(Loc) & 0xf800) | ((Val >> 1) & 0x07ff));
break;
case R_ARM_THM_JUMP19:
// Encoding T3: Val = S:J2:J1:imm6:imm11:0
- checkInt<21>(Loc, Val, Type);
+ checkInt(Loc, Val, 21, Type);
write16le(Loc,
(read16le(Loc) & 0xfbc0) | // opcode cond
((Val >> 10) & 0x0400) | // S
@@ -451,7 +452,7 @@
case R_ARM_THM_JUMP24:
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
// FIXME: Use of I1 and I2 require v6T2ops
- checkInt<25>(Loc, Val, Type);
+ checkInt(Loc, Val, 25, Type);
write16le(Loc,
0xf000 | // opcode
((Val >> 14) & 0x0400) | // S
@@ -469,14 +470,14 @@
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32le(Loc, (read32le(Loc) & ~0x000f0fff) |
(((Val >> 16) & 0xf000) << 4) | ((Val >> 16) & 0xfff));
break;
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVT_PREL:
// Encoding T1: A = imm4:i:imm3:imm8
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write16le(Loc,
0xf2c0 | // opcode
((Val >> 17) & 0x0400) | // i
diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp
index fcf5aff..a8e54a8 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -50,6 +50,7 @@
DefaultMaxPageSize = 65536;
GotEntrySize = sizeof(typename ELFT::uint);
GotPltEntrySize = sizeof(typename ELFT::uint);
+ GotBaseSymInGotPlt = false;
PltEntrySize = 16;
PltHeaderSize = 32;
CopyRel = R_MIPS_COPY;
@@ -510,7 +511,7 @@
if (Config->Relocatable) {
writeValue<E>(Loc, Val + 0x8000, 16, 16);
} else {
- checkInt<16>(Loc, Val, Type);
+ checkInt(Loc, Val, 16, Type);
writeValue<E>(Loc, Val, 16, 0);
}
break;
@@ -518,7 +519,7 @@
if (Config->Relocatable) {
writeShuffleValue<E>(Loc, Val + 0x8000, 16, 16);
} else {
- checkInt<16>(Loc, Val, Type);
+ checkInt(Loc, Val, 16, Type);
writeShuffleValue<E>(Loc, Val, 16, 0);
}
break;
@@ -529,7 +530,7 @@
case R_MIPS_TLS_GD:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
- checkInt<16>(Loc, Val, Type);
+ checkInt(Loc, Val, 16, Type);
LLVM_FALLTHROUGH;
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
@@ -545,7 +546,7 @@
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_LDM:
- checkInt<16>(Loc, Val, Type);
+ checkInt(Loc, Val, 16, Type);
writeShuffleValue<E>(Loc, Val, 16, 0);
break;
case R_MICROMIPS_CALL16:
@@ -558,7 +559,7 @@
writeShuffleValue<E>(Loc, Val, 16, 0);
break;
case R_MICROMIPS_GPREL7_S2:
- checkInt<7>(Loc, Val, Type);
+ checkInt(Loc, Val, 7, Type);
writeShuffleValue<E>(Loc, Val, 7, 2);
break;
case R_MIPS_CALL_HI16:
@@ -593,23 +594,23 @@
// Ignore this optimization relocation for now
break;
case R_MIPS_PC16:
- checkAlignment<4>(Loc, Val, Type);
- checkInt<18>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
+ checkInt(Loc, Val, 18, Type);
writeValue<E>(Loc, Val, 16, 2);
break;
case R_MIPS_PC19_S2:
- checkAlignment<4>(Loc, Val, Type);
- checkInt<21>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
+ checkInt(Loc, Val, 21, Type);
writeValue<E>(Loc, Val, 19, 2);
break;
case R_MIPS_PC21_S2:
- checkAlignment<4>(Loc, Val, Type);
- checkInt<23>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
+ checkInt(Loc, Val, 23, Type);
writeValue<E>(Loc, Val, 21, 2);
break;
case R_MIPS_PC26_S2:
- checkAlignment<4>(Loc, Val, Type);
- checkInt<28>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
+ checkInt(Loc, Val, 28, Type);
writeValue<E>(Loc, Val, 26, 2);
break;
case R_MIPS_PC32:
@@ -617,35 +618,35 @@
break;
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC26_S1:
- checkInt<27>(Loc, Val, Type);
+ checkInt(Loc, Val, 27, Type);
writeShuffleValue<E>(Loc, Val, 26, 1);
break;
case R_MICROMIPS_PC7_S1:
- checkInt<8>(Loc, Val, Type);
+ checkInt(Loc, Val, 8, Type);
writeMicroRelocation16<E>(Loc, Val, 7, 1);
break;
case R_MICROMIPS_PC10_S1:
- checkInt<11>(Loc, Val, Type);
+ checkInt(Loc, Val, 11, Type);
writeMicroRelocation16<E>(Loc, Val, 10, 1);
break;
case R_MICROMIPS_PC16_S1:
- checkInt<17>(Loc, Val, Type);
+ checkInt(Loc, Val, 17, Type);
writeShuffleValue<E>(Loc, Val, 16, 1);
break;
case R_MICROMIPS_PC18_S3:
- checkInt<21>(Loc, Val, Type);
+ checkInt(Loc, Val, 21, Type);
writeShuffleValue<E>(Loc, Val, 18, 3);
break;
case R_MICROMIPS_PC19_S2:
- checkInt<21>(Loc, Val, Type);
+ checkInt(Loc, Val, 21, Type);
writeShuffleValue<E>(Loc, Val, 19, 2);
break;
case R_MICROMIPS_PC21_S1:
- checkInt<22>(Loc, Val, Type);
+ checkInt(Loc, Val, 22, Type);
writeShuffleValue<E>(Loc, Val, 21, 1);
break;
case R_MICROMIPS_PC23_S2:
- checkInt<25>(Loc, Val, Type);
+ checkInt(Loc, Val, 25, Type);
writeShuffleValue<E>(Loc, Val, 23, 2);
break;
default:
diff --git a/ELF/Arch/PPC.cpp b/ELF/Arch/PPC.cpp
index 6af0df3..20cae0e 100644
--- a/ELF/Arch/PPC.cpp
+++ b/ELF/Arch/PPC.cpp
@@ -21,13 +21,18 @@
namespace {
class PPC final : public TargetInfo {
public:
- PPC() { GotBaseSymOff = 0x8000; }
+ PPC();
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
};
} // namespace
+PPC::PPC() {
+ GotBaseSymOff = 0x8000;
+ GotBaseSymInGotPlt = false;
+}
+
RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
diff --git a/ELF/Arch/PPC64.cpp b/ELF/Arch/PPC64.cpp
index ac4021b..d3c2dbd 100644
--- a/ELF/Arch/PPC64.cpp
+++ b/ELF/Arch/PPC64.cpp
@@ -14,6 +14,7 @@
#include "llvm/Support/Endian.h"
using namespace llvm;
+using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
@@ -39,11 +40,13 @@
class PPC64 final : public TargetInfo {
public:
PPC64();
+ uint32_t calcEFlags() const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
int32_t Index, unsigned RelOff) const override;
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
+ void writeGotHeader(uint8_t *Buf) const override;
};
} // namespace
@@ -66,6 +69,10 @@
GotPltEntrySize = 8;
PltEntrySize = 32;
PltHeaderSize = 0;
+ GotBaseSymInGotPlt = false;
+ GotBaseSymOff = 0x8000;
+ if (Config->EKind == ELF64LEKind)
+ GotHeaderEntriesNum = 1;
// We need 64K pages (at least under glibc/Linux, the loader won't
// set different permissions on a finer granularity than that).
@@ -82,6 +89,42 @@
DefaultImageBase = 0x10000000;
}
+static uint32_t getEFlags(InputFile *File) {
+ // Get the e_flag from the input file and if it is unspecified, then set it to
+ // the e_flag appropriate for the ABI.
+
+ // We are currently handling both ELF64LE and ELF64BE but eventually will
+ // remove BE support once v2 ABI support is complete.
+ switch (Config->EKind) {
+ case ELF64BEKind:
+ if (uint32_t EFlags =
+ cast<ObjFile<ELF64BE>>(File)->getObj().getHeader()->e_flags)
+ return EFlags;
+ return 1;
+ case ELF64LEKind:
+ if (uint32_t EFlags =
+ cast<ObjFile<ELF64LE>>(File)->getObj().getHeader()->e_flags)
+ return EFlags;
+ return 2;
+ default:
+ llvm_unreachable("unknown Config->EKind");
+ }
+}
+
+uint32_t PPC64::calcEFlags() const {
+ assert(!ObjectFiles.empty());
+ uint32_t Ret = getEFlags(ObjectFiles[0]);
+
+ // Verify that all input files have the same e_flags.
+ for (InputFile *F : makeArrayRef(ObjectFiles).slice(1)) {
+ if (Ret == getEFlags(F))
+ continue;
+ error("incompatible e_flags: " + toString(F));
+ return 0;
+ }
+ return Ret;
+}
+
RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const {
switch (Type) {
@@ -96,11 +139,19 @@
return R_PPC_TOC;
case R_PPC64_REL24:
return R_PPC_PLT_OPD;
+ case R_PPC64_REL16_LO:
+ case R_PPC64_REL16_HA:
+ return R_PC;
default:
return R_ABS;
}
}
+void PPC64::writeGotHeader(uint8_t *Buf) const {
+ if (Config->EKind == ELF64LEKind)
+ write64(Buf, getPPC64TocBase());
+}
+
void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
@@ -112,14 +163,14 @@
// be a pointer to the function descriptor in the .opd section. Using
// this scheme is simpler, but requires an extra indirection per PLT dispatch.
- write32be(Buf, 0xf8410028); // std %r2, 40(%r1)
- write32be(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha
- write32be(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11)
- write32be(Buf + 12, 0xe96c0000); // ld %r11,0(%r12)
- write32be(Buf + 16, 0x7d6903a6); // mtctr %r11
- write32be(Buf + 20, 0xe84c0008); // ld %r2,8(%r12)
- write32be(Buf + 24, 0xe96c0010); // ld %r11,16(%r12)
- write32be(Buf + 28, 0x4e800420); // bctr
+ write32(Buf, 0xf8410028); // std %r2, 40(%r1)
+ write32(Buf + 4, 0x3d620000 | applyPPCHa(Off)); // addis %r11, %r2, X@ha
+ write32(Buf + 8, 0xe98b0000 | applyPPCLo(Off)); // ld %r12, X@l(%r11)
+ write32(Buf + 12, 0xe96c0000); // ld %r11,0(%r12)
+ write32(Buf + 16, 0x7d6903a6); // mtctr %r11
+ write32(Buf + 20, 0xe84c0008); // ld %r2,8(%r12)
+ write32(Buf + 24, 0xe96c0010); // ld %r11,16(%r12)
+ write32(Buf + 28, 0x4e800420); // bctr
}
static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
@@ -149,61 +200,61 @@
switch (Type) {
case R_PPC64_ADDR14: {
- checkAlignment<4>(Loc, Val, Type);
+ checkAlignment(Loc, Val, 4, Type);
// Preserve the AA/LK bits in the branch instruction
uint8_t AALK = Loc[3];
- write16be(Loc + 2, (AALK & 3) | (Val & 0xfffc));
+ write16(Loc + 2, (AALK & 3) | (Val & 0xfffc));
break;
}
case R_PPC64_ADDR16:
- checkInt<16>(Loc, Val, Type);
- write16be(Loc, Val);
+ checkInt(Loc, Val, 16, Type);
+ write16(Loc, Val);
break;
case R_PPC64_ADDR16_DS:
- checkInt<16>(Loc, Val, Type);
- write16be(Loc, (read16be(Loc) & 3) | (Val & ~3));
+ checkInt(Loc, Val, 16, Type);
+ write16(Loc, (read16(Loc) & 3) | (Val & ~3));
break;
case R_PPC64_ADDR16_HA:
case R_PPC64_REL16_HA:
- write16be(Loc, applyPPCHa(Val));
+ write16(Loc, applyPPCHa(Val));
break;
case R_PPC64_ADDR16_HI:
case R_PPC64_REL16_HI:
- write16be(Loc, applyPPCHi(Val));
+ write16(Loc, applyPPCHi(Val));
break;
case R_PPC64_ADDR16_HIGHER:
- write16be(Loc, applyPPCHigher(Val));
+ write16(Loc, applyPPCHigher(Val));
break;
case R_PPC64_ADDR16_HIGHERA:
- write16be(Loc, applyPPCHighera(Val));
+ write16(Loc, applyPPCHighera(Val));
break;
case R_PPC64_ADDR16_HIGHEST:
- write16be(Loc, applyPPCHighest(Val));
+ write16(Loc, applyPPCHighest(Val));
break;
case R_PPC64_ADDR16_HIGHESTA:
- write16be(Loc, applyPPCHighesta(Val));
+ write16(Loc, applyPPCHighesta(Val));
break;
case R_PPC64_ADDR16_LO:
- write16be(Loc, applyPPCLo(Val));
+ case R_PPC64_REL16_LO:
+ write16(Loc, applyPPCLo(Val));
break;
case R_PPC64_ADDR16_LO_DS:
- case R_PPC64_REL16_LO:
- write16be(Loc, (read16be(Loc) & 3) | (applyPPCLo(Val) & ~3));
+ write16(Loc, (read16(Loc) & 3) | (applyPPCLo(Val) & ~3));
break;
case R_PPC64_ADDR32:
case R_PPC64_REL32:
- checkInt<32>(Loc, Val, Type);
- write32be(Loc, Val);
+ checkInt(Loc, Val, 32, Type);
+ write32(Loc, Val);
break;
case R_PPC64_ADDR64:
case R_PPC64_REL64:
case R_PPC64_TOC:
- write64be(Loc, Val);
+ write64(Loc, Val);
break;
case R_PPC64_REL24: {
uint32_t Mask = 0x03FFFFFC;
- checkInt<24>(Loc, Val, Type);
- write32be(Loc, (read32be(Loc) & ~Mask) | (Val & Mask));
+ checkInt(Loc, Val, 24, Type);
+ write32(Loc, (read32(Loc) & ~Mask) | (Val & Mask));
break;
}
default:
diff --git a/ELF/Arch/SPARCV9.cpp b/ELF/Arch/SPARCV9.cpp
index d9d6e13..36f5c83 100644
--- a/ELF/Arch/SPARCV9.cpp
+++ b/ELF/Arch/SPARCV9.cpp
@@ -77,23 +77,23 @@
case R_SPARC_32:
case R_SPARC_UA32:
// V-word32
- checkUInt<32>(Loc, Val, Type);
+ checkUInt(Loc, Val, 32, Type);
write32be(Loc, Val);
break;
case R_SPARC_DISP32:
// V-disp32
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32be(Loc, Val);
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
// V-disp30
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32be(Loc, (read32be(Loc) & ~0x3fffffff) | ((Val >> 2) & 0x3fffffff));
break;
case R_SPARC_22:
// V-imm22
- checkUInt<22>(Loc, Val, Type);
+ checkUInt(Loc, Val, 22, Type);
write32be(Loc, (read32be(Loc) & ~0x003fffff) | (Val & 0x003fffff));
break;
case R_SPARC_GOT22:
@@ -103,7 +103,7 @@
break;
case R_SPARC_WDISP19:
// V-disp19
- checkInt<21>(Loc, Val, Type);
+ checkInt(Loc, Val, 21, Type);
write32be(Loc, (read32be(Loc) & ~0x0007ffff) | ((Val >> 2) & 0x0007ffff));
break;
case R_SPARC_GOT10:
@@ -137,7 +137,7 @@
};
memcpy(Buf, PltData, sizeof(PltData));
- uint64_t Off = PltHeaderSize + Index * PltEntrySize;
+ uint64_t Off = getPltEntryOffset(Index);
relocateOne(Buf, R_SPARC_22, Off);
relocateOne(Buf + 4, R_SPARC_WDISP19, -(Off + 4 - PltEntrySize));
}
diff --git a/ELF/Arch/X86.cpp b/ELF/Arch/X86.cpp
index 09c16db..87c84ad 100644
--- a/ELF/Arch/X86.cpp
+++ b/ELF/Arch/X86.cpp
@@ -46,7 +46,6 @@
} // namespace
X86::X86() {
- GotBaseSymOff = -1;
CopyRel = R_386_COPY;
GotRel = R_386_GLOB_DAT;
PltRel = R_386_JUMP_SLOT;
@@ -224,7 +223,7 @@
}
write32le(Buf + 7, RelOff);
- write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
+ write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
}
int64_t X86::getImplicitAddend(const uint8_t *Buf, RelType Type) const {
@@ -256,15 +255,15 @@
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
// being used for some 16-bit programs such as boot loaders, so
// we want to support them.
- checkUInt<8>(Loc, Val, Type);
+ checkUInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_386_PC8:
- checkInt<8>(Loc, Val, Type);
+ checkInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_386_16:
- checkUInt<16>(Loc, Val, Type);
+ checkUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_386_PC16:
@@ -278,7 +277,7 @@
// current location subtracted from it.
// We just check that Val fits in 17 bits. This misses some cases, but
// should have no false positives.
- checkInt<17>(Loc, Val, Type);
+ checkInt(Loc, Val, 17, Type);
write16le(Loc, Val);
break;
case R_386_32:
@@ -301,7 +300,7 @@
case R_386_TLS_LE_32:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
default:
@@ -444,6 +443,7 @@
0x89, 0xc8, // 2b: mov %ecx, %eax
0x59, // 2d: pop %ecx
0xc3, // 2e: ret
+ 0xcc, // 2f: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
@@ -457,21 +457,23 @@
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const uint8_t Insn[] = {
- 0x50, // pushl %eax
- 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax
- 0xe8, 0, 0, 0, 0, // call plt+0x20
- 0xe9, 0, 0, 0, 0, // jmp plt+0x12
- 0x68, 0, 0, 0, 0, // pushl $reloc_offset
- 0xe9, 0, 0, 0, 0, // jmp plt+0
+ 0x50, // pushl %eax
+ 0x8b, 0x83, 0, 0, 0, 0, // mov foo@GOT(%ebx), %eax
+ 0xe8, 0, 0, 0, 0, // call plt+0x20
+ 0xe9, 0, 0, 0, 0, // jmp plt+0x12
+ 0x68, 0, 0, 0, 0, // pushl $reloc_offset
+ 0xe9, 0, 0, 0, 0, // jmp plt+0
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
uint32_t Ebx = InX::Got->getVA() + InX::Got->getSize();
+ unsigned Off = getPltEntryOffset(Index);
write32le(Buf + 3, GotPltEntryAddr - Ebx);
- write32le(Buf + 8, -Index * PltEntrySize - PltHeaderSize - 12 + 32);
- write32le(Buf + 13, -Index * PltEntrySize - PltHeaderSize - 17 + 18);
+ write32le(Buf + 8, -Off - 12 + 32);
+ write32le(Buf + 13, -Off - 17 + 18);
write32le(Buf + 18, RelOff);
- write32le(Buf + 23, -Index * PltEntrySize - PltHeaderSize - 27);
+ write32le(Buf + 23, -Off - 27);
}
RetpolineNoPic::RetpolineNoPic() {
@@ -484,7 +486,7 @@
}
void RetpolineNoPic::writePltHeader(uint8_t *Buf) const {
- const uint8_t PltData[] = {
+ const uint8_t Insn[] = {
0xff, 0x35, 0, 0, 0, 0, // 0: pushl GOTPLT+4
0x50, // 6: pushl %eax
0xa1, 0, 0, 0, 0, // 7: mov GOTPLT+8, %eax
@@ -500,8 +502,9 @@
0x89, 0xc8, // 2b: mov %ecx, %eax
0x59, // 2d: pop %ecx
0xc3, // 2e: ret
+ 0xcc, // 2f: int3; padding
};
- memcpy(Buf, PltData, sizeof(PltData));
+ memcpy(Buf, Insn, sizeof(Insn));
uint32_t GotPlt = InX::GotPlt->getVA();
write32le(Buf + 2, GotPlt + 4);
@@ -512,20 +515,23 @@
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const uint8_t Insn[] = {
- 0x50, // 0: pushl %eax
- 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax
- 0xe8, 0, 0, 0, 0, // 6: call plt+0x20
- 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11
- 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset
- 0xe9, 0, 0, 0, 0, // 15: jmp plt+0
+ 0x50, // 0: pushl %eax
+ 0xa1, 0, 0, 0, 0, // 1: mov foo_in_GOT, %eax
+ 0xe8, 0, 0, 0, 0, // 6: call plt+0x20
+ 0xe9, 0, 0, 0, 0, // b: jmp plt+0x11
+ 0x68, 0, 0, 0, 0, // 10: pushl $reloc_offset
+ 0xe9, 0, 0, 0, 0, // 15: jmp plt+0
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
+ 0xcc, // 1f: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
+ unsigned Off = getPltEntryOffset(Index);
write32le(Buf + 2, GotPltEntryAddr);
- write32le(Buf + 7, -Index * PltEntrySize - PltHeaderSize - 11 + 32);
- write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16 + 17);
+ write32le(Buf + 7, -Off - 11 + 32);
+ write32le(Buf + 12, -Off - 16 + 17);
write32le(Buf + 17, RelOff);
- write32le(Buf + 22, -Index * PltEntrySize - PltHeaderSize - 26);
+ write32le(Buf + 22, -Off - 26);
}
TargetInfo *elf::getX86TargetInfo() {
diff --git a/ELF/Arch/X86_64.cpp b/ELF/Arch/X86_64.cpp
index 3db391e..a370508 100644
--- a/ELF/Arch/X86_64.cpp
+++ b/ELF/Arch/X86_64.cpp
@@ -51,7 +51,6 @@
} // namespace
template <class ELFT> X86_64<ELFT>::X86_64() {
- GotBaseSymOff = -1;
CopyRel = R_X86_64_COPY;
GotRel = R_X86_64_GLOB_DAT;
PltRel = R_X86_64_JUMP_SLOT;
@@ -153,7 +152,7 @@
write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6);
write32le(Buf + 7, Index);
- write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
+ write32le(Buf + 12, -getPltEntryOffset(Index) - 16);
}
template <class ELFT> bool X86_64<ELFT>::isPicRel(RelType Type) const {
@@ -285,15 +284,15 @@
void X86_64<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
switch (Type) {
case R_X86_64_8:
- checkUInt<8>(Loc, Val, Type);
+ checkUInt(Loc, Val, 8, Type);
*Loc = Val;
break;
case R_X86_64_16:
- checkUInt<16>(Loc, Val, Type);
+ checkUInt(Loc, Val, 16, Type);
write16le(Loc, Val);
break;
case R_X86_64_32:
- checkUInt<32>(Loc, Val, Type);
+ checkUInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
case R_X86_64_32S:
@@ -309,7 +308,7 @@
case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_SIZE32:
- checkInt<32>(Loc, Val, Type);
+ checkInt(Loc, Val, 32, Type);
write32le(Loc, Val);
break;
case R_X86_64_64:
@@ -501,6 +500,8 @@
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 19: int3; .align 16
0x4c, 0x89, 0x1c, 0x24, // 20: next: mov %r11, (%rsp)
0xc3, // 24: ret
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 25: int3; padding
+ 0xcc, 0xcc, 0xcc, 0xcc, // 2c: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
@@ -516,14 +517,15 @@
unsigned RelOff) const {
const uint8_t Insn[] = {
0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // 0: mov foo@GOTPLT(%rip), %r11
- 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20
- 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12
- 0x68, 0, 0, 0, 0, // 11: pushq <relocation index>
- 0xe9, 0, 0, 0, 0, // 16: jmp plt+0
+ 0xe8, 0, 0, 0, 0, // 7: callq plt+0x20
+ 0xe9, 0, 0, 0, 0, // c: jmp plt+0x12
+ 0x68, 0, 0, 0, 0, // 11: pushq <relocation index>
+ 0xe9, 0, 0, 0, 0, // 16: jmp plt+0
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1b: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
- uint64_t Off = TargetInfo::PltHeaderSize + TargetInfo::PltEntrySize * Index;
+ uint64_t Off = TargetInfo::getPltEntryOffset(Index);
write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
write32le(Buf + 8, -Off - 12 + 32);
@@ -547,6 +549,9 @@
0xcc, 0xcc, 0xcc, 0xcc, // c: int3; .align 16
0x4c, 0x89, 0x1c, 0x24, // 10: next: mov %r11, (%rsp)
0xc3, // 14: ret
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 15: int3; padding
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // 1a: int3; padding
+ 0xcc, // 1f: int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
}
@@ -556,14 +561,14 @@
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const uint8_t Insn[] = {
- 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11
- 0xe9, 0, 0, 0, 0, // jmp plt+0
+ 0x4c, 0x8b, 0x1d, 0, 0, 0, 0, // mov foo@GOTPLT(%rip), %r11
+ 0xe9, 0, 0, 0, 0, // jmp plt+0
+ 0xcc, 0xcc, 0xcc, 0xcc, // int3; padding
};
memcpy(Buf, Insn, sizeof(Insn));
write32le(Buf + 3, GotPltEntryAddr - PltEntryAddr - 7);
- write32le(Buf + 8,
- -Index * TargetInfo::PltEntrySize - TargetInfo::PltHeaderSize - 12);
+ write32le(Buf + 8, -TargetInfo::getPltEntryOffset(Index) - 12);
}
template <class ELFT> TargetInfo *getTargetInfo() {
diff --git a/ELF/Config.h b/ELF/Config.h
index 193a520..2007992 100644
--- a/ELF/Config.h
+++ b/ELF/Config.h
@@ -113,6 +113,7 @@
bool BsymbolicFunctions;
bool CheckSections;
bool CompressDebugSections;
+ bool Cref;
bool DefineCommon;
bool Demangle = true;
bool DisableVerify;
diff --git a/ELF/Driver.cpp b/ELF/Driver.cpp
index 8abac81..4565fde 100644
--- a/ELF/Driver.cpp
+++ b/ELF/Driver.cpp
@@ -127,6 +127,7 @@
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
.Case("elf64ppc", {ELF64BEKind, EM_PPC64})
+ .Case("elf64lppc", {ELF64LEKind, EM_PPC64})
.Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64})
.Case("elf_i386", {ELF32LEKind, EM_386})
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
@@ -600,6 +601,17 @@
return Names.takeVector();
}
+static void parseClangOption(StringRef Opt, const Twine &Msg) {
+ std::string Err;
+ raw_string_ostream OS(Err);
+
+ const char *Argv[] = {Config->ProgName.data(), Opt.data()};
+ if (cl::ParseCommandLineOptions(2, Argv, "", &OS))
+ return;
+ OS.flush();
+ error(Msg + ": " + StringRef(Err).trim());
+}
+
// Initializes Config members by the command line options.
void LinkerDriver::readConfigs(opt::InputArgList &Args) {
errorHandler().Verbose = Args.hasArg(OPT_verbose);
@@ -617,6 +629,7 @@
Args.hasFlag(OPT_check_sections, OPT_no_check_sections, true);
Config->Chroot = Args.getLastArgValue(OPT_chroot);
Config->CompressDebugSections = getCompressDebugSections(Args);
+ Config->Cref = Args.hasFlag(OPT_cref, OPT_no_cref, false);
Config->DefineCommon = Args.hasFlag(OPT_define_common, OPT_no_define_common,
!Args.hasArg(OPT_relocatable));
Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
@@ -707,7 +720,6 @@
Config->ZWxneeded = hasZOption(Args, "wxneeded");
// Parse LTO plugin-related options for compatibility with gold.
- std::vector<const char *> LTOOptions({Config->ProgName.data()});
for (auto *Arg : Args.filtered(OPT_plugin_opt)) {
StringRef S = Arg->getValue();
if (S == "disable-verify")
@@ -721,15 +733,15 @@
else if (S.startswith("jobs="))
Config->ThinLTOJobs = parseInt(S.substr(5), Arg);
else if (S.startswith("mcpu="))
- LTOOptions.push_back(Saver.save("-" + S).data());
+ parseClangOption(Saver.save("-" + S), Arg->getSpelling());
else if (!S.startswith("/") && !S.startswith("-fresolution=") &&
!S.startswith("-pass-through=") && !S.startswith("thinlto"))
- LTOOptions.push_back(S.data());
+ parseClangOption(S, Arg->getSpelling());
}
- // Parse and evaluate -mllvm options.
+
+ // Parse -mllvm options.
for (auto *Arg : Args.filtered(OPT_mllvm))
- LTOOptions.push_back(Arg->getValue());
- cl::ParseCommandLineOptions(LTOOptions.size(), LTOOptions.data());
+ parseClangOption(Arg->getValue(), Arg->getSpelling());
if (Config->LTOO > 3)
error("invalid optimization level for LTO: " + Twine(Config->LTOO));
@@ -910,6 +922,12 @@
case OPT_no_whole_archive:
InWholeArchive = false;
break;
+ case OPT_just_symbols:
+ if (Optional<MemoryBufferRef> MB = readFile(Arg->getValue())) {
+ Files.push_back(createObjectFile(*MB));
+ Files.back()->JustSymbols = true;
+ }
+ break;
case OPT_start_lib:
InLib = true;
break;
@@ -1081,6 +1099,10 @@
if (errorCount())
return;
+ // Now when we read all script files, we want to finalize order of linker
+ // script commands, which can be not yet final because of INSERT commands.
+ Script->processInsertCommands();
+
// We want to declare linker script's symbols early,
// so that we can version them.
// They also might be exported if referenced by DSOs.
diff --git a/ELF/ICF.cpp b/ELF/ICF.cpp
index 8b58c99..ffd6029 100644
--- a/ELF/ICF.cpp
+++ b/ELF/ICF.cpp
@@ -77,6 +77,7 @@
#include "Config.h"
#include "SymbolTable.h"
#include "Symbols.h"
+#include "SyntheticSections.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/BinaryFormat/ELF.h"
@@ -161,16 +162,26 @@
// Returns true if section S is subject of ICF.
static bool isEligible(InputSection *S) {
+ if (!S->Live || !(S->Flags & SHF_ALLOC) || (S->Flags & SHF_WRITE))
+ return false;
+
// Don't merge read only data sections unless
// --ignore-data-address-equality was passed.
if (!(S->Flags & SHF_EXECINSTR) && !Config->IgnoreDataAddressEquality)
return false;
- // .init and .fini contains instructions that must be executed to
- // initialize and finalize the process. They cannot and should not
- // be merged.
- return S->Live && (S->Flags & SHF_ALLOC) && !(S->Flags & SHF_WRITE) &&
- S->Name != ".init" && S->Name != ".fini";
+ // Don't merge synthetic sections as their Data member is not valid and empty.
+ // The Data member needs to be valid for ICF as it is used by ICF to determine
+ // the equality of section contents.
+ if (isa<SyntheticSection>(S))
+ return false;
+
+ // .init and .fini contains instructions that must be executed to initialize
+ // and finalize the process. They cannot and should not be merged.
+ if (S->Name == ".init" || S->Name == ".fini")
+ return false;
+
+ return true;
}
// Split an equivalence class into smaller classes.
diff --git a/ELF/InputFiles.cpp b/ELF/InputFiles.cpp
index 0bff035..bb20c83 100644
--- a/ELF/InputFiles.cpp
+++ b/ELF/InputFiles.cpp
@@ -115,49 +115,46 @@
}
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
- DWARFContext Dwarf(make_unique<LLDDwarfObj<ELFT>>(this));
- const DWARFObject &Obj = Dwarf.getDWARFObj();
+ Dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
+ const DWARFObject &Obj = Dwarf->getDWARFObj();
DwarfLine.reset(new DWARFDebugLine);
DWARFDataExtractor LineData(Obj, Obj.getLineSection(), Config->IsLE,
Config->Wordsize);
- // The second parameter is offset in .debug_line section
- // for compilation unit (CU) of interest. We have only one
- // CU (object file), so offset is always 0.
- const DWARFDebugLine::LineTable *LT =
- DwarfLine->getOrParseLineTable(LineData, 0, Dwarf, nullptr);
-
- // Return if there is no debug information about CU available.
- if (!Dwarf.getNumCompileUnits())
- return;
-
- // Loop over variable records and insert them to VariableLoc.
- DWARFCompileUnit *CU = Dwarf.getCompileUnitAtIndex(0);
- for (const auto &Entry : CU->dies()) {
- DWARFDie Die(CU, &Entry);
- // Skip all tags that are not variables.
- if (Die.getTag() != dwarf::DW_TAG_variable)
+ for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units()) {
+ const DWARFDebugLine::LineTable *LT = Dwarf->getLineTableForUnit(CU.get());
+ if (!LT)
continue;
+ LineTables.push_back(LT);
- // Skip if a local variable because we don't need them for generating error
- // messages. In general, only non-local symbols can fail to be linked.
- if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
- continue;
+ // Loop over variable records and insert them to VariableLoc.
+ for (const auto &Entry : CU->dies()) {
+ DWARFDie Die(CU.get(), &Entry);
+ // Skip all tags that are not variables.
+ if (Die.getTag() != dwarf::DW_TAG_variable)
+ continue;
- // Get the source filename index for the variable.
- unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
- if (!LT->hasFileAtIndex(File))
- continue;
+ // Skip if a local variable because we don't need them for generating
+ // error messages. In general, only non-local symbols can fail to be
+ // linked.
+ if (!dwarf::toUnsigned(Die.find(dwarf::DW_AT_external), 0))
+ continue;
- // Get the line number on which the variable is declared.
- unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
+ // Get the source filename index for the variable.
+ unsigned File = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_file), 0);
+ if (!LT->hasFileAtIndex(File))
+ continue;
- // Get the name of the variable and add the collected information to
- // VariableLoc. Usually Name is non-empty, but it can be empty if the input
- // object file lacks some debug info.
- StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), "");
- if (!Name.empty())
- VariableLoc.insert({Name, {File, Line}});
+ // Get the line number on which the variable is declared.
+ unsigned Line = dwarf::toUnsigned(Die.find(dwarf::DW_AT_decl_line), 0);
+
+ // Get the name of the variable and add the collected information to
+ // VariableLoc. Usually Name is non-empty, but it can be empty if the
+ // input object file lacks some debug info.
+ StringRef Name = dwarf::toString(Die.find(dwarf::DW_AT_name), "");
+ if (!Name.empty())
+ VariableLoc.insert({Name, {LT, File, Line}});
+ }
}
}
@@ -168,11 +165,6 @@
ObjFile<ELFT>::getVariableLoc(StringRef Name) {
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
- // There is always only one CU so it's offset is 0.
- const DWARFDebugLine::LineTable *LT = DwarfLine->getLineTable(0);
- if (!LT)
- return None;
-
// Return if we have no debug information about data object.
auto It = VariableLoc.find(Name);
if (It == VariableLoc.end())
@@ -180,12 +172,12 @@
// Take file name string from line table.
std::string FileName;
- if (!LT->getFileNameByIndex(
- It->second.first /* File */, nullptr,
+ if (!It->second.LT->getFileNameByIndex(
+ It->second.File, nullptr,
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FileName))
return None;
- return std::make_pair(FileName, It->second.second /*Line*/);
+ return std::make_pair(FileName, It->second.Line);
}
// Returns source line information for a given offset
@@ -195,24 +187,18 @@
uint64_t Offset) {
llvm::call_once(InitDwarfLine, [this]() { initializeDwarf(); });
- // The offset to CU is 0.
- const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);
- if (!Tbl)
- return None;
-
// Use fake address calcuated by adding section file offset and offset in
// section. See comments for ObjectInfo class.
DILineInfo Info;
- Tbl->getFileLineInfoForAddress(
- S->getOffsetInFile() + Offset, nullptr,
- DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info);
- if (Info.Line == 0)
- return None;
- return Info;
+ for (const llvm::DWARFDebugLine::LineTable *LT : LineTables)
+ if (LT->getFileLineInfoForAddress(
+ S->getOffsetInFile() + Offset, nullptr,
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info))
+ return Info;
+ return None;
}
-// Returns source line information for a given offset
-// using DWARF debug info.
+// Returns source line information for a given offset using DWARF debug info.
template <class ELFT>
std::string ObjFile<ELFT>::getLineInfo(InputSectionBase *S, uint64_t Offset) {
if (Optional<DILineInfo> Info = getDILineInfo(S, Offset))
@@ -247,7 +233,7 @@
template <class ELFT>
typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalELFSyms() {
- return makeArrayRef(ELFSyms.begin() + FirstNonLocal, ELFSyms.end());
+ return makeArrayRef(ELFSyms.begin() + FirstGlobal, ELFSyms.end());
}
template <class ELFT>
@@ -258,9 +244,9 @@
template <class ELFT>
void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
const Elf_Shdr *Symtab) {
- FirstNonLocal = Symtab->sh_info;
+ FirstGlobal = Symtab->sh_info;
ELFSyms = CHECK(getObj().symbols(Symtab), this);
- if (FirstNonLocal == 0 || FirstNonLocal > ELFSyms.size())
+ if (FirstGlobal == 0 || FirstGlobal > ELFSyms.size())
fatal(toString(this) + ": invalid sh_info in symbol table");
StringTable =
@@ -276,13 +262,22 @@
template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getLocalSymbols() {
if (this->Symbols.empty())
return {};
- return makeArrayRef(this->Symbols).slice(1, this->FirstNonLocal - 1);
+ return makeArrayRef(this->Symbols).slice(1, this->FirstGlobal - 1);
+}
+
+template <class ELFT> ArrayRef<Symbol *> ObjFile<ELFT>::getGlobalSymbols() {
+ return makeArrayRef(this->Symbols).slice(this->FirstGlobal);
}
template <class ELFT>
void ObjFile<ELFT>::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
- // Read section and symbol tables.
- initializeSections(ComdatGroups);
+ // Read a section table. JustSymbols is usually false.
+ if (this->JustSymbols)
+ initializeJustSymbols();
+ else
+ initializeSections(ComdatGroups);
+
+ // Read a symbol table.
initializeSymbols();
}
@@ -306,7 +301,7 @@
// we use a section name as a signature.
//
// Such SHT_GROUP sections are invalid from the perspective of the ELF
- // standard, but GNU gold 1.14 (the neweset version as of July 2017) or
+ // standard, but GNU gold 1.14 (the newest version as of July 2017) or
// older produce such sections as outputs for the -r option, so we need
// a bug-compatibility.
if (Signature.empty() && Sym->getType() == STT_SECTION)
@@ -326,9 +321,19 @@
}
template <class ELFT> bool ObjFile<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)
+ // On a regular link we don't merge sections if -O0 (default is -O1). This
+ // sometimes makes the linker significantly faster, although the output will
+ // be bigger.
+ //
+ // Doing the same for -r would create a problem as it would combine sections
+ // with different sh_entsize. One option would be to just copy every SHF_MERGE
+ // section as is to the output. While this would produce a valid ELF file with
+ // usable SHF_MERGE sections, tools like (llvm-)?dwarfdump get confused when
+ // they see two .debug_str. We could have separate logic for combining
+ // SHF_MERGE sections based both on their name and sh_entsize, but that seems
+ // to be more trouble than it is worth. Instead, we just use the regular (-O1)
+ // logic for -r.
+ if (Config->Optimize == 0 && !Config->Relocatable)
return false;
// A mergeable section with size 0 is useless because they don't have
@@ -359,6 +364,27 @@
return true;
}
+// This is for --just-symbols.
+//
+// --just-symbols is a very minor feature that allows you to link your
+// output against other existing program, so that if you load both your
+// program and the other program into memory, your output can refer the
+// other program's symbols.
+//
+// When the option is given, we link "just symbols". The section table is
+// initialized with null pointers.
+template <class ELFT> void ObjFile<ELFT>::initializeJustSymbols() {
+ ArrayRef<Elf_Shdr> ObjSections = CHECK(this->getObj().sections(), this);
+ this->Sections.resize(ObjSections.size());
+
+ for (const Elf_Shdr &Sec : ObjSections) {
+ if (Sec.sh_type != SHT_SYMTAB)
+ continue;
+ this->initSymtab(ObjSections, &Sec);
+ return;
+ }
+}
+
template <class ELFT>
void ObjFile<ELFT>::initializeSections(
DenseSet<CachedHashStringRef> &ComdatGroups) {
@@ -429,8 +455,15 @@
if (Sec.sh_link >= this->Sections.size())
fatal(toString(this) +
": invalid sh_link index: " + Twine(Sec.sh_link));
- this->Sections[Sec.sh_link]->DependentSections.push_back(
- cast<InputSection>(this->Sections[I]));
+
+ InputSectionBase *LinkSec = this->Sections[Sec.sh_link];
+ InputSection *IS = cast<InputSection>(this->Sections[I]);
+ LinkSec->DependentSections.push_back(IS);
+ if (!isa<InputSection>(LinkSec))
+ error("a section " + IS->Name +
+ " with SHF_LINK_ORDER should not refer a non-regular "
+ "section: " +
+ toString(LinkSec));
}
}
}
@@ -525,10 +558,11 @@
}
case SHT_RELA:
case SHT_REL: {
- // Find the relocation target section and associate this
- // section with it. Target can be discarded, for example
- // if it is a duplicated member of SHT_GROUP section, we
- // do not create or proccess relocatable sections then.
+ // Find a relocation target section and associate this section with that.
+ // Target may have been discarded if it is in a different section group
+ // and the group is discarded, even though it's a violation of the
+ // spec. We handle that situation gracefully by discarding dangling
+ // relocation sections.
InputSectionBase *Target = getRelocTarget(Sec);
if (!Target)
return nullptr;
@@ -543,32 +577,28 @@
fatal(toString(this) +
": multiple relocation sections to one section are not supported");
- // Mergeable sections with relocations are tricky because relocations
- // need to be taken into account when comparing section contents for
- // merging. It's not worth supporting such mergeable sections because
- // they are rare and it'd complicates the internal design (we usually
- // have to determine if two sections are mergeable early in the link
- // process much before applying relocations). We simply handle mergeable
- // sections with relocations as non-mergeable.
+ // ELF spec allows mergeable sections with relocations, but they are
+ // rare, and it is in practice hard to merge such sections by contents,
+ // because applying relocations at end of linking changes section
+ // contents. So, we simply handle such sections as non-mergeable ones.
+ // Degrading like this is acceptable because section merging is optional.
if (auto *MS = dyn_cast<MergeInputSection>(Target)) {
Target = toRegularSection(MS);
this->Sections[Sec.sh_info] = Target;
}
- size_t NumRelocations;
if (Sec.sh_type == SHT_RELA) {
ArrayRef<Elf_Rela> Rels = CHECK(this->getObj().relas(&Sec), this);
Target->FirstRelocation = Rels.begin();
- NumRelocations = Rels.size();
+ Target->NumRelocations = Rels.size();
Target->AreRelocsRela = true;
} else {
ArrayRef<Elf_Rel> Rels = CHECK(this->getObj().rels(&Sec), this);
Target->FirstRelocation = Rels.begin();
- NumRelocations = Rels.size();
+ Target->NumRelocations = Rels.size();
Target->AreRelocsRela = false;
}
- assert(isUInt<31>(NumRelocations));
- Target->NumRelocations = NumRelocations;
+ assert(isUInt<31>(Target->NumRelocations));
// Relocation sections processed by the linker are usually removed
// from the output, so returning `nullptr` for the normal case.
@@ -789,34 +819,42 @@
}
}
+// Parses ".gnu.version" section which is a parallel array for the symbol table.
+// If a given file doesn't have ".gnu.version" section, returns VER_NDX_GLOBAL.
+template <class ELFT> std::vector<uint32_t> SharedFile<ELFT>::parseVersyms() {
+ size_t Size = this->ELFSyms.size() - this->FirstGlobal;
+ if (!VersymSec)
+ return std::vector<uint32_t>(Size, VER_NDX_GLOBAL);
+
+ const char *Base = this->MB.getBuffer().data();
+ const Elf_Versym *Versym =
+ reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) +
+ this->FirstGlobal;
+
+ std::vector<uint32_t> Ret(Size);
+ for (size_t I = 0; I < Size; ++I)
+ Ret[I] = Versym[I].vs_index;
+ return Ret;
+}
+
// 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.
+// n. Version identifiers that are not definitions map to nullptr.
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.
- const char *Base = this->MB.getBuffer().data();
- Versym = reinterpret_cast<const Elf_Versym *>(Base + VersymSec->sh_offset) +
- this->FirstNonLocal;
+std::vector<const typename ELFT::Verdef *> SharedFile<ELFT>::parseVerdefs() {
+ if (!VerdefSec)
+ return {};
// 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);
+ std::vector<const Elf_Verdef *> Verdefs(VerdefCount + 1);
// Build the Verdefs array by following the chain of Elf_Verdef objects
// from the start of the .gnu.version_d section.
+ const char *Base = this->MB.getBuffer().data();
const char *Verdef = Base + VerdefSec->sh_offset;
for (unsigned I = 0; I != VerdefCount; ++I) {
auto *CurVerdef = reinterpret_cast<const Elf_Verdef *>(Verdef);
@@ -830,24 +868,49 @@
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;
- Verdefs = parseVerdefs(Versym);
+// We do not usually care about alignments of data in shared object
+// files because the loader takes care of it. However, if we promote a
+// DSO symbol to point to .bss due to copy relocation, we need to keep
+// the original alignment requirements. We infer it in this function.
+template <class ELFT>
+uint32_t SharedFile<ELFT>::getAlignment(ArrayRef<Elf_Shdr> Sections,
+ const Elf_Sym &Sym) {
+ uint64_t Ret = 1;
+ if (Sym.st_value)
+ Ret = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
+ if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size())
+ Ret = std::min<uint64_t>(Ret, Sections[Sym.st_shndx].sh_addralign);
+ if (Ret > UINT32_MAX)
+ error(toString(this) + ": alignment too large: " +
+ CHECK(Sym.getName(this->StringTable), this));
+ return Ret;
+}
+
+// Fully parse the shared object file. This must be called after parseSoName().
+//
+// This function parses symbol versions. If a DSO has version information,
+// the file has a ".gnu.version_d" section which contains symbol version
+// definitions. Each symbol is associated to one version through a table in
+// ".gnu.version" section. That table is a parallel array for the symbol
+// table, and each table entry contains an index in ".gnu.version_d".
+//
+// The special index 0 is reserved for VERF_NDX_LOCAL and 1 is for
+// VER_NDX_GLOBAL. There's no table entry for these special versions in
+// ".gnu.version_d".
+//
+// The file format for symbol versioning is perhaps a bit more complicated
+// than necessary, but you can easily understand the code if you wrap your
+// head around the data structure described above.
+template <class ELFT> void SharedFile<ELFT>::parseRest() {
+ Verdefs = parseVerdefs(); // parse .gnu.version_d
+ std::vector<uint32_t> Versyms = parseVersyms(); // parse .gnu.version
ArrayRef<Elf_Shdr> Sections = CHECK(this->getObj().sections(), this);
// Add symbols to the symbol table.
- Elf_Sym_Range Syms = this->getGlobalELFSyms();
- for (const Elf_Sym &Sym : Syms) {
- unsigned VersymIndex = VER_NDX_GLOBAL;
- if (Versym) {
- VersymIndex = Versym->vs_index;
- ++Versym;
- }
- bool Hidden = VersymIndex & VERSYM_HIDDEN;
- VersymIndex = VersymIndex & ~VERSYM_HIDDEN;
+ ArrayRef<Elf_Sym> Syms = this->getGlobalELFSyms();
+ for (size_t I = 0; I < Syms.size(); ++I) {
+ const Elf_Sym &Sym = Syms[I];
StringRef Name = CHECK(Sym.getName(this->StringTable), this);
if (Sym.isUndefined()) {
@@ -858,57 +921,44 @@
continue;
}
+ // ELF spec requires that all local symbols precede weak or global
+ // symbols in each symbol table, and the index of first non-local symbol
+ // is stored to sh_info. If a local symbol appears after some non-local
+ // symbol, that's a violation of the spec.
if (Sym.getBinding() == STB_LOCAL) {
warn("found local symbol '" + Name +
"' in global part of symbol table in file " + toString(this));
continue;
}
- if (Config->EMachine == EM_MIPS) {
- // FIXME: MIPS BFD linker puts _gp_disp symbol into DSO files
- // and incorrectly assigns VER_NDX_LOCAL to this section global
- // symbol. Here is a workaround for this bug.
- if (Versym && VersymIndex == VER_NDX_LOCAL && Name == "_gp_disp")
- continue;
- }
+ // MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly
+ // assigns VER_NDX_LOCAL to this section global symbol. Here is a
+ // workaround for this bug.
+ uint32_t Idx = Versyms[I] & ~VERSYM_HIDDEN;
+ if (Config->EMachine == EM_MIPS && Idx == VER_NDX_LOCAL &&
+ Name == "_gp_disp")
+ continue;
- const Elf_Verdef *Ver = nullptr;
- if (VersymIndex != VER_NDX_GLOBAL) {
- if (VersymIndex >= Verdefs.size() || VersymIndex == VER_NDX_LOCAL) {
- error("corrupt input file: version definition index " +
- Twine(VersymIndex) + " for symbol " + Name +
- " is out of bounds\n>>> defined in " + toString(this));
- continue;
- }
- Ver = Verdefs[VersymIndex];
- } else {
- VersymIndex = 0;
- }
-
- // We do not usually care about alignments of data in shared object
- // files because the loader takes care of it. However, if we promote a
- // DSO symbol to point to .bss due to copy relocation, we need to keep
- // the original alignment requirements. We infer it here.
- uint64_t Alignment = 1;
- if (Sym.st_value)
- Alignment = 1ULL << countTrailingZeros((uint64_t)Sym.st_value);
- if (0 < Sym.st_shndx && Sym.st_shndx < Sections.size()) {
- uint64_t SecAlign = Sections[Sym.st_shndx].sh_addralign;
- Alignment = std::min(Alignment, SecAlign);
- }
- if (Alignment > UINT32_MAX)
- error(toString(this) + ": alignment too large: " + Name);
-
- if (!Hidden)
- Symtab->addShared(Name, *this, Sym, Alignment, VersymIndex);
+ uint64_t Alignment = getAlignment(Sections, Sym);
+ if (!(Versyms[I] & VERSYM_HIDDEN))
+ Symtab->addShared(Name, *this, Sym, Alignment, Idx);
// Also add the symbol with the versioned name to handle undefined symbols
// with explicit versions.
- if (Ver) {
- StringRef VerName = this->StringTable.data() + Ver->getAux()->vda_name;
- Name = Saver.save(Name + "@" + VerName);
- Symtab->addShared(Name, *this, Sym, Alignment, VersymIndex);
+ if (Idx == VER_NDX_GLOBAL)
+ continue;
+
+ if (Idx >= Verdefs.size() || Idx == VER_NDX_LOCAL) {
+ error("corrupt input file: version definition index " + Twine(Idx) +
+ " for symbol " + Name + " is out of bounds\n>>> defined in " +
+ toString(this));
+ continue;
}
+
+ StringRef VerName =
+ this->StringTable.data() + Verdefs[Idx]->getAux()->vda_name;
+ Name = Saver.save(Name + "@" + VerName);
+ Symtab->addShared(Name, *this, Sym, Alignment, Idx);
}
}
@@ -1117,64 +1167,55 @@
}
template <class ELFT> void LazyObjFile::parse() {
- for (StringRef Sym : getSymbolNames())
- Symtab->addLazyObject<ELFT>(Sym, *this);
-}
-
-template <class ELFT> std::vector<StringRef> LazyObjFile::getElfSymbols() {
- typedef typename ELFT::Shdr Elf_Shdr;
- typedef typename ELFT::Sym Elf_Sym;
- typedef typename ELFT::SymRange Elf_Sym_Range;
-
- ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(this->MB.getBuffer()));
- ArrayRef<Elf_Shdr> Sections = CHECK(Obj.sections(), this);
- for (const Elf_Shdr &Sec : Sections) {
- if (Sec.sh_type != SHT_SYMTAB)
- continue;
-
- Elf_Sym_Range Syms = CHECK(Obj.symbols(&Sec), this);
- uint32_t FirstNonLocal = Sec.sh_info;
- StringRef StringTable =
- CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
- 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), this));
- return V;
+ // A lazy object file wraps either a bitcode file or an ELF file.
+ if (isBitcode(this->MB)) {
+ std::unique_ptr<lto::InputFile> Obj =
+ CHECK(lto::InputFile::create(this->MB), this);
+ for (const lto::InputFile::Symbol &Sym : Obj->symbols())
+ if (!Sym.isUndefined())
+ Symtab->addLazyObject<ELFT>(Saver.save(Sym.getName()), *this);
+ return;
}
- return {};
-}
-
-std::vector<StringRef> LazyObjFile::getBitcodeSymbols() {
- std::unique_ptr<lto::InputFile> Obj =
- CHECK(lto::InputFile::create(this->MB), this);
- std::vector<StringRef> V;
- for (const lto::InputFile::Symbol &Sym : Obj->symbols())
- if (!Sym.isUndefined())
- V.push_back(Saver.save(Sym.getName()));
- return V;
-}
-
-// Returns a vector of globally-visible defined symbol names.
-std::vector<StringRef> LazyObjFile::getSymbolNames() {
- if (isBitcode(this->MB))
- return getBitcodeSymbols();
switch (getELFKind(this->MB)) {
case ELF32LEKind:
- return getElfSymbols<ELF32LE>();
+ addElfSymbols<ELF32LE>();
+ return;
case ELF32BEKind:
- return getElfSymbols<ELF32BE>();
+ addElfSymbols<ELF32BE>();
+ return;
case ELF64LEKind:
- return getElfSymbols<ELF64LE>();
+ addElfSymbols<ELF64LE>();
+ return;
case ELF64BEKind:
- return getElfSymbols<ELF64BE>();
+ addElfSymbols<ELF64BE>();
+ return;
default:
llvm_unreachable("getELFKind");
}
}
+template <class ELFT> void LazyObjFile::addElfSymbols() {
+ ELFFile<ELFT> Obj = check(ELFFile<ELFT>::create(MB.getBuffer()));
+ ArrayRef<typename ELFT::Shdr> Sections = CHECK(Obj.sections(), this);
+
+ for (const typename ELFT::Shdr &Sec : Sections) {
+ if (Sec.sh_type != SHT_SYMTAB)
+ continue;
+
+ typename ELFT::SymRange Syms = CHECK(Obj.symbols(&Sec), this);
+ uint32_t FirstGlobal = Sec.sh_info;
+ StringRef StringTable =
+ CHECK(Obj.getStringTableForSymtab(Sec, Sections), this);
+
+ for (const typename ELFT::Sym &Sym : Syms.slice(FirstGlobal))
+ if (Sym.st_shndx != SHN_UNDEF)
+ Symtab->addLazyObject<ELFT>(CHECK(Sym.getName(StringTable), this),
+ *this);
+ return;
+ }
+}
+
template void ArchiveFile::parse<ELF32LE>();
template void ArchiveFile::parse<ELF32BE>();
template void ArchiveFile::parse<ELF64LE>();
diff --git a/ELF/InputFiles.h b/ELF/InputFiles.h
index 5b9e9d7..8ec5953 100644
--- a/ELF/InputFiles.h
+++ b/ELF/InputFiles.h
@@ -17,6 +17,7 @@
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
#include "llvm/IR/Comdat.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
@@ -25,7 +26,6 @@
#include <map>
namespace llvm {
-class DWARFDebugLine;
class TarWriter;
struct DILineInfo;
namespace lto {
@@ -110,6 +110,9 @@
std::string getSrcMsg(const Symbol &Sym, InputSectionBase &Sec,
uint64_t Offset);
+ // True if this is an argument for --just-symbols. Usually false.
+ bool JustSymbols = false;
+
protected:
InputFile(Kind K, MemoryBufferRef M);
std::vector<InputSectionBase *> Sections;
@@ -142,7 +145,7 @@
protected:
ArrayRef<Elf_Sym> ELFSyms;
- uint32_t FirstNonLocal = 0;
+ uint32_t FirstGlobal = 0;
ArrayRef<Elf_Word> SymtabSHNDX;
StringRef StringTable;
void initSymtab(ArrayRef<Elf_Shdr> Sections, const Elf_Shdr *Symtab);
@@ -165,6 +168,7 @@
static bool classof(const InputFile *F) { return F->kind() == Base::ObjKind; }
ArrayRef<Symbol *> getLocalSymbols();
+ ArrayRef<Symbol *> getGlobalSymbols();
ObjFile(MemoryBufferRef M, StringRef ArchiveName);
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
@@ -200,6 +204,7 @@
void
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
void initializeSymbols();
+ void initializeJustSymbols();
void initializeDwarf();
InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
InputSectionBase *createInputSection(const Elf_Shdr &Sec);
@@ -215,8 +220,15 @@
// reporting. Linker may find reasonable number of errors in a
// single object file, so we cache debugging information in order to
// parse it only once for each object file we link.
+ std::unique_ptr<llvm::DWARFContext> Dwarf;
+ std::vector<const llvm::DWARFDebugLine::LineTable *> LineTables;
std::unique_ptr<llvm::DWARFDebugLine> DwarfLine;
- llvm::DenseMap<StringRef, std::pair<unsigned, unsigned>> VariableLoc;
+ struct VarLoc {
+ const llvm::DWARFDebugLine::LineTable *LT;
+ unsigned File;
+ unsigned Line;
+ };
+ llvm::DenseMap<StringRef, VarLoc> VariableLoc;
llvm::once_flag InitDwarfLine;
};
@@ -242,9 +254,7 @@
InputFile *fetch();
private:
- std::vector<StringRef> getSymbolNames();
- template <class ELFT> std::vector<StringRef> getElfSymbols();
- std::vector<StringRef> getBitcodeSymbols();
+ template <class ELFT> void addElfSymbols();
bool Seen = false;
uint64_t OffsetInArchive;
@@ -303,7 +313,9 @@
void parseSoName();
void parseRest();
- std::vector<const Elf_Verdef *> parseVerdefs(const Elf_Versym *&Versym);
+ uint32_t getAlignment(ArrayRef<Elf_Shdr> Sections, const Elf_Sym &Sym);
+ std::vector<const Elf_Verdef *> parseVerdefs();
+ std::vector<uint32_t> parseVersyms();
struct NeededVer {
// The string table offset of the version name in the output file.
diff --git a/ELF/InputSection.cpp b/ELF/InputSection.cpp
index f7a757e..49426e8 100644
--- a/ELF/InputSection.cpp
+++ b/ELF/InputSection.cpp
@@ -142,9 +142,9 @@
return Offset == uint64_t(-1) ? OS->Size : Offset;
}
case Regular:
- return cast<InputSection>(this)->OutSecOff + Offset;
+ return cast<InputSection>(this->Repl)->OutSecOff + Offset;
case Synthetic: {
- auto *IS = cast<InputSection>(this);
+ auto *IS = cast<InputSection>(this->Repl);
// For synthetic sections we treat offset -1 as the end of the section.
return IS->OutSecOff + (Offset == uint64_t(-1) ? IS->getSize() : Offset);
}
@@ -156,23 +156,28 @@
case Merge:
const MergeInputSection *MS = cast<MergeInputSection>(this);
if (InputSection *IS = MS->getParent())
- return IS->OutSecOff + MS->getOffset(Offset);
+ return cast<InputSection>(IS->Repl)->OutSecOff + MS->getOffset(Offset);
return MS->getOffset(Offset);
}
llvm_unreachable("invalid section kind");
}
+uint64_t SectionBase::getVA(uint64_t Offset) const {
+ const OutputSection *Out = getOutputSection();
+ return (Out ? Out->Addr : 0) + getOffset(Offset);
+}
+
OutputSection *SectionBase::getOutputSection() {
InputSection *Sec;
if (auto *IS = dyn_cast<InputSection>(this))
- return IS->getParent();
+ Sec = IS;
else if (auto *MS = dyn_cast<MergeInputSection>(this))
Sec = MS->getParent();
else if (auto *EH = dyn_cast<EhInputSection>(this))
Sec = EH->getParent();
else
return cast<OutputSection>(this);
- return Sec ? Sec->getParent() : nullptr;
+ return Sec ? cast<InputSection>(Sec->Repl)->getParent() : nullptr;
}
// Decompress section contents if required. Note that this function
@@ -206,15 +211,9 @@
}
InputSection *InputSectionBase::getLinkOrderDep() const {
- if ((Flags & SHF_LINK_ORDER) && Link != 0) {
- InputSectionBase *L = File->getSections()[Link];
- if (auto *IS = dyn_cast<InputSection>(L))
- return IS;
- error("a section with SHF_LINK_ORDER should not refer a non-regular "
- "section: " +
- toString(L));
- }
- return nullptr;
+ assert(Link);
+ assert(Flags & SHF_LINK_ORDER);
+ return cast<InputSection>(File->getSections()[Link]);
}
// Returns a source location string. Used to construct an error message.
@@ -331,7 +330,8 @@
}
InputSectionBase *InputSection::getRelocatedSection() {
- assert(Type == SHT_RELA || Type == SHT_REL);
+ if (!File || (Type != SHT_RELA && Type != SHT_REL))
+ return nullptr;
ArrayRef<InputSectionBase *> Sections = File->getSections();
return Sections[Info];
}
@@ -355,7 +355,7 @@
// Output section VA is zero for -r, so r_offset is an offset within the
// section, but for --emit-relocs it is an virtual address.
- P->r_offset = Sec->getOutputSection()->Addr + Sec->getOffset(Rel.r_offset);
+ P->r_offset = Sec->getVA(Rel.r_offset);
P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type,
Config->IsMips64EL);
@@ -710,11 +710,10 @@
const unsigned Bits = Config->Wordsize * 8;
for (const Relocation &Rel : Relocations) {
- uint64_t Offset = getOffset(Rel.Offset);
- uint8_t *BufLoc = Buf + Offset;
+ uint8_t *BufLoc = Buf + getOffset(Rel.Offset);
RelType Type = Rel.Type;
- uint64_t AddrLoc = getOutputSection()->Addr + Offset;
+ uint64_t AddrLoc = getVA(Rel.Offset);
RelExpr Expr = Rel.Expr;
uint64_t TargetVA = SignExtend64(
getRelocTargetVA(Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr), Bits);
@@ -922,16 +921,10 @@
splitNonStrings(Data, Entsize);
if (Config->GcSections && (Flags & SHF_ALLOC))
- for (uint64_t Off : LiveOffsets)
+ for (uint32_t Off : LiveOffsets)
getSectionPiece(Off)->Live = true;
}
-// Do binary search to get a section piece at a given input offset.
-SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
- auto *This = static_cast<const MergeInputSection *>(this);
- return const_cast<SectionPiece *>(This->getSectionPiece(Offset));
-}
-
template <class It, class T, class Compare>
static It fastUpperBound(It First, It Last, const T &Value, Compare Comp) {
size_t Size = std::distance(First, Last);
@@ -945,7 +938,8 @@
return Comp(Value, *First) ? First : First + 1;
}
-const SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) const {
+// Do binary search to get a section piece at a given input offset.
+SectionPiece *MergeInputSection::getSectionPiece(uint64_t Offset) {
if (Data.size() <= Offset)
fatal(toString(this) + ": entry is past the end of the section");
@@ -964,13 +958,6 @@
if (!Live)
return 0;
- // Initialize OffsetMap lazily.
- llvm::call_once(InitOffsetMap, [&] {
- OffsetMap.reserve(Pieces.size());
- for (size_t I = 0; I < Pieces.size(); ++I)
- OffsetMap[Pieces[I].InputOff] = I;
- });
-
// Find a string starting at a given offset.
auto It = OffsetMap.find(Offset);
if (It != OffsetMap.end())
@@ -986,6 +973,12 @@
return Piece.OutputOff + Addend;
}
+void MergeInputSection::initOffsetMap() {
+ OffsetMap.reserve(Pieces.size());
+ for (size_t I = 0; I < Pieces.size(); ++I)
+ OffsetMap[Pieces[I].InputOff] = I;
+}
+
template InputSection::InputSection(ObjFile<ELF32LE> &, const ELF32LE::Shdr &,
StringRef);
template InputSection::InputSection(ObjFile<ELF32BE> &, const ELF32BE::Shdr &,
diff --git a/ELF/InputSection.h b/ELF/InputSection.h
index 58549c3..56b5412 100644
--- a/ELF/InputSection.h
+++ b/ELF/InputSection.h
@@ -18,8 +18,6 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Object/ELF.h"
-#include "llvm/Support/Threading.h"
-#include <mutex>
namespace lld {
namespace elf {
@@ -80,6 +78,8 @@
// section.
uint64_t getOffset(uint64_t Offset) const;
+ uint64_t getVA(uint64_t Offset) const;
+
protected:
SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags,
uint64_t Entsize, uint64_t Alignment, uint32_t Type,
@@ -249,18 +249,20 @@
// Returns the SectionPiece at a given input section offset.
SectionPiece *getSectionPiece(uint64_t Offset);
- const SectionPiece *getSectionPiece(uint64_t Offset) const;
+ const SectionPiece *getSectionPiece(uint64_t Offset) const {
+ return const_cast<MergeInputSection *>(this)->getSectionPiece(Offset);
+ }
SyntheticSection *getParent() const;
+ void initOffsetMap();
private:
void splitStrings(ArrayRef<uint8_t> A, size_t Size);
void splitNonStrings(ArrayRef<uint8_t> A, size_t Size);
- mutable llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
- mutable llvm::once_flag InitOffsetMap;
+ llvm::DenseMap<uint32_t, uint32_t> OffsetMap;
- llvm::DenseSet<uint64_t> LiveOffsets;
+ llvm::DenseSet<uint32_t> LiveOffsets;
};
struct EhSectionPiece {
diff --git a/ELF/LinkerScript.cpp b/ELF/LinkerScript.cpp
index bdc8a59..d3a3d29 100644
--- a/ELF/LinkerScript.cpp
+++ b/ELF/LinkerScript.cpp
@@ -112,14 +112,18 @@
"': overflowed by " + Twine(NewSize - MemRegion->Length) + " bytes");
}
-void LinkerScript::expandOutputSection(uint64_t Size) {
- Ctx->OutSec->Size += Size;
+void LinkerScript::expandMemoryRegions(uint64_t Size) {
if (Ctx->MemRegion)
expandMemoryRegion(Ctx->MemRegion, Size, Ctx->MemRegion->Name,
Ctx->OutSec->Name);
- // FIXME: check LMA region overflow too.
if (Ctx->LMARegion)
- Ctx->LMARegion->CurPos += Size;
+ expandMemoryRegion(Ctx->LMARegion, Size, Ctx->LMARegion->Name,
+ Ctx->OutSec->Name);
+}
+
+void LinkerScript::expandOutputSection(uint64_t Size) {
+ Ctx->OutSec->Size += Size;
+ expandMemoryRegions(Size);
}
void LinkerScript::setDot(Expr E, const Twine &Loc, bool InSec) {
@@ -203,6 +207,34 @@
Cmd->Provide = false;
}
+// This method is used to handle INSERT AFTER statement. Here we rebuild
+// the list of script commands to mix sections inserted into.
+void LinkerScript::processInsertCommands() {
+ std::vector<BaseCommand *> V;
+ auto Insert = [&](std::vector<BaseCommand *> &From) {
+ V.insert(V.end(), From.begin(), From.end());
+ From.clear();
+ };
+
+ for (BaseCommand *Base : SectionCommands) {
+ if (auto *OS = dyn_cast<OutputSection>(Base)) {
+ Insert(InsertBeforeCommands[OS->Name]);
+ V.push_back(Base);
+ Insert(InsertAfterCommands[OS->Name]);
+ continue;
+ }
+ V.push_back(Base);
+ }
+
+ for (auto &Cmds : {InsertBeforeCommands, InsertAfterCommands})
+ for (const std::pair<StringRef, std::vector<BaseCommand *>> &P : Cmds)
+ if (!P.second.empty())
+ error("unable to INSERT AFTER/BEFORE " + P.first +
+ ": section not defined");
+
+ SectionCommands = std::move(V);
+}
+
// Symbols defined in script should not be inlined by LTO. At the same time
// we don't know their final values until late stages of link. Here we scan
// over symbol assignment commands and create placeholder symbols if needed.
@@ -352,8 +384,11 @@
// For -emit-relocs we have to ignore entries like
// .rela.dyn : { *(.rela.data) }
// which are common because they are in the default bfd script.
- if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA)
- continue;
+ // We do not ignore SHT_REL[A] linker-synthesized sections here because
+ // want to support scripts that do custom layout for them.
+ if (auto *IS = dyn_cast<InputSection>(Sec))
+ if (IS->getRelocatedSection())
+ continue;
std::string Filename = getFilename(Sec->File);
if (!Cmd->FilePat.match(Filename) ||
@@ -377,9 +412,17 @@
void LinkerScript::discard(ArrayRef<InputSection *> V) {
for (InputSection *S : V) {
if (S == InX::ShStrTab || S == InX::Dynamic || S == InX::DynSymTab ||
- S == InX::DynStrTab)
+ S == InX::DynStrTab || S == InX::RelaPlt || S == InX::RelaDyn)
error("discarding " + S->Name + " section is not allowed");
+ // You can discard .hash and .gnu.hash sections by linker scripts. Since
+ // they are synthesized sections, we need to handle them differently than
+ // other regular sections.
+ if (S == InX::GnuHashTab)
+ InX::GnuHashTab = nullptr;
+ if (S == InX::HashTab)
+ InX::HashTab = nullptr;
+
S->Assigned = false;
S->Live = false;
discard(S->DependentSections);
@@ -593,11 +636,11 @@
void LinkerScript::addOrphanSections() {
unsigned End = SectionCommands.size();
StringMap<OutputSection *> Map;
-
std::vector<OutputSection *> V;
- for (InputSectionBase *S : InputSections) {
+
+ auto Add = [&](InputSectionBase *S) {
if (!S->Live || S->Parent)
- continue;
+ return;
StringRef Name = getOutputSectionName(S);
@@ -609,12 +652,24 @@
if (OutputSection *Sec =
findByName(makeArrayRef(SectionCommands).slice(0, End), Name)) {
Sec->addSection(cast<InputSection>(S));
- continue;
+ return;
}
if (OutputSection *OS = addInputSec(Map, S, Name))
V.push_back(OS);
- assert(S->getOutputSection()->SectionIndex == INT_MAX);
+ assert(S->getOutputSection()->SectionIndex == UINT32_MAX);
+ };
+
+ // For futher --emit-reloc handling code we need target output section
+ // to be created before we create relocation output section, so we want
+ // to create target sections first. We do not want priority handling
+ // for synthetic sections because them are special.
+ for (InputSectionBase *IS : InputSections) {
+ if (auto *Sec = dyn_cast<InputSection>(IS))
+ if (InputSectionBase *Rel = Sec->getRelocatedSection())
+ if (auto *RelIS = dyn_cast_or_null<InputSectionBase>(Rel->Parent))
+ Add(RelIS);
+ Add(IS);
}
// If no SECTIONS command was given, we should insert sections commands
@@ -655,9 +710,11 @@
void LinkerScript::switchTo(OutputSection *Sec) {
if (Ctx->OutSec == Sec)
return;
-
Ctx->OutSec = Sec;
+
+ uint64_t Before = advance(0, 1);
Ctx->OutSec->Addr = advance(0, Ctx->OutSec->Alignment);
+ expandMemoryRegions(Ctx->OutSec->Addr - Before);
}
// This function searches for a memory region to place the given output
@@ -731,7 +788,9 @@
for (BaseCommand *Base : Sec->SectionCommands) {
// This handles the assignments to symbol or to the dot.
if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
+ Cmd->Offset = Dot - Ctx->OutSec->Addr;
assignSymbol(Cmd, true);
+ Cmd->Size = Dot - Ctx->OutSec->Addr - Cmd->Offset;
continue;
}
@@ -785,7 +844,7 @@
for (BaseCommand *Base : Sec.SectionCommands)
if (!isa<InputSectionDescription>(*Base))
return false;
- return getInputSections(&Sec).empty();
+ return true;
}
void LinkerScript::adjustSectionsBeforeSorting() {
@@ -817,14 +876,18 @@
continue;
// A live output section means that some input section was added to it. It
- // might have been removed (gc, or empty synthetic section), but we at least
- // know the flags.
+ // might have been removed (if it was empty synthetic section), but we at
+ // least know the flags.
if (Sec->Live)
- Flags = Sec->Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR);
- else
- Sec->Flags = Flags;
+ Flags = Sec->Flags;
- if (isDiscardable(*Sec)) {
+ // We do not want to keep any special flags for output section
+ // in case it is empty.
+ bool IsEmpty = getInputSections(Sec).empty();
+ if (IsEmpty)
+ Sec->Flags = Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR);
+
+ if (IsEmpty && isDiscardable(*Sec)) {
Sec->Live = false;
Cmd = nullptr;
}
diff --git a/ELF/LinkerScript.h b/ELF/LinkerScript.h
index 4eddafa..f808849 100644
--- a/ELF/LinkerScript.h
+++ b/ELF/LinkerScript.h
@@ -86,8 +86,10 @@
// This represents ". = <expr>" or "<symbol> = <expr>".
struct SymbolAssignment : BaseCommand {
- SymbolAssignment(StringRef Name, Expr E, std::string Loc)
- : BaseCommand(AssignmentKind), Name(Name), Expression(E), Location(Loc) {}
+ SymbolAssignment(StringRef Name, Expr E, std::string Loc,
+ std::string CommandString)
+ : BaseCommand(AssignmentKind), Name(Name), Expression(E), Location(Loc),
+ CommandString(CommandString) {}
static bool classof(const BaseCommand *C) {
return C->Kind == AssignmentKind;
@@ -106,6 +108,16 @@
// Holds file name and line number for error reporting.
std::string Location;
+
+ // A string representation of this command. We use this for -Map.
+ std::string CommandString;
+
+ // This is just an offset of this assignment command in the output section.
+ unsigned Offset;
+
+ // Size of this assignment command. This is usually 0, but if you move '.'
+ // or use a BYTE()-family command, this may be greater than 0."
+ unsigned Size;
};
// Linker scripts allow additional constraints to be put on ouput sections.
@@ -178,11 +190,15 @@
// Represents BYTE(), SHORT(), LONG(), or QUAD().
struct ByteCommand : BaseCommand {
- ByteCommand(Expr E, unsigned Size)
- : BaseCommand(ByteKind), Expression(E), Size(Size) {}
+ ByteCommand(Expr E, unsigned Size, std::string CommandString)
+ : BaseCommand(ByteKind), CommandString(CommandString), Expression(E),
+ Size(Size) {}
static bool classof(const BaseCommand *C) { return C->Kind == ByteKind; }
+ // Keeps string representing the command. Used for -Map" is perhaps better.
+ std::string CommandString;
+
Expr Expression;
unsigned Offset;
unsigned Size;
@@ -216,6 +232,7 @@
void assignSymbol(SymbolAssignment *Cmd, bool InSec);
void setDot(Expr E, const Twine &Loc, bool InSec);
void expandOutputSection(uint64_t Size);
+ void expandMemoryRegions(uint64_t Size);
std::vector<InputSection *>
computeInputSections(const InputSectionDescription *);
@@ -267,6 +284,9 @@
void processSectionCommands();
void declareSymbols();
+ // Used to handle INSERT AFTER statements.
+ void processInsertCommands();
+
// SECTIONS command list.
std::vector<BaseCommand *> SectionCommands;
@@ -285,6 +305,11 @@
// A list of symbols referenced by the script.
std::vector<llvm::StringRef> ReferencedSymbols;
+
+ // Used to implement INSERT [AFTER|BEFORE]. Contains commands that need
+ // to be inserted into SECTIONS commands list.
+ llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertAfterCommands;
+ llvm::DenseMap<StringRef, std::vector<BaseCommand *>> InsertBeforeCommands;
};
extern LinkerScript *Script;
diff --git a/ELF/MapFile.cpp b/ELF/MapFile.cpp
index ae13f13..f5dcec8 100644
--- a/ELF/MapFile.cpp
+++ b/ELF/MapFile.cpp
@@ -28,6 +28,8 @@
#include "SyntheticSections.h"
#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -38,6 +40,9 @@
typedef DenseMap<const SectionBase *, SmallVector<Symbol *, 4>> SymbolMapTy;
+static const std::string Indent8 = " "; // 8 spaces
+static const std::string Indent16 = " "; // 16 spaces
+
// Print out the first three columns of a line.
static void writeHeader(raw_ostream &OS, uint64_t Addr, uint64_t Size,
uint64_t Align) {
@@ -45,8 +50,6 @@
OS << format("%0*llx %0*llx %5lld ", W, Addr, W, Size, Align);
}
-static std::string indent(int Depth) { return std::string(Depth * 8, ' '); }
-
// Returns a list of all symbols that we want to print out.
static std::vector<Symbol *> getSymbols() {
std::vector<Symbol *> V;
@@ -100,7 +103,7 @@
parallelForEachN(0, Syms.size(), [&](size_t I) {
raw_string_ostream OS(Str[I]);
writeHeader(OS, Syms[I]->getVA(), Syms[I]->getSize(), 0);
- OS << indent(2) << toString(*Syms[I]);
+ OS << Indent16 << toString(*Syms[I]);
});
DenseMap<Symbol *, std::string> Ret;
@@ -109,6 +112,43 @@
return Ret;
}
+// Print .eh_frame contents. Since the section consists of EhSectionPieces,
+// we need a specialized printer for that section.
+//
+// .eh_frame tend to contain a lot of section pieces that are contiguous
+// both in input file and output file. Such pieces are squashed before
+// being displayed to make output compact.
+static void printEhFrame(raw_ostream &OS, OutputSection *OSec) {
+ std::vector<EhSectionPiece> Pieces;
+
+ auto Add = [&](const EhSectionPiece &P) {
+ // If P is adjacent to Last, squash the two.
+ if (!Pieces.empty()) {
+ EhSectionPiece &Last = Pieces.back();
+ if (Last.Sec == P.Sec && Last.InputOff + Last.Size == P.InputOff &&
+ Last.OutputOff + Last.Size == P.OutputOff) {
+ Last.Size += P.Size;
+ return;
+ }
+ }
+ Pieces.push_back(P);
+ };
+
+ // Gather section pieces.
+ for (const CieRecord *Rec : InX::EhFrame->getCieRecords()) {
+ Add(*Rec->Cie);
+ for (const EhSectionPiece *Fde : Rec->Fdes)
+ Add(*Fde);
+ }
+
+ // Print out section pieces.
+ for (EhSectionPiece &P : Pieces) {
+ writeHeader(OS, OSec->Addr + P.OutputOff, P.Size, 0);
+ OS << Indent8 << toString(P.Sec->File) << ":(" << P.Sec->Name << "+0x"
+ << Twine::utohexstr(P.InputOff) + ")\n";
+ }
+}
+
void elf::writeMapFile() {
if (Config->MapFile.empty())
return;
@@ -137,11 +177,80 @@
OS << OSec->Name << '\n';
// Dump symbols for each input section.
- for (InputSection *IS : getInputSections(OSec)) {
- writeHeader(OS, OSec->Addr + IS->OutSecOff, IS->getSize(), IS->Alignment);
- OS << indent(1) << toString(IS) << '\n';
- for (Symbol *Sym : SectionSyms[IS])
- OS << SymStr[Sym] << '\n';
+ for (BaseCommand *Base : OSec->SectionCommands) {
+ if (auto *ISD = dyn_cast<InputSectionDescription>(Base)) {
+ for (InputSection *IS : ISD->Sections) {
+ if (IS == InX::EhFrame) {
+ printEhFrame(OS, OSec);
+ continue;
+ }
+
+ writeHeader(OS, IS->getVA(0), IS->getSize(), IS->Alignment);
+ OS << Indent8 << toString(IS) << '\n';
+ for (Symbol *Sym : SectionSyms[IS])
+ OS << SymStr[Sym] << '\n';
+ }
+ continue;
+ }
+
+ if (auto *Cmd = dyn_cast<ByteCommand>(Base)) {
+ writeHeader(OS, OSec->Addr + Cmd->Offset, Cmd->Size, 1);
+ OS << Indent8 << Cmd->CommandString << '\n';
+ continue;
+ }
+
+ if (auto *Cmd = dyn_cast<SymbolAssignment>(Base)) {
+ writeHeader(OS, OSec->Addr + Cmd->Offset, Cmd->Size, 1);
+ OS << Indent8 << Cmd->CommandString << '\n';
+ continue;
+ }
}
}
}
+
+static void print(StringRef A, StringRef B) {
+ outs() << left_justify(A, 49) << " " << B << "\n";
+}
+
+// Output a cross reference table to stdout. This is for --cref.
+//
+// For each global symbol, we print out a file that defines the symbol
+// followed by files that uses that symbol. Here is an example.
+//
+// strlen /lib/x86_64-linux-gnu/libc.so.6
+// tools/lld/tools/lld/CMakeFiles/lld.dir/lld.cpp.o
+// lib/libLLVMSupport.a(PrettyStackTrace.cpp.o)
+//
+// In this case, strlen is defined by libc.so.6 and used by other two
+// files.
+void elf::writeCrossReferenceTable() {
+ if (!Config->Cref)
+ return;
+
+ // Collect symbols and files.
+ MapVector<Symbol *, SetVector<InputFile *>> Map;
+ for (InputFile *File : ObjectFiles) {
+ for (Symbol *Sym : File->getSymbols()) {
+ if (isa<SharedSymbol>(Sym))
+ Map[Sym].insert(File);
+ if (auto *D = dyn_cast<Defined>(Sym))
+ if (!D->isLocal() && (!D->Section || D->Section->Live))
+ Map[D].insert(File);
+ }
+ }
+
+ // Print out a header.
+ outs() << "Cross Reference Table\n\n";
+ print("Symbol", "File");
+
+ // Print out a table.
+ for (auto KV : Map) {
+ Symbol *Sym = KV.first;
+ SetVector<InputFile *> &Files = KV.second;
+
+ print(toString(*Sym), toString(Sym->File));
+ for (InputFile *File : Files)
+ if (File != Sym->File)
+ print("", toString(File));
+ }
+}
diff --git a/ELF/MapFile.h b/ELF/MapFile.h
index 2d93e26..0282425 100644
--- a/ELF/MapFile.h
+++ b/ELF/MapFile.h
@@ -13,6 +13,7 @@
namespace lld {
namespace elf {
void writeMapFile();
+void writeCrossReferenceTable();
} // namespace elf
} // namespace lld
diff --git a/ELF/Options.td b/ELF/Options.td
index b1c7a1d..82a2a1c 100644
--- a/ELF/Options.td
+++ b/ELF/Options.td
@@ -75,6 +75,10 @@
def color_diagnostics_eq: J<"color-diagnostics=">,
HelpText<"Use colors in diagnostics">;
+defm cref: B<"cref",
+ "Output cross reference table",
+ "Do not output cross reference table">;
+
defm define_common: B<"define-common",
"Assign space to common symbols",
"Do not assign space to common symbols">;
@@ -180,6 +184,8 @@
defm init: Eq<"init">, HelpText<"Specify an initializer function">,
MetaVarName<"<symbol>">;
+defm just_symbols: Eq<"just-symbols">, HelpText<"Just link symbols">;
+
defm library: Eq<"library">, HelpText<"Root name of library to use">,
MetaVarName<"<libName>">;
@@ -418,7 +424,6 @@
// Options listed below are silently ignored for now for compatibility.
def allow_shlib_undefined: F<"allow-shlib-undefined">;
-def cref: F<"cref">;
def detect_odr_violations: F<"detect-odr-violations">;
def g: Flag<["-"], "g">;
def long_plt: F<"long-plt">;
diff --git a/ELF/OutputSections.cpp b/ELF/OutputSections.cpp
index 9d5ebe5..8e9e757 100644
--- a/ELF/OutputSections.cpp
+++ b/ELF/OutputSections.cpp
@@ -25,7 +25,6 @@
using namespace llvm;
using namespace llvm::dwarf;
using namespace llvm::object;
-using namespace llvm::support::endian;
using namespace llvm::ELF;
using namespace lld;
@@ -72,9 +71,7 @@
OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
: BaseCommand(OutputSectionKind),
SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
- /*Info*/ 0,
- /*Link*/ 0),
- SectionIndex(INT_MAX) {
+ /*Info*/ 0, /*Link*/ 0) {
Live = false;
}
@@ -100,7 +97,8 @@
Flags = IS->Flags;
} else {
// Otherwise, check if new type or flags are compatible with existing ones.
- if ((Flags & (SHF_ALLOC | SHF_TLS)) != (IS->Flags & (SHF_ALLOC | SHF_TLS)))
+ unsigned Mask = SHF_ALLOC | SHF_TLS | SHF_LINK_ORDER;
+ if ((Flags & Mask) != (IS->Flags & Mask))
error("incompatible section flags for " + Name + "\n>>> " + toString(IS) +
": 0x" + utohexstr(IS->Flags) + "\n>>> output section " + Name +
": 0x" + utohexstr(Flags));
@@ -214,11 +212,11 @@
if (Size == 1)
*Buf = Data;
else if (Size == 2)
- write16(Buf, Data, Config->Endianness);
+ write16(Buf, Data);
else if (Size == 4)
- write32(Buf, Data, Config->Endianness);
+ write32(Buf, Data);
else if (Size == 8)
- write64(Buf, Data, Config->Endianness);
+ write64(Buf, Data);
else
llvm_unreachable("unsupported Size argument");
}
@@ -367,8 +365,6 @@
assert(Y.startswith(".ctors") || Y.startswith(".dtors"));
X = X.substr(6);
Y = Y.substr(6);
- if (X.empty() && Y.empty())
- return false;
return X < Y;
}
diff --git a/ELF/OutputSections.h b/ELF/OutputSections.h
index ce6bff3..514b9da 100644
--- a/ELF/OutputSections.h
+++ b/ELF/OutputSections.h
@@ -51,7 +51,7 @@
uint64_t getLMA() const { return PtLoad ? Addr + PtLoad->LMAOffset : Addr; }
template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
- unsigned SectionIndex;
+ uint32_t SectionIndex = UINT32_MAX;
unsigned SortRank;
uint32_t getPhdrFlags() const;
diff --git a/ELF/Relocations.cpp b/ELF/Relocations.cpp
index bf67e61..bc4f4b2 100644
--- a/ELF/Relocations.cpp
+++ b/ELF/Relocations.cpp
@@ -814,7 +814,8 @@
error(
"can't create dynamic relocation " + toString(Type) + " against " +
(Sym.getName().empty() ? "local symbol" : "symbol: " + toString(Sym)) +
- " in readonly segment; recompile object files with -fPIC" +
+ " in readonly segment; recompile object files with -fPIC "
+ "or pass '-Wl,-z,notext' to allow text relocations in the output" +
getLocation(Sec, Sym, Offset));
return Expr;
}
@@ -875,6 +876,17 @@
// that points to the real function is a dedicated got entry used by the
// plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT,
// R_386_JMP_SLOT, etc).
+
+ // For position independent executable on i386, the plt entry requires ebx
+ // to be set. This causes two problems:
+ // * If some code has a direct reference to a function, it was probably
+ // compiled without -fPIE/-fPIC and doesn't maintain ebx.
+ // * If a library definition gets preempted to the executable, it will have
+ // the wrong ebx value.
+ if (Config->Pie && Config->EMachine == EM_386)
+ errorOrWarn("symbol '" + toString(Sym) +
+ "' cannot be preempted; recompile with -fPIE" +
+ getLocation(Sec, Sym, Offset));
Sym.NeedsPltAddr = true;
Expr = toPlt(Expr);
Sec.Relocations.push_back({Expr, Type, Offset, Addend, &Sym});
@@ -1206,17 +1218,30 @@
//
// We follow a simple but conservative heuristic to place ThunkSections at
// offsets that are multiples of a Target specific branch range.
-// For an InputSectionRange that is smaller than the range, a single
+// For an InputSectionDescription that is smaller than the range, a single
// ThunkSection at the end of the range will do.
+//
+// For an InputSectionDescription that is more than twice the size of the range,
+// we place the last ThunkSection at range bytes from the end of the
+// InputSectionDescription in order to increase the likelihood that the
+// distance from a thunk to its target will be sufficiently small to
+// allow for the creation of a short thunk.
void ThunkCreator::createInitialThunkSections(
ArrayRef<OutputSection *> OutputSections) {
forEachInputSectionDescription(
OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
if (ISD->Sections.empty())
return;
+ uint32_t ISDBegin = ISD->Sections.front()->OutSecOff;
+ uint32_t ISDEnd =
+ ISD->Sections.back()->OutSecOff + ISD->Sections.back()->getSize();
+ uint32_t LastThunkLowerBound = -1;
+ if (ISDEnd - ISDBegin > Target->ThunkSectionSpacing * 2)
+ LastThunkLowerBound = ISDEnd - Target->ThunkSectionSpacing;
+
uint32_t ISLimit;
- uint32_t PrevISLimit = ISD->Sections.front()->OutSecOff;
- uint32_t ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
+ uint32_t PrevISLimit = ISDBegin;
+ uint32_t ThunkUpperBound = ISDBegin + Target->ThunkSectionSpacing;
for (const InputSection *IS : ISD->Sections) {
ISLimit = IS->OutSecOff + IS->getSize();
@@ -1224,6 +1249,8 @@
addThunkSection(OS, ISD, PrevISLimit);
ThunkUpperBound = PrevISLimit + Target->ThunkSectionSpacing;
}
+ if (ISLimit > LastThunkLowerBound)
+ break;
PrevISLimit = ISLimit;
}
addThunkSection(OS, ISD, ISLimit);
@@ -1240,17 +1267,22 @@
std::pair<Thunk *, bool> ThunkCreator::getThunk(Symbol &Sym, RelType Type,
uint64_t Src) {
- auto Res = ThunkedSymbols.insert({&Sym, std::vector<Thunk *>()});
- if (!Res.second) {
- // Check existing Thunks for Sym to see if they can be reused
- for (Thunk *ET : Res.first->second)
- if (ET->isCompatibleWith(Type) &&
- Target->inBranchRange(Type, Src, ET->ThunkSym->getVA()))
- return std::make_pair(ET, false);
- }
+ std::vector<Thunk *> *ThunkVec = nullptr;
+ // We use (section, offset) pair to find the thunk position if possible so
+ // that we create only one thunk for aliased symbols or ICFed sections.
+ if (auto *D = dyn_cast<Defined>(&Sym))
+ if (!D->isInPlt() && D->Section)
+ ThunkVec = &ThunkedSymbolsBySection[{D->Section->Repl, D->Value}];
+ if (!ThunkVec)
+ ThunkVec = &ThunkedSymbols[&Sym];
+ // Check existing Thunks for Sym to see if they can be reused
+ for (Thunk *ET : *ThunkVec)
+ if (ET->isCompatibleWith(Type) &&
+ Target->inBranchRange(Type, Src, ET->getThunkTargetSym()->getVA()))
+ return std::make_pair(ET, false);
// No existing compatible Thunk in range, create a new one
Thunk *T = addThunk(Type, Sym);
- Res.first->second.push_back(T);
+ ThunkVec->push_back(T);
return std::make_pair(T, true);
}
@@ -1326,7 +1358,7 @@
OutputSections, [&](OutputSection *OS, InputSectionDescription *ISD) {
for (InputSection *IS : ISD->Sections)
for (Relocation &Rel : IS->Relocations) {
- uint64_t Src = OS->Addr + IS->OutSecOff + Rel.Offset;
+ uint64_t Src = IS->getVA(Rel.Offset);
// If we are a relocation to an existing Thunk, check if it is
// still in range. If not then Rel will be altered to point to its
@@ -1341,7 +1373,6 @@
bool IsNew;
std::tie(T, IsNew) = getThunk(*Rel.Sym, Rel.Type, Src);
if (IsNew) {
- AddressesChanged = true;
// Find or create a ThunkSection for the new Thunk
ThunkSection *TS;
if (auto *TIS = T->getTargetInputSection())
@@ -1349,13 +1380,18 @@
else
TS = getISDThunkSec(OS, IS, ISD, Rel.Type, Src);
TS->addThunk(T);
- Thunks[T->ThunkSym] = T;
+ Thunks[T->getThunkTargetSym()] = T;
}
// Redirect relocation to Thunk, we never go via the PLT to a Thunk
- Rel.Sym = T->ThunkSym;
+ Rel.Sym = T->getThunkTargetSym();
Rel.Expr = fromPlt(Rel.Expr);
}
+ for (auto &P : ISD->ThunkSections)
+ AddressesChanged |= P.first->assignOffsets();
});
+ for (auto &P : ThunkedSections)
+ AddressesChanged |= P.second->assignOffsets();
+
// Merge all created synthetic ThunkSections back into OutputSection
mergeThunks(OutputSections);
++Pass;
diff --git a/ELF/Relocations.h b/ELF/Relocations.h
index 7b142e1..69afe31 100644
--- a/ELF/Relocations.h
+++ b/ELF/Relocations.h
@@ -21,7 +21,7 @@
class InputSection;
class InputSectionBase;
class OutputSection;
-class OutputSection;
+class SectionBase;
// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
typedef uint32_t RelType;
@@ -161,6 +161,8 @@
bool normalizeExistingThunk(Relocation &Rel, uint64_t Src);
// Record all the available Thunks for a Symbol
+ llvm::DenseMap<std::pair<SectionBase *, uint64_t>, std::vector<Thunk *>>
+ ThunkedSymbolsBySection;
llvm::DenseMap<Symbol *, std::vector<Thunk *>> ThunkedSymbols;
// Find a Thunk from the Thunks symbol definition, we can use this to find
diff --git a/ELF/ScriptParser.cpp b/ELF/ScriptParser.cpp
index c68c25a..ff650d8 100644
--- a/ELF/ScriptParser.cpp
+++ b/ELF/ScriptParser.cpp
@@ -267,7 +267,8 @@
Expr E = readExpr();
if (!atEOF())
setError("EOF expected, but got " + next());
- SymbolAssignment *Cmd = make<SymbolAssignment>(Name, E, getCurrentLocation());
+ SymbolAssignment *Cmd = make<SymbolAssignment>(Name, E, getCurrentLocation(),
+ "" /*CommandString*/);
Script->SectionCommands.push_back(Cmd);
}
@@ -436,6 +437,7 @@
Config->SingleRoRx = true;
expect("{");
+ std::vector<BaseCommand *> V;
while (!errorCount() && !consume("}")) {
StringRef Tok = next();
BaseCommand *Cmd = readProvideOrAssignment(Tok);
@@ -445,17 +447,34 @@
else
Cmd = readOutputSectionDescription(Tok);
}
- Script->SectionCommands.push_back(Cmd);
+ V.push_back(Cmd);
}
+
+ if (!atEOF() && consume("INSERT")) {
+ std::vector<BaseCommand *> *Dest = nullptr;
+ if (consume("AFTER"))
+ Dest = &Script->InsertAfterCommands[next()];
+ else if (consume("BEFORE"))
+ Dest = &Script->InsertBeforeCommands[next()];
+ else
+ setError("expected AFTER/BEFORE, but got '" + next() + "'");
+ if (Dest)
+ Dest->insert(Dest->end(), V.begin(), V.end());
+ return;
+ }
+
+ Script->SectionCommands.insert(Script->SectionCommands.end(), V.begin(),
+ V.end());
}
static int precedence(StringRef Op) {
return StringSwitch<int>(Op)
- .Cases("*", "/", "%", 5)
- .Cases("+", "-", 4)
- .Cases("<<", ">>", 3)
- .Cases("<", "<=", ">", ">=", "==", "!=", 2)
- .Cases("&", "|", 1)
+ .Cases("*", "/", "%", 6)
+ .Cases("+", "-", 5)
+ .Cases("<<", ">>", 4)
+ .Cases("<", "<=", ">", ">=", "==", "!=", 3)
+ .Case("&", 2)
+ .Case("|", 1)
.Default(-1);
}
@@ -774,6 +793,7 @@
}
SymbolAssignment *ScriptParser::readAssignment(StringRef Name) {
+ size_t OldPos = Pos;
StringRef Op = next();
assert(Op == "=" || Op == "+=");
Expr E = readExpr();
@@ -781,7 +801,11 @@
std::string Loc = getCurrentLocation();
E = [=] { return add(Script->getSymbolValue(Name, Loc), E()); };
}
- return make<SymbolAssignment>(Name, E, getCurrentLocation());
+
+ std::string CommandString =
+ Name.str() + " " +
+ llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
+ return make<SymbolAssignment>(Name, E, getCurrentLocation(), CommandString);
}
// This is an operator-precedence parser to parse a linker
@@ -935,7 +959,13 @@
.Default(-1);
if (Size == -1)
return nullptr;
- return make<ByteCommand>(readParenExpr(), Size);
+
+ size_t OldPos = Pos;
+ Expr E = readParenExpr();
+ std::string CommandString =
+ Tok.str() + " " +
+ llvm::join(Tokens.begin() + OldPos, Tokens.begin() + Pos, " ");
+ return make<ByteCommand>(E, Size, CommandString);
}
StringRef ScriptParser::readParenLiteral() {
@@ -1067,6 +1097,16 @@
return Cmd->getLMA();
};
}
+ if (Tok == "MAX" || Tok == "MIN") {
+ expect("(");
+ Expr A = readExpr();
+ expect(",");
+ Expr B = readExpr();
+ expect(")");
+ if (Tok == "MIN")
+ return [=] { return std::min(A().getValue(), B().getValue()); };
+ return [=] { return std::max(A().getValue(), B().getValue()); };
+ }
if (Tok == "ORIGIN") {
StringRef Name = readParenLiteral();
if (Script->MemoryRegions.count(Name) == 0) {
@@ -1310,11 +1350,10 @@
uint64_t Length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
- if (Script->MemoryRegions.count(Name))
- setError("region '" + Name + "' already defined");
MemoryRegion *MR =
make<MemoryRegion>(Name, Origin, Length, Flags, NegFlags);
- Script->MemoryRegions[Name] = MR;
+ if (!Script->MemoryRegions.insert({Name, MR}).second)
+ setError("region '" + Name + "' already defined");
}
}
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp
index eaf7005..40c7d7f 100644
--- a/ELF/SymbolTable.cpp
+++ b/ELF/SymbolTable.cpp
@@ -130,7 +130,10 @@
for (InputFile *File : LTO->compile()) {
DenseSet<CachedHashStringRef> DummyGroups;
- cast<ObjFile<ELFT>>(File)->parse(DummyGroups);
+ auto *Obj = cast<ObjFile<ELFT>>(File);
+ Obj->parse(DummyGroups);
+ for (Symbol *Sym : Obj->getGlobalSymbols())
+ Sym->parseSymbolVersion();
ObjectFiles.push_back(File);
}
}
@@ -410,20 +413,17 @@
return S;
}
-static void warnOrError(const Twine &Msg) {
- if (Config->AllowMultipleDefinition)
- warn(Msg);
- else
- error(Msg);
-}
-
static void reportDuplicate(Symbol *Sym, InputFile *NewFile) {
- warnOrError("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
- toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
+ if (!Config->AllowMultipleDefinition)
+ error("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " +
+ toString(Sym->File) + "\n>>> defined in " + toString(NewFile));
}
static void reportDuplicate(Symbol *Sym, InputFile *NewFile,
InputSectionBase *ErrSec, uint64_t ErrOffset) {
+ if (Config->AllowMultipleDefinition)
+ return;
+
Defined *D = cast<Defined>(Sym);
if (!D->Section || !ErrSec) {
reportDuplicate(Sym, NewFile);
@@ -450,7 +450,7 @@
if (!Src2.empty())
Msg += Src2 + "\n>>> ";
Msg += Obj2;
- warnOrError(Msg);
+ error(Msg);
}
Symbol *SymbolTable::addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
diff --git a/ELF/Symbols.cpp b/ELF/Symbols.cpp
index 58ed899..8ac3e5f 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -56,7 +56,6 @@
if (!IS)
return D.Value;
- IS = IS->Repl;
uint64_t Offset = D.Value;
// An object in an SHF_MERGE section might be referenced via a
@@ -75,8 +74,6 @@
Addend = 0;
}
- const OutputSection *OutSec = IS->getOutputSection();
-
// In the typical case, this is actually very simple and boils
// down to adding together 3 numbers:
// 1. The address of the output section.
@@ -87,7 +84,7 @@
// If you understand the data structures involved with this next
// line (and how they get built), then you have a pretty good
// understanding of the linker.
- uint64_t VA = (OutSec ? OutSec->Addr : 0) + IS->getOffset(Offset);
+ uint64_t VA = IS->getVA(Offset);
if (D.isTls() && !Config->Relocatable) {
if (!Out::TlsPhdr)
@@ -100,7 +97,7 @@
case Symbol::SharedKind: {
auto &SS = cast<SharedSymbol>(Sym);
if (SS.CopyRelSec)
- return SS.CopyRelSec->getParent()->Addr + SS.CopyRelSec->OutSecOff;
+ return SS.CopyRelSec->getVA(0);
if (SS.NeedsPltAddr)
return Sym.getPltVA();
return 0;
@@ -139,8 +136,7 @@
uint64_t Symbol::getPltVA() const {
if (this->IsInIplt)
return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
- return InX::Plt->getVA() + Target->PltHeaderSize +
- PltIndex * Target->PltEntrySize;
+ return InX::Plt->getVA() + Target->getPltEntryOffset(PltIndex);
}
uint64_t Symbol::getSize() const {
@@ -154,7 +150,7 @@
OutputSection *Symbol::getOutputSection() const {
if (auto *S = dyn_cast<Defined>(this)) {
if (auto *Sec = S->Section)
- return Sec->Repl->getOutputSection();
+ return Sec->getOutputSection();
return nullptr;
}
diff --git a/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
index 6057bd8..5c214d0 100644
--- a/ELF/SyntheticSections.cpp
+++ b/ELF/SyntheticSections.cpp
@@ -47,16 +47,14 @@
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
-using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
-constexpr size_t MergeNoTailSection::NumShards;
+using llvm::support::endian::write32le;
+using llvm::support::endian::write64le;
-static void write32(void *Buf, uint32_t Val) {
- endian::write32(Buf, Val, Config->Endianness);
-}
+constexpr size_t MergeNoTailSection::NumShards;
uint64_t SyntheticSection::getVA() const {
if (OutputSection *Sec = getParent())
@@ -67,7 +65,7 @@
// Returns an LLD version string.
static ArrayRef<uint8_t> getVersion() {
// Check LLD_VERSION first for ease of testing.
- // You can get consitent output by using the environment variable.
+ // You can get consistent output by using the environment variable.
// This is only for testing.
StringRef S = getenv("LLD_VERSION");
if (S.empty())
@@ -266,8 +264,8 @@
return Sec;
}
-Symbol *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
- uint64_t Size, InputSectionBase &Section) {
+Defined *elf::addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
+ uint64_t Size, InputSectionBase &Section) {
auto *S = make<Defined>(Section.File, Name, STB_LOCAL, STV_DEFAULT, Type,
Value, Size, &Section);
if (InX::SymTab)
@@ -381,7 +379,7 @@
template <class ELFT, class RelTy>
CieRecord *EhFrameSection::addCie(EhSectionPiece &Cie, ArrayRef<RelTy> Rels) {
auto *Sec = cast<EhInputSection>(Cie.Sec);
- if (read32(Cie.data().data() + 4, Config->Endianness) != 0)
+ if (read32(Cie.data().data() + 4) != 0)
fatal(toString(Sec) + ": CIE expected at beginning of .eh_frame");
Symbol *Personality = nullptr;
@@ -440,7 +438,7 @@
return;
size_t Offset = Piece.InputOff;
- uint32_t ID = read32(Piece.data().data() + 4, Config->Endianness);
+ uint32_t ID = read32(Piece.data().data() + 4);
if (ID == 0) {
OffsetToCie[Offset] = addCie<ELFT>(Piece, Rels);
continue;
@@ -538,11 +536,11 @@
static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
switch (Size) {
case DW_EH_PE_udata2:
- return read16(Buf, Config->Endianness);
+ return read16(Buf);
case DW_EH_PE_udata4:
- return read32(Buf, Config->Endianness);
+ return read32(Buf);
case DW_EH_PE_udata8:
- return read64(Buf, Config->Endianness);
+ return read64(Buf);
case DW_EH_PE_absptr:
return readUint(Buf);
}
@@ -589,7 +587,15 @@
GotSection::GotSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- Target->GotEntrySize, ".got") {}
+ Target->GotEntrySize, ".got") {
+ // PPC64 saves the ElfSym::GlobalOffsetTable .TOC. as the first entry in the
+ // .got. If there are no references to .TOC. in the symbol table,
+ // ElfSym::GlobalOffsetTable will not be defined and we won't need to save
+ // .TOC. in the .got. When it is defined, we increase NumEntries by the number
+ // of entries used to emit ElfSym::GlobalOffsetTable.
+ if (ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt)
+ NumEntries += Target->GotHeaderEntriesNum;
+}
void GotSection::addEntry(Symbol &Sym) {
Sym.GotIndex = NumEntries;
@@ -623,19 +629,24 @@
return B.GlobalDynIndex * Config->Wordsize;
}
-void GotSection::finalizeContents() { Size = NumEntries * Config->Wordsize; }
+void GotSection::finalizeContents() {
+ Size = NumEntries * Config->Wordsize;
+}
bool GotSection::empty() const {
// We need to emit a GOT even if it's empty if there's a relocation that is
// relative to GOT(such as GOTOFFREL) or there's a symbol that points to a GOT
- // (i.e. _GLOBAL_OFFSET_TABLE_).
- return NumEntries == 0 && !HasGotOffRel && !ElfSym::GlobalOffsetTable;
+ // (i.e. _GLOBAL_OFFSET_TABLE_) that the target defines relative to the .got.
+ return NumEntries == 0 && !HasGotOffRel &&
+ !(ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt);
}
void GotSection::writeTo(uint8_t *Buf) {
// Buf points to the start of this section's buffer,
// whereas InputSectionBase::relocateAlloc() expects its argument
// to point to the start of the output section.
+ Target->writeGotHeader(Buf);
+ Buf += Target->GotHeaderEntriesNum * Target->GotEntrySize;
relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + Size);
}
@@ -900,6 +911,14 @@
}
}
+bool GotPltSection::empty() const {
+ // We need to emit a GOT.PLT even if it's empty if there's a symbol that
+ // references the _GLOBAL_OFFSET_TABLE_ and the Target defines the symbol
+ // relative to the .got.plt section.
+ return Entries.empty() &&
+ !(ElfSym::GlobalOffsetTable && Target->GotBaseSymInGotPlt);
+}
+
// On ARM the IgotPltSection is part of the GotSection, on other Targets it is
// part of the .got.plt
IgotPltSection::IgotPltSection()
@@ -1005,8 +1024,7 @@
template <class ELFT>
void DynamicSection<ELFT>::addInSec(int32_t Tag, InputSection *Sec) {
- Entries.push_back(
- {Tag, [=] { return Sec->getParent()->Addr + Sec->OutSecOff; }});
+ Entries.push_back({Tag, [=] { return Sec->getVA(0); }});
}
template <class ELFT>
@@ -1183,7 +1201,7 @@
}
uint64_t DynamicReloc::getOffset() const {
- return InputSec->getOutputSection()->Addr + InputSec->getOffset(OffsetInSec);
+ return InputSec->getVA(OffsetInSec);
}
int64_t DynamicReloc::computeAddend() const {
@@ -1722,7 +1740,7 @@
write32(Buf, NBuckets);
write32(Buf + 4, InX::DynSymTab->getNumSymbols() - Symbols.size());
write32(Buf + 8, MaskWords);
- write32(Buf + 12, getShift2());
+ write32(Buf + 12, Shift2);
Buf += 16;
// Write a bloom filter and a hash table.
@@ -1744,7 +1762,7 @@
size_t I = (Sym.Hash / C) & (MaskWords - 1);
uint64_t Val = readUint(Buf + I * Config->Wordsize);
Val |= uint64_t(1) << (Sym.Hash % C);
- Val |= uint64_t(1) << ((Sym.Hash >> getShift2()) % C);
+ Val |= uint64_t(1) << ((Sym.Hash >> Shift2) % C);
writeUint(Buf + I * Config->Wordsize, Val);
}
}
@@ -1792,15 +1810,21 @@
return SS->CopyRelSec == nullptr && !SS->NeedsPltAddr;
return !S.Sym->isDefined();
});
- if (Mid == V.end())
- return;
// We chose load factor 4 for the on-disk hash table. For each hash
// collision, the dynamic linker will compare a uint32_t hash value.
- // Since the integer comparison is quite fast, we believe we can make
- // the load factor even larger. 4 is just a conservative choice.
+ // Since the integer comparison is quite fast, we believe we can
+ // make the load factor even larger. 4 is just a conservative choice.
+ //
+ // Note that we don't want to create a zero-sized hash table because
+ // Android loader as of 2018 doesn't like a .gnu.hash containing such
+ // table. If that's the case, we create a hash table with one unused
+ // dummy slot.
NBuckets = std::max<size_t>((V.end() - Mid) / 4, 1);
+ if (Mid == V.end())
+ return;
+
for (SymbolTableEntry &Ent : llvm::make_range(Mid, V.end())) {
Symbol *B = Ent.Sym;
uint32_t Hash = hashGnu(B->getName());
@@ -2128,8 +2152,7 @@
// Write the address area.
for (GdbIndexChunk &D : Chunks) {
for (GdbIndexChunk::AddressEntry &E : D.AddressAreas) {
- uint64_t BaseAddr =
- E.Section->getParent()->Addr + E.Section->getOffset(0);
+ uint64_t BaseAddr = E.Section->getVA(0);
write64le(Buf, BaseAddr + E.LowAddress);
write64le(Buf + 8, BaseAddr + E.HighAddress);
write32le(Buf + 16, E.CuIndex);
@@ -2312,8 +2335,7 @@
template <class ELFT>
void VersionNeedSection<ELFT>::addSymbol(SharedSymbol *SS) {
SharedFile<ELFT> &File = SS->getFile<ELFT>();
- const typename ELFT::Verdef *Ver = File.Verdefs[SS->VerdefIndex];
- if (!Ver) {
+ if (SS->VerdefIndex == VER_NDX_GLOBAL) {
SS->VersionId = VER_NDX_GLOBAL;
return;
}
@@ -2323,7 +2345,9 @@
// for the soname.
if (File.VerdefMap.empty())
Needed.push_back({&File, InX::DynStrTab->addString(File.SoName)});
+ const typename ELFT::Verdef *Ver = File.Verdefs[SS->VerdefIndex];
typename SharedFile<ELFT>::NeededVer &NV = File.VerdefMap[Ver];
+
// If we don't already know that we need an Elf_Vernaux for this Elf_Verdef,
// prepare to create one by allocating a version identifier and creating a
// dynstr entry for the version name.
@@ -2549,8 +2573,11 @@
}
(*I)->addSection(MS);
}
- for (auto *MS : MergeSections)
+ for (auto *MS : MergeSections) {
MS->finalizeContents();
+ parallelForEach(MS->Sections,
+ [](MergeInputSection *Sec) { Sec->initOffsetMap(); });
+ }
std::vector<InputSectionBase *> &V = InputSections;
V.erase(std::remove(V.begin(), V.end(), nullptr), V.end());
@@ -2572,8 +2599,7 @@
// address described by any other table entry.
void ARMExidxSentinelSection::writeTo(uint8_t *Buf) {
assert(Highest);
- uint64_t S =
- Highest->getParent()->Addr + Highest->getOffset(Highest->getSize());
+ uint64_t S = Highest->getVA(Highest->getSize());
uint64_t P = getVA();
Target->relocateOne(Buf, R_ARM_PREL31, S - P);
write32le(Buf + 4, 1);
@@ -2595,16 +2621,13 @@
}
void ThunkSection::addThunk(Thunk *T) {
- uint64_t Off = alignTo(Size, T->Alignment);
- T->Offset = Off;
Thunks.push_back(T);
T->addSymbols(*this);
- Size = Off + T->size();
}
void ThunkSection::writeTo(uint8_t *Buf) {
- for (const Thunk *T : Thunks)
- T->writeTo(Buf + T->Offset, *this);
+ for (Thunk *T : Thunks)
+ T->writeTo(Buf + T->Offset);
}
InputSection *ThunkSection::getTargetInputSection() const {
@@ -2614,6 +2637,20 @@
return T->getTargetInputSection();
}
+bool ThunkSection::assignOffsets() {
+ uint64_t Off = 0;
+ for (Thunk *T : Thunks) {
+ Off = alignTo(Off, T->Alignment);
+ T->setOffset(Off);
+ uint32_t Size = T->size();
+ T->getThunkTargetSym()->Size = Size;
+ Off += Size;
+ }
+ bool Changed = Off != Size;
+ Size = Off;
+ return Changed;
+}
+
InputSection *InX::ARMAttributes;
BssSection *InX::Bss;
BssSection *InX::BssRelRo;
diff --git a/ELF/SyntheticSections.h b/ELF/SyntheticSections.h
index 855b91d..b709f4b 100644
--- a/ELF/SyntheticSections.h
+++ b/ELF/SyntheticSections.h
@@ -30,6 +30,7 @@
namespace lld {
namespace elf {
+class Defined;
class SharedSymbol;
class SyntheticSection : public InputSection {
@@ -83,6 +84,7 @@
};
std::vector<FdeData> getFdeData() const;
+ ArrayRef<CieRecord *> getCieRecords() const { return CieRecords; }
private:
uint64_t Size = 0;
@@ -269,7 +271,7 @@
void addEntry(Symbol &Sym);
size_t getSize() const override;
void writeTo(uint8_t *Buf) override;
- bool empty() const override { return Entries.empty(); }
+ bool empty() const override;
private:
std::vector<const Symbol *> Entries;
@@ -470,7 +472,7 @@
void addSymbols(std::vector<SymbolTableEntry> &Symbols);
private:
- size_t getShift2() const { return Config->Is64 ? 6 : 5; }
+ enum { Shift2 = 6 };
void writeBloomFilter(uint8_t *Buf);
void writeHashTable(uint8_t *Buf);
@@ -683,13 +685,12 @@
class MergeSyntheticSection : public SyntheticSection {
public:
void addSection(MergeInputSection *MS);
+ std::vector<MergeInputSection *> Sections;
protected:
MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
uint32_t Alignment)
: SyntheticSection(Flags, Type, Alignment, Name) {}
-
- std::vector<MergeInputSection *> Sections;
};
class MergeTailSection final : public MergeSyntheticSection {
@@ -823,9 +824,10 @@
size_t getSize() const override { return Size; }
void writeTo(uint8_t *Buf) override;
InputSection *getTargetInputSection() const;
+ bool assignOffsets();
private:
- std::vector<const Thunk *> Thunks;
+ std::vector<Thunk *> Thunks;
size_t Size = 0;
};
@@ -834,8 +836,8 @@
void decompressSections();
void mergeSections();
-Symbol *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
- uint64_t Size, InputSectionBase &Section);
+Defined *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
+ uint64_t Size, InputSectionBase &Section);
// Linker generated sections which can be used as inputs.
struct InX {
diff --git a/ELF/Target.cpp b/ELF/Target.cpp
index b528fd5..c19cb5f 100644
--- a/ELF/Target.cpp
+++ b/ELF/Target.cpp
@@ -87,7 +87,7 @@
fatal("unknown target machine");
}
-template <class ELFT> static std::string getErrorLoc(const uint8_t *Loc) {
+template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *Loc) {
for (InputSectionBase *D : InputSections) {
auto *IS = dyn_cast<InputSection>(D);
if (!IS || !IS->getParent())
@@ -95,21 +95,21 @@
uint8_t *ISLoc = IS->getParent()->Loc + IS->OutSecOff;
if (ISLoc <= Loc && Loc < ISLoc + IS->getSize())
- return IS->template getLocation<ELFT>(Loc - ISLoc) + ": ";
+ return {IS, IS->template getLocation<ELFT>(Loc - ISLoc) + ": "};
}
- return "";
+ return {};
}
-std::string elf::getErrorLocation(const uint8_t *Loc) {
+ErrorPlace elf::getErrorPlace(const uint8_t *Loc) {
switch (Config->EKind) {
case ELF32LEKind:
- return getErrorLoc<ELF32LE>(Loc);
+ return getErrPlace<ELF32LE>(Loc);
case ELF32BEKind:
- return getErrorLoc<ELF32BE>(Loc);
+ return getErrPlace<ELF32BE>(Loc);
case ELF64LEKind:
- return getErrorLoc<ELF64LE>(Loc);
+ return getErrPlace<ELF64LE>(Loc);
case ELF64BEKind:
- return getErrorLoc<ELF64BE>(Loc);
+ return getErrPlace<ELF64BE>(Loc);
default:
llvm_unreachable("unknown ELF type");
}
diff --git a/ELF/Target.h b/ELF/Target.h
index 1f58adb..698758e 100644
--- a/ELF/Target.h
+++ b/ELF/Target.h
@@ -28,6 +28,7 @@
virtual bool isPicRel(RelType Type) const { return true; }
virtual RelType getDynRel(RelType Type) const { return Type; }
virtual void writeGotPltHeader(uint8_t *Buf) const {}
+ virtual void writeGotHeader(uint8_t *Buf) const {}
virtual void writeGotPlt(uint8_t *Buf, const Symbol &S) const {};
virtual void writeIgotPlt(uint8_t *Buf, const Symbol &S) const;
virtual int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const;
@@ -43,6 +44,10 @@
virtual void addPltHeaderSymbols(InputSection &IS) const {}
virtual void addPltSymbols(InputSection &IS, uint64_t Off) const {}
+ unsigned getPltEntryOffset(unsigned Index) const {
+ return Index * PltEntrySize + PltHeaderSize;
+ }
+
// Returns true if a relocation only uses the low bits of a value such that
// all those bits are in in the same page. For example, if the relocation
// only uses the low 12 bits in a system with 4k pages. If this is true, the
@@ -71,9 +76,10 @@
uint64_t getImageBase();
- // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
- // end of .got
+ // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got or .got.plt section.
uint64_t GotBaseSymOff = 0;
+ // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got.
+ bool GotBaseSymInGotPlt = true;
// On systems with range extensions we place collections of Thunks at
// regular spacings that enable the majority of branches reach the Thunks.
@@ -97,6 +103,9 @@
// to support lazy loading.
unsigned GotPltHeaderEntriesNum = 3;
+ // On PPC ELF V2 abi, the first entry in the .got is the .TOC.
+ unsigned GotHeaderEntriesNum = 0;
+
// Set to 0 for variant 2
unsigned TcbSize = 0;
@@ -134,7 +143,17 @@
TargetInfo *getX86_64TargetInfo();
template <class ELFT> TargetInfo *getMipsTargetInfo();
-std::string getErrorLocation(const uint8_t *Loc);
+struct ErrorPlace {
+ InputSectionBase *IS;
+ std::string Loc;
+};
+
+// Returns input section and corresponding source string for the given location.
+ErrorPlace getErrorPlace(const uint8_t *Loc);
+
+static inline std::string getErrorLocation(const uint8_t *Loc) {
+ return getErrorPlace(Loc).Loc;
+}
uint64_t getPPC64TocBase();
uint64_t getAArch64Page(uint64_t Expr);
@@ -146,39 +165,74 @@
static inline void reportRangeError(uint8_t *Loc, RelType Type, const Twine &V,
int64_t Min, uint64_t Max) {
- error(getErrorLocation(Loc) + "relocation " + lld::toString(Type) +
- " out of range: " + V + " is not in [" + Twine(Min) + ", " +
- Twine(Max) + "]");
+ ErrorPlace ErrPlace = getErrorPlace(Loc);
+ StringRef Hint;
+ if (ErrPlace.IS && ErrPlace.IS->Name.startswith(".debug"))
+ Hint = "; consider recompiling with -fdebug-types-section to reduce size "
+ "of debug sections";
+
+ error(ErrPlace.Loc + "relocation " + lld::toString(Type) +
+ " out of range: " + V.str() + " is not in [" + Twine(Min).str() + ", " +
+ Twine(Max).str() + "]" + Hint);
}
-template <unsigned N>
-static void checkInt(uint8_t *Loc, int64_t V, RelType Type) {
- if (!llvm::isInt<N>(V))
+// Sign-extend Nth bit all the way to MSB.
+inline int64_t signExtend(uint64_t V, int N) {
+ return int64_t(V << (64 - N)) >> (64 - N);
+}
+
+// Make sure that V can be represented as an N bit signed integer.
+inline void checkInt(uint8_t *Loc, int64_t V, int N, RelType Type) {
+ if (V != signExtend(V, N))
reportRangeError(Loc, Type, Twine(V), llvm::minIntN(N), llvm::maxIntN(N));
}
-template <unsigned N>
-static void checkUInt(uint8_t *Loc, uint64_t V, RelType Type) {
- if (!llvm::isUInt<N>(V))
+// Make sure that V can be represented as an N bit unsigned integer.
+inline void checkUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) {
+ if ((V >> N) != 0)
reportRangeError(Loc, Type, Twine(V), 0, llvm::maxUIntN(N));
}
-template <unsigned N>
-static void checkIntUInt(uint8_t *Loc, uint64_t V, RelType Type) {
- if (!llvm::isInt<N>(V) && !llvm::isUInt<N>(V))
- // For the error message we should cast V to a signed integer so that error
- // messages show a small negative value rather than an extremely large one
+// Make sure that V can be represented as an N bit signed or unsigned integer.
+inline void checkIntUInt(uint8_t *Loc, uint64_t V, int N, RelType Type) {
+ // For the error message we should cast V to a signed integer so that error
+ // messages show a small negative value rather than an extremely large one
+ if (V != (uint64_t)signExtend(V, N) && (V >> N) != 0)
reportRangeError(Loc, Type, Twine((int64_t)V), llvm::minIntN(N),
- llvm::maxUIntN(N));
+ llvm::maxIntN(N));
}
-template <unsigned N>
-static void checkAlignment(uint8_t *Loc, uint64_t V, RelType Type) {
+inline void checkAlignment(uint8_t *Loc, uint64_t V, int N, RelType Type) {
if ((V & (N - 1)) != 0)
error(getErrorLocation(Loc) + "improper alignment for relocation " +
lld::toString(Type) + ": 0x" + llvm::utohexstr(V) +
" is not aligned to " + Twine(N) + " bytes");
}
+
+// Endianness-aware read/write.
+inline uint16_t read16(const void *P) {
+ return llvm::support::endian::read16(P, Config->Endianness);
+}
+
+inline uint32_t read32(const void *P) {
+ return llvm::support::endian::read32(P, Config->Endianness);
+}
+
+inline uint64_t read64(const void *P) {
+ return llvm::support::endian::read64(P, Config->Endianness);
+}
+
+inline void write16(void *P, uint16_t V) {
+ llvm::support::endian::write16(P, V, Config->Endianness);
+}
+
+inline void write32(void *P, uint32_t V) {
+ llvm::support::endian::write32(P, V, Config->Endianness);
+}
+
+inline void write64(void *P, uint64_t V) {
+ llvm::support::endian::write64(P, V, Config->Endianness);
+}
} // namespace elf
} // namespace lld
diff --git a/ELF/Thunks.cpp b/ELF/Thunks.cpp
index b0bbf6d..4db4990 100644
--- a/ELF/Thunks.cpp
+++ b/ELF/Thunks.cpp
@@ -40,7 +40,6 @@
using namespace llvm;
using namespace llvm::object;
-using namespace llvm::support::endian;
using namespace llvm::ELF;
namespace lld {
@@ -52,59 +51,112 @@
class AArch64ABSLongThunk final : public Thunk {
public:
AArch64ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 16; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 16; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
};
class AArch64ADRPThunk final : public Thunk {
public:
AArch64ADRPThunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 12; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 12; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
};
+// Base class for ARM thunks.
+//
+// An ARM thunk may be either short or long. A short thunk is simply a branch
+// (B) instruction, and it may be used to call ARM functions when the distance
+// from the thunk to the target is less than 32MB. Long thunks can branch to any
+// virtual address and can switch between ARM and Thumb, and they are
+// implemented in the derived classes. This class tries to create a short thunk
+// if the target is in range, otherwise it creates a long thunk.
+class ARMThunk : public Thunk {
+public:
+ ARMThunk(Symbol &Dest) : Thunk(Dest) {}
+
+ bool mayUseShortThunk();
+ uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
+ void writeTo(uint8_t *Buf) override;
+ bool isCompatibleWith(RelType Type) const override;
+
+ // Returns the size of a long thunk.
+ virtual uint32_t sizeLong() = 0;
+
+ // Writes a long thunk to Buf.
+ virtual void writeLong(uint8_t *Buf) = 0;
+
+private:
+ // This field tracks whether all previously considered layouts would allow
+ // this thunk to be short. If we have ever needed a long thunk, we always
+ // create a long thunk, even if the thunk may be short given the current
+ // distance to the target. We do this because transitioning from long to short
+ // can create layout oscillations in certain corner cases which would prevent
+ // the layout from converging.
+ bool MayUseShortThunk = true;
+};
+
+// Base class for Thumb-2 thunks.
+//
+// This class is similar to ARMThunk, but it uses the Thumb-2 B.W instruction
+// which has a range of 16MB.
+class ThumbThunk : public Thunk {
+public:
+ ThumbThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
+
+ bool mayUseShortThunk();
+ uint32_t size() override { return mayUseShortThunk() ? 4 : sizeLong(); }
+ void writeTo(uint8_t *Buf) override;
+ bool isCompatibleWith(RelType Type) const override;
+
+ // Returns the size of a long thunk.
+ virtual uint32_t sizeLong() = 0;
+
+ // Writes a long thunk to Buf.
+ virtual void writeLong(uint8_t *Buf) = 0;
+
+private:
+ // See comment in ARMThunk above.
+ bool MayUseShortThunk = true;
+};
+
// Specific ARM Thunk implementations. The naming convention is:
// Source State, TargetState, Target Requirement, ABS or PI, Range
-class ARMV7ABSLongThunk final : public Thunk {
+class ARMV7ABSLongThunk final : public ARMThunk {
public:
- ARMV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) {}
+ ARMV7ABSLongThunk(Symbol &Dest) : ARMThunk(Dest) {}
- uint32_t size() const override { return 12; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t sizeLong() override { return 12; }
+ void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(RelType Type) const override;
};
-class ARMV7PILongThunk final : public Thunk {
+class ARMV7PILongThunk final : public ARMThunk {
public:
- ARMV7PILongThunk(Symbol &Dest) : Thunk(Dest) {}
+ ARMV7PILongThunk(Symbol &Dest) : ARMThunk(Dest) {}
- uint32_t size() const override { return 16; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t sizeLong() override { return 16; }
+ void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(RelType Type) const override;
};
-class ThumbV7ABSLongThunk final : public Thunk {
+class ThumbV7ABSLongThunk final : public ThumbThunk {
public:
- ThumbV7ABSLongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
+ ThumbV7ABSLongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
- uint32_t size() const override { return 10; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t sizeLong() override { return 10; }
+ void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(RelType Type) const override;
};
-class ThumbV7PILongThunk final : public Thunk {
+class ThumbV7PILongThunk final : public ThumbThunk {
public:
- ThumbV7PILongThunk(Symbol &Dest) : Thunk(Dest) { Alignment = 2; }
+ ThumbV7PILongThunk(Symbol &Dest) : ThumbThunk(Dest) {}
- uint32_t size() const override { return 12; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t sizeLong() override { return 12; }
+ void writeLong(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
- bool isCompatibleWith(RelType Type) const override;
};
// MIPS LA25 thunk
@@ -112,8 +164,8 @@
public:
MipsThunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 16; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 16; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
@@ -123,8 +175,8 @@
public:
MicroMipsThunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 14; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 14; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
@@ -134,14 +186,27 @@
public:
MicroMipsR6Thunk(Symbol &Dest) : Thunk(Dest) {}
- uint32_t size() const override { return 12; }
- void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
+ uint32_t size() override { return 12; }
+ void writeTo(uint8_t *Buf) override;
void addSymbols(ThunkSection &IS) override;
InputSection *getTargetInputSection() const override;
};
} // end anonymous namespace
+Defined *Thunk::addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
+ InputSectionBase &Section) {
+ Defined *D = addSyntheticLocal(Name, Type, Value, /*Size=*/0, Section);
+ Syms.push_back(D);
+ return D;
+}
+
+void Thunk::setOffset(uint64_t NewOffset) {
+ for (Defined *D : Syms)
+ D->Value = D->Value - Offset + NewOffset;
+ Offset = NewOffset;
+}
+
// AArch64 long range Thunks
static uint64_t getAArch64ThunkDestVA(const Symbol &S) {
@@ -149,7 +214,7 @@
return V;
}
-void AArch64ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+void AArch64ABSLongThunk::writeTo(uint8_t *Buf) {
const uint8_t Data[] = {
0x50, 0x00, 0x00, 0x58, // ldr x16, L0
0x00, 0x02, 0x1f, 0xd6, // br x16
@@ -162,11 +227,10 @@
}
void AArch64ABSLongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__AArch64AbsLongThunk_" + Destination.getName()), STT_FUNC,
- Offset, size(), IS);
- addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS);
- addSyntheticLocal("$d", STT_NOTYPE, Offset + 8, 0, IS);
+ addSymbol(Saver.save("__AArch64AbsLongThunk_" + Destination.getName()),
+ STT_FUNC, 0, IS);
+ addSymbol("$x", STT_NOTYPE, 0, IS);
+ addSymbol("$d", STT_NOTYPE, 8, IS);
}
// This Thunk has a maximum range of 4Gb, this is sufficient for all programs
@@ -174,26 +238,24 @@
// clang and gcc do not support the large code model for position independent
// code so it is safe to use this for position independent thunks without
// worrying about the destination being more than 4Gb away.
-void AArch64ADRPThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+void AArch64ADRPThunk::writeTo(uint8_t *Buf) {
const uint8_t Data[] = {
0x10, 0x00, 0x00, 0x90, // adrp x16, Dest R_AARCH64_ADR_PREL_PG_HI21(Dest)
0x10, 0x02, 0x00, 0x91, // add x16, x16, R_AARCH64_ADD_ABS_LO12_NC(Dest)
0x00, 0x02, 0x1f, 0xd6, // br x16
};
uint64_t S = getAArch64ThunkDestVA(Destination);
- uint64_t P = ThunkSym->getVA();
+ uint64_t P = getThunkTargetSym()->getVA();
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
getAArch64Page(S) - getAArch64Page(P));
Target->relocateOne(Buf + 4, R_AARCH64_ADD_ABS_LO12_NC, S);
}
-void AArch64ADRPThunk::addSymbols(ThunkSection &IS)
-{
- ThunkSym = addSyntheticLocal(
- Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
- Offset, size(), IS);
- addSyntheticLocal("$x", STT_NOTYPE, Offset, 0, IS);
+void AArch64ADRPThunk::addSymbols(ThunkSection &IS) {
+ addSymbol(Saver.save("__AArch64ADRPThunk_" + Destination.getName()), STT_FUNC,
+ 0, IS);
+ addSymbol("$x", STT_NOTYPE, 0, IS);
}
// ARM Target Thunks
@@ -202,7 +264,81 @@
return SignExtend64<32>(V);
}
-void ARMV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+// This function returns true if the target is not Thumb and is within 2^26, and
+// it has not previously returned false (see comment for MayUseShortThunk).
+bool ARMThunk::mayUseShortThunk() {
+ if (!MayUseShortThunk)
+ return false;
+ uint64_t S = getARMThunkDestVA(Destination);
+ if (S & 1) {
+ MayUseShortThunk = false;
+ return false;
+ }
+ uint64_t P = getThunkTargetSym()->getVA();
+ int64_t Offset = S - P - 8;
+ MayUseShortThunk = llvm::isInt<26>(Offset);
+ return MayUseShortThunk;
+}
+
+void ARMThunk::writeTo(uint8_t *Buf) {
+ if (!mayUseShortThunk()) {
+ writeLong(Buf);
+ return;
+ }
+
+ uint64_t S = getARMThunkDestVA(Destination);
+ uint64_t P = getThunkTargetSym()->getVA();
+ int64_t Offset = S - P - 8;
+ const uint8_t Data[] = {
+ 0x00, 0x00, 0x00, 0xea, // b S
+ };
+ memcpy(Buf, Data, sizeof(Data));
+ Target->relocateOne(Buf, R_ARM_JUMP24, Offset);
+}
+
+bool ARMThunk::isCompatibleWith(RelType Type) const {
+ // Thumb branch relocations can't use BLX
+ return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
+}
+
+// This function returns true if the target is Thumb and is within 2^25, and
+// it has not previously returned false (see comment for MayUseShortThunk).
+bool ThumbThunk::mayUseShortThunk() {
+ if (!MayUseShortThunk)
+ return false;
+ uint64_t S = getARMThunkDestVA(Destination);
+ if ((S & 1) == 0) {
+ MayUseShortThunk = false;
+ return false;
+ }
+ uint64_t P = getThunkTargetSym()->getVA() & ~1;
+ int64_t Offset = S - P - 4;
+ MayUseShortThunk = llvm::isInt<25>(Offset);
+ return MayUseShortThunk;
+}
+
+void ThumbThunk::writeTo(uint8_t *Buf) {
+ if (!mayUseShortThunk()) {
+ writeLong(Buf);
+ return;
+ }
+
+ uint64_t S = getARMThunkDestVA(Destination);
+ uint64_t P = getThunkTargetSym()->getVA();
+ int64_t Offset = S - P - 4;
+ const uint8_t Data[] = {
+ 0x00, 0xf0, 0x00, 0xb0, // b.w S
+ };
+ memcpy(Buf, Data, sizeof(Data));
+ Target->relocateOne(Buf, R_ARM_THM_JUMP24, Offset);
+}
+
+bool ThumbThunk::isCompatibleWith(RelType Type) const {
+ // ARM branch relocations can't use BLX
+ return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
+}
+
+void ARMV7ABSLongThunk::writeLong(uint8_t *Buf) {
const uint8_t Data[] = {
0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
@@ -215,18 +351,12 @@
}
void ARMV7ABSLongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
- Offset, size(), IS);
- addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS);
+ addSymbol(Saver.save("__ARMv7ABSLongThunk_" + Destination.getName()),
+ STT_FUNC, 0, IS);
+ addSymbol("$a", STT_NOTYPE, 0, IS);
}
-bool ARMV7ABSLongThunk::isCompatibleWith(RelType Type) const {
- // Thumb branch relocations can't use BLX
- return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
-}
-
-void ThumbV7ABSLongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+void ThumbV7ABSLongThunk::writeLong(uint8_t *Buf) {
const uint8_t Data[] = {
0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
@@ -239,18 +369,12 @@
}
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()), STT_FUNC,
- Offset | 0x1, size(), IS);
- addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS);
+ addSymbol(Saver.save("__Thumbv7ABSLongThunk_" + Destination.getName()),
+ STT_FUNC, 1, IS);
+ addSymbol("$t", STT_NOTYPE, 0, IS);
}
-bool ThumbV7ABSLongThunk::isCompatibleWith(RelType Type) const {
- // ARM branch relocations can't use BLX
- return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
-}
-
-void ARMV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+void ARMV7PILongThunk::writeLong(uint8_t *Buf) {
const uint8_t Data[] = {
0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) + 8)
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P) + 8)
@@ -258,7 +382,7 @@
0x1c, 0xff, 0x2f, 0xe1, // bx r12
};
uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = ThunkSym->getVA();
+ uint64_t P = getThunkTargetSym()->getVA();
uint64_t Offset = S - P - 16;
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, Offset);
@@ -266,18 +390,12 @@
}
void ARMV7PILongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
- Offset, size(), IS);
- addSyntheticLocal("$a", STT_NOTYPE, Offset, 0, IS);
+ addSymbol(Saver.save("__ARMV7PILongThunk_" + Destination.getName()), STT_FUNC,
+ 0, IS);
+ addSymbol("$a", STT_NOTYPE, 0, IS);
}
-bool ARMV7PILongThunk::isCompatibleWith(RelType Type) const {
- // Thumb branch relocations can't use BLX
- return Type != R_ARM_THM_JUMP19 && Type != R_ARM_THM_JUMP24;
-}
-
-void ThumbV7PILongThunk::writeTo(uint8_t *Buf, ThunkSection &IS) const {
+void ThumbV7PILongThunk::writeLong(uint8_t *Buf) {
const uint8_t Data[] = {
0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P) + 4)
@@ -285,7 +403,7 @@
0x60, 0x47, // bx r12
};
uint64_t S = getARMThunkDestVA(Destination);
- uint64_t P = ThunkSym->getVA() & ~0x1;
+ uint64_t P = getThunkTargetSym()->getVA() & ~0x1;
uint64_t Offset = S - P - 12;
memcpy(Buf, Data, sizeof(Data));
Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, Offset);
@@ -293,32 +411,25 @@
}
void ThumbV7PILongThunk::addSymbols(ThunkSection &IS) {
- ThunkSym = addSyntheticLocal(
- Saver.save("__ThumbV7PILongThunk_" + Destination.getName()), STT_FUNC,
- Offset | 0x1, size(), IS);
- addSyntheticLocal("$t", STT_NOTYPE, Offset, 0, IS);
-}
-
-bool ThumbV7PILongThunk::isCompatibleWith(RelType Type) const {
- // ARM branch relocations can't use BLX
- return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32;
+ addSymbol(Saver.save("__ThumbV7PILongThunk_" + Destination.getName()),
+ STT_FUNC, 1, IS);
+ addSymbol("$t", STT_NOTYPE, 0, IS);
}
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
-void MipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
+void MipsThunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA();
- write32(Buf, 0x3c190000, Config->Endianness); // lui $25, %hi(func)
- write32(Buf + 4, 0x08000000 | (S >> 2), Config->Endianness); // j func
- write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func)
- write32(Buf + 12, 0x00000000, Config->Endianness); // nop
+ write32(Buf, 0x3c190000); // lui $25, %hi(func)
+ write32(Buf + 4, 0x08000000 | (S >> 2)); // j func
+ write32(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
+ write32(Buf + 12, 0x00000000); // nop
Target->relocateOne(Buf, R_MIPS_HI16, S);
Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
}
void MipsThunk::addSymbols(ThunkSection &IS) {
- ThunkSym =
- addSyntheticLocal(Saver.save("__LA25Thunk_" + Destination.getName()),
- STT_FUNC, Offset, size(), IS);
+ addSymbol(Saver.save("__LA25Thunk_" + Destination.getName()), STT_FUNC, 0,
+ IS);
}
InputSection *MipsThunk::getTargetInputSection() const {
@@ -328,22 +439,21 @@
// Write microMIPS R2-R5 LA25 thunk code
// to call PIC function from the non-PIC one.
-void MicroMipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const {
+void MicroMipsThunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA() | 1;
- write16(Buf, 0x41b9, Config->Endianness); // lui $25, %hi(func)
- write16(Buf + 4, 0xd400, Config->Endianness); // j func
- write16(Buf + 8, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func)
- write16(Buf + 12, 0x0c00, Config->Endianness); // nop
+ write16(Buf, 0x41b9); // lui $25, %hi(func)
+ write16(Buf + 4, 0xd400); // j func
+ write16(Buf + 8, 0x3339); // addiu $25, $25, %lo(func)
+ write16(Buf + 12, 0x0c00); // nop
Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
Target->relocateOne(Buf + 4, R_MICROMIPS_26_S1, S);
Target->relocateOne(Buf + 8, R_MICROMIPS_LO16, S);
}
void MicroMipsThunk::addSymbols(ThunkSection &IS) {
- ThunkSym =
- addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
- STT_FUNC, Offset, size(), IS);
- ThunkSym->StOther |= STO_MIPS_MICROMIPS;
+ Defined *D = addSymbol(
+ Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
+ D->StOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsThunk::getTargetInputSection() const {
@@ -353,22 +463,21 @@
// Write microMIPS R6 LA25 thunk code
// to call PIC function from the non-PIC one.
-void MicroMipsR6Thunk::writeTo(uint8_t *Buf, ThunkSection &) const {
+void MicroMipsR6Thunk::writeTo(uint8_t *Buf) {
uint64_t S = Destination.getVA() | 1;
- uint64_t P = ThunkSym->getVA();
- write16(Buf, 0x1320, Config->Endianness); // lui $25, %hi(func)
- write16(Buf + 4, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func)
- write16(Buf + 8, 0x9400, Config->Endianness); // bc func
+ uint64_t P = getThunkTargetSym()->getVA();
+ write16(Buf, 0x1320); // lui $25, %hi(func)
+ write16(Buf + 4, 0x3339); // addiu $25, $25, %lo(func)
+ write16(Buf + 8, 0x9400); // bc func
Target->relocateOne(Buf, R_MICROMIPS_HI16, S);
Target->relocateOne(Buf + 4, R_MICROMIPS_LO16, S);
Target->relocateOne(Buf + 8, R_MICROMIPS_PC26_S1, S - P - 12);
}
void MicroMipsR6Thunk::addSymbols(ThunkSection &IS) {
- ThunkSym =
- addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()),
- STT_FUNC, Offset, size(), IS);
- ThunkSym->StOther |= STO_MIPS_MICROMIPS;
+ Defined *D = addSymbol(
+ Saver.save("__microLA25Thunk_" + Destination.getName()), STT_FUNC, 0, IS);
+ D->StOther |= STO_MIPS_MICROMIPS;
}
InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
diff --git a/ELF/Thunks.h b/ELF/Thunks.h
index 828fac0..ed82b4d 100644
--- a/ELF/Thunks.h
+++ b/ELF/Thunks.h
@@ -14,6 +14,7 @@
namespace lld {
namespace elf {
+class Defined;
class Symbol;
class ThunkSection;
// Class to describe an instance of a Thunk.
@@ -30,12 +31,17 @@
Thunk(Symbol &Destination);
virtual ~Thunk();
- virtual uint32_t size() const { return 0; }
- virtual void writeTo(uint8_t *Buf, ThunkSection &IS) const {}
+ virtual uint32_t size() = 0;
+ virtual void writeTo(uint8_t *Buf) = 0;
- // All Thunks must define at least one symbol ThunkSym so that we can
- // redirect relocations to it.
- virtual void addSymbols(ThunkSection &IS) {}
+ // All Thunks must define at least one symbol, known as the thunk target
+ // symbol, so that we can redirect relocations to it. The thunk may define
+ // additional symbols, but these are never targets for relocations.
+ virtual void addSymbols(ThunkSection &IS) = 0;
+
+ void setOffset(uint64_t Offset);
+ Defined *addSymbol(StringRef Name, uint8_t Type, uint64_t Value,
+ InputSectionBase &Section);
// Some Thunks must be placed immediately before their Target as they elide
// a branch and fall through to the first Symbol in the Target.
@@ -45,10 +51,12 @@
// compatible with it.
virtual bool isCompatibleWith(RelType Type) const { return true; }
+ Defined *getThunkTargetSym() const { return Syms[0]; }
+
// The alignment requirement for this Thunk, defaults to the size of the
// typical code section alignment.
Symbol &Destination;
- Symbol *ThunkSym;
+ llvm::SmallVector<Defined *, 3> Syms;
uint64_t Offset = 0;
uint32_t Alignment = 4;
};
diff --git a/ELF/Writer.cpp b/ELF/Writer.cpp
index 309022d..e685c8e 100644
--- a/ELF/Writer.cpp
+++ b/ELF/Writer.cpp
@@ -62,7 +62,7 @@
void assignFileOffsets();
void assignFileOffsetsBinary();
void setPhdrs();
- void checkNoOverlappingSections();
+ void checkSectionOverlap();
void fixSectionAlignments();
void openFile();
void writeTrapInstr();
@@ -94,13 +94,13 @@
// This is for --emit-relocs. If .text.foo is emitted as .text.bar, we want
// to emit .rela.text.foo as .rela.text.bar for consistency (this is not
// technically required, but not doing it is odd). This code guarantees that.
- if ((S->Type == SHT_REL || S->Type == SHT_RELA) &&
- !isa<SyntheticSection>(S)) {
- OutputSection *Out =
- cast<InputSection>(S)->getRelocatedSection()->getOutputSection();
- if (S->Type == SHT_RELA)
- return Saver.save(".rela" + Out->Name);
- return Saver.save(".rel" + Out->Name);
+ if (auto *IS = dyn_cast<InputSection>(S)) {
+ if (InputSectionBase *Rel = IS->getRelocatedSection()) {
+ OutputSection *Out = Rel->getOutputSection();
+ if (S->Type == SHT_RELA)
+ return Saver.save(".rela" + Out->Name);
+ return Saver.save(".rel" + Out->Name);
+ }
}
for (StringRef V :
@@ -190,21 +190,29 @@
Symtab->addAbsolute("__gnu_local_gp", STV_HIDDEN, STB_GLOBAL);
}
+ // The 64-bit PowerOpen ABI defines a TableOfContents (TOC) which combines the
+ // typical ELF GOT with the small data sections. It commonly includes .got
+ // .toc .sdata .sbss. The .TOC. symbol replaces both _GLOBAL_OFFSET_TABLE_ and
+ // _SDA_BASE_ from the 32-bit ABI. It is used to represent the TOC base which
+ // is offset by 0x8000 bytes from the start of the .got section.
ElfSym::GlobalOffsetTable = addOptionalRegular(
- "_GLOBAL_OFFSET_TABLE_", Out::ElfHeader, Target->GotBaseSymOff);
+ (Config->EMachine == EM_PPC64) ? ".TOC." : "_GLOBAL_OFFSET_TABLE_",
+ Out::ElfHeader, Target->GotBaseSymOff);
// __ehdr_start is the location of ELF file headers. Note that we define
// this symbol unconditionally even when using a linker script, which
// differs from the behavior implemented by GNU linker which only define
// this symbol if ELF headers are in the memory mapped segment.
+ addOptionalRegular("__ehdr_start", Out::ElfHeader, 0, STV_HIDDEN);
+
// __executable_start is not documented, but the expectation of at
- // least the android libc is that it points to the elf header too.
+ // least the Android libc is that it points to the ELF header.
+ addOptionalRegular("__executable_start", Out::ElfHeader, 0, STV_HIDDEN);
+
// __dso_handle symbol is passed to cxa_finalize as a marker to identify
// each DSO. The address of the symbol doesn't matter as long as they are
// different in different DSOs, so we chose the start address of the DSO.
- for (const char *Name :
- {"__ehdr_start", "__executable_start", "__dso_handle"})
- addOptionalRegular(Name, Out::ElfHeader, 0, STV_HIDDEN);
+ addOptionalRegular("__dso_handle", Out::ElfHeader, 0, STV_HIDDEN);
// If linker script do layout we do not need to create any standart symbols.
if (Script->HasSectionsCommand)
@@ -276,10 +284,9 @@
// If there is a SECTIONS command and a .data.rel.ro section name use name
// .data.rel.ro.bss so that we match in the .data.rel.ro output section.
// This makes sure our relro is contiguous.
- bool HasDataRelRo =
- Script->HasSectionsCommand && findSection(".data.rel.ro");
- InX::BssRelRo = make<BssSection>(
- HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
+ bool HasDataRelRo = Script->HasSectionsCommand && findSection(".data.rel.ro");
+ InX::BssRelRo =
+ make<BssSection>(HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1);
Add(InX::BssRelRo);
// Add MIPS-specific sections.
@@ -451,7 +458,7 @@
}
if (Config->CheckSections)
- checkNoOverlappingSections();
+ checkSectionOverlap();
// It does not make sense try to open the file if we have error already.
if (errorCount())
@@ -475,8 +482,9 @@
if (errorCount())
return;
- // Handle -Map option.
+ // Handle -Map and -cref options.
writeMapFile();
+ writeCrossReferenceTable();
if (errorCount())
return;
@@ -868,11 +876,12 @@
// defining these symbols explicitly in the linker script.
template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
if (ElfSym::GlobalOffsetTable) {
- // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
- // be at some offset from the base of the .got section, usually 0 or the end
- // of the .got
- InputSection *GotSection = InX::MipsGot ? cast<InputSection>(InX::MipsGot)
- : cast<InputSection>(InX::Got);
+ // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually
+ // to the start of the .got or .got.plt section.
+ InputSection *GotSection = InX::GotPlt;
+ if (!Target->GotBaseSymInGotPlt)
+ GotSection = InX::MipsGot ? cast<InputSection>(InX::MipsGot)
+ : cast<InputSection>(InX::Got);
ElfSym::GlobalOffsetTable->Section = GotSection;
}
@@ -1187,7 +1196,7 @@
auto E = Script->SectionCommands.end();
auto NonScriptI = std::find_if(I, E, [](BaseCommand *Base) {
if (auto *Sec = dyn_cast<OutputSection>(Base))
- return Sec->SectionIndex == INT_MAX;
+ return Sec->SectionIndex == UINT32_MAX;
return false;
});
@@ -1621,8 +1630,8 @@
StringRef S = Sec->Name;
if (!isValidCIdentifier(S))
return;
- addOptionalRegular(Saver.save("__start_" + S), Sec, 0, STV_DEFAULT);
- addOptionalRegular(Saver.save("__stop_" + S), Sec, -1, STV_DEFAULT);
+ addOptionalRegular(Saver.save("__start_" + S), Sec, 0, STV_PROTECTED);
+ addOptionalRegular(Saver.save("__stop_" + S), Sec, -1, STV_PROTECTED);
}
static bool needsPtLoad(OutputSection *Sec) {
@@ -1750,11 +1759,9 @@
// pages for the stack non-executable. If you really want an executable
// stack, you can pass -z execstack, but that's not recommended for
// security reasons.
- unsigned Perm;
+ unsigned Perm = PF_R | PF_W;
if (Config->ZExecstack)
- Perm = PF_R | PF_W | PF_X;
- else
- Perm = PF_R | PF_W;
+ Perm |= PF_X;
AddHdr(PT_GNU_STACK, Perm)->p_memsz = Config->ZStackSize;
// PT_OPENBSD_WXNEEDED is a OpenBSD-specific header to mark the executable
@@ -1872,6 +1879,12 @@
FileSize = alignTo(Off, Config->Wordsize);
}
+static std::string rangeToString(uint64_t Addr, uint64_t Len) {
+ if (Len == 0)
+ return "<empty range at 0x" + utohexstr(Addr) + ">";
+ return "[0x" + utohexstr(Addr) + ", 0x" + utohexstr(Addr + Len - 1) + "]";
+}
+
// Assign file offsets to output sections.
template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
uint64_t Off = 0;
@@ -1896,6 +1909,25 @@
SectionHeaderOff = alignTo(Off, Config->Wordsize);
FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr);
+
+ // Our logic assumes that sections have rising VA within the same segment.
+ // With use of linker scripts it is possible to violate this rule and get file
+ // offset overlaps or overflows. That should never happen with a valid script
+ // which does not move the location counter backwards and usually scripts do
+ // not do that. Unfortunately, there are apps in the wild, for example, Linux
+ // kernel, which control segment distribution explicitly and move the counter
+ // backwards, so we have to allow doing that to support linking them. We
+ // perform non-critical checks for overlaps in checkNoOverlappingSections(),
+ // but here we want to prevent file size overflows because it would crash the
+ // linker.
+ for (OutputSection *Sec : OutputSections) {
+ if (Sec->Type == SHT_NOBITS)
+ continue;
+ if ((Sec->Offset > FileSize) || (Sec->Offset + Sec->Size > FileSize))
+ error("unable to place section " + Sec->Name + " at file offset " +
+ rangeToString(Sec->Offset, Sec->Offset + Sec->Size) +
+ "; check your linker script for overflows");
+ }
}
// Finalize the program headers. We call this function after we assign
@@ -1934,55 +1966,33 @@
}
}
-static std::string rangeToString(uint64_t Addr, uint64_t Len) {
- if (Len == 0)
- return "<empty range at 0x" + utohexstr(Addr) + ">";
- return "[0x" + utohexstr(Addr) + " -> 0x" + utohexstr(Addr + Len - 1) + "]";
-}
+// A helper struct for checkSectionOverlap.
+namespace {
+struct SectionOffset {
+ OutputSection *Sec;
+ uint64_t Offset;
+};
+} // namespace
// Check whether sections overlap for a specific address range (file offsets,
// load and virtual adresses).
-//
-// This is a helper function called by Writer::checkNoOverlappingSections().
-template <typename Getter, typename Predicate>
-static void checkForSectionOverlap(ArrayRef<OutputSection *> AllSections,
- StringRef Kind, Getter GetStart,
- Predicate ShouldSkip) {
- std::vector<OutputSection *> Sections;
- // By removing all zero-size sections we can simplify the check for overlap to
- // just checking whether the section range contains the other section's start
- // address. Additionally, it also slightly speeds up the checking since we
- // don't bother checking for overlap with sections that can never overlap.
- for (OutputSection *Sec : AllSections)
- if (Sec->Size > 0 && !ShouldSkip(Sec))
- Sections.push_back(Sec);
-
- // Instead of comparing every OutputSection with every other output section
- // we sort the sections by address (file offset or load/virtual address). This
- // way we find all overlapping sections but only need one comparision with the
- // next section in the common non-overlapping case. The only time we end up
- // doing more than one iteration of the following nested loop is if there are
- // overlapping sections.
+static void checkOverlap(StringRef Name, std::vector<SectionOffset> &Sections) {
std::sort(Sections.begin(), Sections.end(),
- [=](const OutputSection *A, const OutputSection *B) {
- return GetStart(A) < GetStart(B);
+ [=](const SectionOffset &A, const SectionOffset &B) {
+ return A.Offset < B.Offset;
});
- for (size_t I = 0; I < Sections.size(); ++I) {
- OutputSection *Sec = Sections[I];
- uint64_t Start = GetStart(Sec);
- for (auto *Other : ArrayRef<OutputSection *>(Sections).slice(I + 1)) {
- // Since the sections are sorted by start address we only need to check
- // whether the other sections starts before the end of Sec. If this is
- // not the case we can break out of this loop since all following sections
- // will also start after the end of Sec.
- if (Start + Sec->Size <= GetStart(Other))
- break;
- errorOrWarn("section " + Sec->Name + " " + Kind +
- " range overlaps with " + Other->Name + "\n>>> " + Sec->Name +
- " range is " + rangeToString(Start, Sec->Size) + "\n>>> " +
- Other->Name + " range is " +
- rangeToString(GetStart(Other), Other->Size));
- }
+
+ // Finding overlap is easy given a vector is sorted by start position.
+ // If an element starts before the end of the previous element, they overlap.
+ for (size_t I = 1, End = Sections.size(); I < End; ++I) {
+ SectionOffset A = Sections[I - 1];
+ SectionOffset B = Sections[I];
+ if (B.Offset < A.Offset + A.Sec->Size)
+ errorOrWarn(
+ "section " + A.Sec->Name + " " + Name + " range overlaps with " +
+ B.Sec->Name + "\n>>> " + A.Sec->Name + " range is " +
+ rangeToString(A.Offset, A.Sec->Size) + "\n>>> " + B.Sec->Name +
+ " range is " + rangeToString(B.Offset, B.Sec->Size));
}
}
@@ -1991,18 +2001,18 @@
// In this function we check that none of the output sections have overlapping
// file offsets. For SHF_ALLOC sections we also check that the load address
// ranges and the virtual address ranges don't overlap
-template <class ELFT> void Writer<ELFT>::checkNoOverlappingSections() {
+template <class ELFT> void Writer<ELFT>::checkSectionOverlap() {
// First check for overlapping file offsets. In this case we need to skip
- // Any section marked as SHT_NOBITS. These sections don't actually occupy
+ // any section marked as SHT_NOBITS. These sections don't actually occupy
// space in the file so Sec->Offset + Sec->Size can overlap with others.
// If --oformat binary is specified only add SHF_ALLOC sections are added to
// the output file so we skip any non-allocated sections in that case.
- checkForSectionOverlap(
- OutputSections, "file", [](const OutputSection *Sec) { return Sec->Offset; },
- [](const OutputSection *Sec) {
- return Sec->Type == SHT_NOBITS ||
- (Config->OFormatBinary && (Sec->Flags & SHF_ALLOC) == 0);
- });
+ std::vector<SectionOffset> FileOffs;
+ for (OutputSection *Sec : OutputSections)
+ if (0 < Sec->Size && Sec->Type != SHT_NOBITS &&
+ (!Config->OFormatBinary || (Sec->Flags & SHF_ALLOC)))
+ FileOffs.push_back({Sec, Sec->Offset});
+ checkOverlap("file", FileOffs);
// When linking with -r there is no need to check for overlapping virtual/load
// addresses since those addresses will only be assigned when the final
@@ -2015,19 +2025,20 @@
// Furthermore, we also need to skip SHF_TLS sections since these will be
// mapped to other addresses at runtime and can therefore have overlapping
// ranges in the file.
- auto SkipNonAllocSections = [](const OutputSection *Sec) {
- return (Sec->Flags & SHF_ALLOC) == 0 || (Sec->Flags & SHF_TLS);
- };
- checkForSectionOverlap(OutputSections, "virtual address",
- [](const OutputSection *Sec) { return Sec->Addr; },
- SkipNonAllocSections);
+ std::vector<SectionOffset> VMAs;
+ for (OutputSection *Sec : OutputSections)
+ if (0 < Sec->Size && (Sec->Flags & SHF_ALLOC) && !(Sec->Flags & SHF_TLS))
+ VMAs.push_back({Sec, Sec->Addr});
+ checkOverlap("virtual address", VMAs);
// Finally, check that the load addresses don't overlap. This will usually be
// the same as the virtual addresses but can be different when using a linker
// script with AT().
- checkForSectionOverlap(OutputSections, "load address",
- [](const OutputSection *Sec) { return Sec->getLMA(); },
- SkipNonAllocSections);
+ std::vector<SectionOffset> LMAs;
+ for (OutputSection *Sec : OutputSections)
+ if (0 < Sec->Size && (Sec->Flags & SHF_ALLOC) && !(Sec->Flags & SHF_TLS))
+ LMAs.push_back({Sec, Sec->getLMA()});
+ checkOverlap("load address", LMAs);
}
// The entry point address is chosen in the following ways.
@@ -2072,17 +2083,19 @@
}
static uint8_t getAbiVersion() {
- if (Config->EMachine == EM_MIPS) {
- // Increment the ABI version for non-PIC executable files.
- if (getELFType() == ET_EXEC &&
- (Config->EFlags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC)
- return 1;
- }
+ // MIPS non-PIC executable gets ABI version 1.
+ if (Config->EMachine == EM_MIPS && getELFType() == ET_EXEC &&
+ (Config->EFlags & (EF_MIPS_PIC | EF_MIPS_CPIC)) == EF_MIPS_CPIC)
+ return 1;
return 0;
}
template <class ELFT> void Writer<ELFT>::writeHeader() {
uint8_t *Buf = Buffer->getBufferStart();
+ // For executable segments, the trap instructions are written before writing
+ // the header. Setting Elf header bytes to zero ensures that any unused bytes
+ // in header are zero-cleared, instead of having trap instructions.
+ memset(Buf, 0, sizeof(Elf_Ehdr));
memcpy(Buf, "\177ELF", 4);
// Write the ELF header.
diff --git a/MinGW/Driver.cpp b/MinGW/Driver.cpp
index 6d3bea5..0b7e083 100644
--- a/MinGW/Driver.cpp
+++ b/MinGW/Driver.cpp
@@ -154,6 +154,8 @@
Add("-debug:dwarf");
if (Args.hasArg(OPT_large_address_aware))
Add("-largeaddressaware");
+ if (Args.hasArg(OPT_kill_at))
+ Add("-kill-at");
if (Args.getLastArgValue(OPT_m) != "thumb2pe" &&
Args.getLastArgValue(OPT_m) != "arm64pe" && !Args.hasArg(OPT_dynamicbase))
diff --git a/MinGW/Options.td b/MinGW/Options.td
index 4213b8e..6840471 100644
--- a/MinGW/Options.td
+++ b/MinGW/Options.td
@@ -14,6 +14,7 @@
def gc_sections: F<"gc-sections">, HelpText<"Remove unused sections">;
def icf: J<"icf=">, HelpText<"Identical code folding">;
def image_base: S<"image-base">, HelpText<"Base address of the program">;
+def kill_at: F<"kill-at">, HelpText<"Remove @n from exported symbols">;
def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"<libName>">,
HelpText<"Root name of library to use">;
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
diff --git a/cmake/modules/AddLLD.cmake b/cmake/modules/AddLLD.cmake
index 0d95179..fa48b42 100644
--- a/cmake/modules/AddLLD.cmake
+++ b/cmake/modules/AddLLD.cmake
@@ -10,7 +10,7 @@
llvm_add_library(${name} ${ARG_ENABLE_SHARED} ${ARG_UNPARSED_ARGUMENTS})
set_target_properties(${name} PROPERTIES FOLDER "lld libraries")
- if (LLD_BUILD_TOOLS)
+ if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR
NOT LLVM_DISTRIBUTION_COMPONENTS)
set(export_to_lldtargets EXPORT lldTargets)
diff --git a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
index 3b07a40..4617d23 100644
--- a/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
+++ b/lib/ReaderWriter/MachO/MachONormalizedFileToAtoms.cpp
@@ -906,7 +906,7 @@
abbrevData.getU8(&abbrevOffset);
uint32_t name;
llvm::dwarf::Form form;
- llvm::DWARFFormParams formParams = {version, addrSize, Format};
+ llvm::dwarf::FormParams formParams = {version, addrSize, Format};
TranslationUnitSource tu;
while ((name = abbrevData.getULEB128(&abbrevOffset)) |
(form = static_cast<llvm::dwarf::Form>(
diff --git a/test/COFF/Inputs/generic.yaml b/test/COFF/Inputs/generic.yaml
new file mode 100644
index 0000000..88c8765
--- /dev/null
+++ b/test/COFF/Inputs/generic.yaml
@@ -0,0 +1,282 @@
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+ - Name: .text
+ Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+ Alignment: 16
+ SectionData: 4883EC1831C0C7442414000000004889542408894C24044883C418C3
+ - Name: .data
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: ''
+ - Name: .bss
+ Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+ Alignment: 4
+ SectionData: ''
+ - Name: .xdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: '0104010004220000'
+ - Name: .drectve
+ Characteristics: [ IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE ]
+ Alignment: 1
+ SectionData: 202F44454641554C544C49423A6C6962636D742E6C6962202F44454641554C544C49423A6F6C646E616D65732E6C6962
+ - Name: '.debug$S'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 04000000F10000002F0000002D003C1101000000D0000700000000000000581B000000000000636C616E672076657273696F6E20372E302E30200000F1000000760000002A0047110000000000000000000000001C000000000000000000000003100000000000000000006D61696E000D003E117400000001006172676300120045114F0100000400000017000000000005000D003E110010000001006172677600120045114F01000008000000170000000000050002004F110000F20000002800000000000000000000001C00000000000000020000001C00000000000000020000001700000003000000F40000001800000001000000100139E9A066A1995A99DD01F5A392F26D7C0000F30000003000000000443A5C7372635C6C6C766D6275696C645C636C5C52656C656173655C7836345C67656E657269632E63707000000000
+ Subsections:
+ - !Symbols
+ Records:
+ - Kind: S_COMPILE3
+ Compile3Sym:
+ Flags: [ ]
+ Machine: X64
+ FrontendMajor: 7
+ FrontendMinor: 0
+ FrontendBuild: 0
+ FrontendQFE: 0
+ BackendMajor: 7000
+ BackendMinor: 0
+ BackendBuild: 0
+ BackendQFE: 0
+ Version: 'clang version 7.0.0 '
+ - !Symbols
+ Records:
+ - Kind: S_GPROC32_ID
+ ProcSym:
+ CodeSize: 28
+ DbgStart: 0
+ DbgEnd: 0
+ FunctionType: 4099
+ Flags: [ ]
+ DisplayName: main
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 116
+ Flags: [ IsParameter ]
+ VarName: argc
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ Register: 335
+ Flags: 0
+ BasePointerOffset: 4
+ Range:
+ OffsetStart: 23
+ ISectStart: 0
+ Range: 5
+ Gaps:
+ - Kind: S_LOCAL
+ LocalSym:
+ Type: 4096
+ Flags: [ IsParameter ]
+ VarName: argv
+ - Kind: S_DEFRANGE_REGISTER_REL
+ DefRangeRegisterRelSym:
+ Register: 335
+ Flags: 0
+ BasePointerOffset: 8
+ Range:
+ OffsetStart: 23
+ ISectStart: 0
+ Range: 5
+ Gaps:
+ - Kind: S_PROC_ID_END
+ ScopeEndSym:
+ - !Lines
+ CodeSize: 28
+ Flags: [ ]
+ RelocOffset: 0
+ RelocSegment: 0
+ Blocks:
+ - FileName: 'D:\src\llvmbuild\cl\Release\x64\generic.cpp'
+ Lines:
+ - Offset: 0
+ LineStart: 2
+ IsStatement: false
+ EndDelta: 0
+ - Offset: 23
+ LineStart: 3
+ IsStatement: false
+ EndDelta: 0
+ Columns:
+ - !FileChecksums
+ Checksums:
+ - FileName: 'D:\src\llvmbuild\cl\Release\x64\generic.cpp'
+ Kind: MD5
+ Checksum: 39E9A066A1995A99DD01F5A392F26D7C
+ - !StringTable
+ Strings:
+ - 'D:\src\llvmbuild\cl\Release\x64\generic.cpp'
+ - ''
+ - ''
+ - ''
+ Relocations:
+ - VirtualAddress: 100
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 104
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 139
+ SymbolName: .text
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 143
+ SymbolName: .text
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 174
+ SymbolName: .text
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 178
+ SymbolName: .text
+ Type: IMAGE_REL_AMD64_SECTION
+ - VirtualAddress: 196
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECREL
+ - VirtualAddress: 200
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_SECTION
+ - Name: '.debug$T'
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 040000000A000210700600000C0001000E0001120200000074000000001000000E0008107400000000000200011000001200011600000000021000006D61696E00F3F2F1
+ Types:
+ - Kind: LF_POINTER
+ Pointer:
+ ReferentType: 1648
+ Attrs: 65548
+ - Kind: LF_ARGLIST
+ ArgList:
+ ArgIndices: [ 116, 4096 ]
+ - Kind: LF_PROCEDURE
+ Procedure:
+ ReturnType: 116
+ CallConv: NearC
+ Options: [ None ]
+ ParameterCount: 2
+ ArgumentList: 4097
+ - Kind: LF_FUNC_ID
+ FuncId:
+ ParentScope: 0
+ FunctionType: 4098
+ Name: main
+ - Name: .pdata
+ Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+ Alignment: 4
+ SectionData: 000000001C00000000000000
+ Relocations:
+ - VirtualAddress: 0
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 4
+ SymbolName: main
+ Type: IMAGE_REL_AMD64_ADDR32NB
+ - VirtualAddress: 8
+ SymbolName: .xdata
+ Type: IMAGE_REL_AMD64_ADDR32NB
+symbols:
+ - Name: .text
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 28
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 594448369
+ Number: 1
+ - Name: .data
+ Value: 0
+ SectionNumber: 2
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 2
+ - Name: .bss
+ Value: 0
+ SectionNumber: 3
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 0
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 0
+ Number: 3
+ - Name: .xdata
+ Value: 0
+ SectionNumber: 4
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 8
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 1192424177
+ Number: 4
+ - Name: .drectve
+ Value: 0
+ SectionNumber: 5
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 48
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 149686238
+ Number: 5
+ - Name: '.debug$S'
+ Value: 0
+ SectionNumber: 6
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 324
+ NumberOfRelocations: 8
+ NumberOfLinenumbers: 0
+ CheckSum: 4196717433
+ Number: 6
+ - Name: '.debug$T'
+ Value: 0
+ SectionNumber: 7
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 68
+ NumberOfRelocations: 0
+ NumberOfLinenumbers: 0
+ CheckSum: 485351071
+ Number: 7
+ - Name: .pdata
+ Value: 0
+ SectionNumber: 8
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_NULL
+ StorageClass: IMAGE_SYM_CLASS_STATIC
+ SectionDefinition:
+ Length: 12
+ NumberOfRelocations: 3
+ NumberOfLinenumbers: 0
+ CheckSum: 722740324
+ Number: 8
+ - Name: main
+ Value: 0
+ SectionNumber: 1
+ SimpleType: IMAGE_SYM_TYPE_NULL
+ ComplexType: IMAGE_SYM_DTYPE_FUNCTION
+ StorageClass: IMAGE_SYM_CLASS_EXTERNAL
+...
diff --git a/test/COFF/Inputs/loadconfig-cfg-x64.s b/test/COFF/Inputs/loadconfig-cfg-x64.s
new file mode 100644
index 0000000..1440b11
--- /dev/null
+++ b/test/COFF/Inputs/loadconfig-cfg-x64.s
@@ -0,0 +1,11 @@
+# This is the _load_config_used definition needed for /guard:cf tests.
+
+ .section .rdata,"dr"
+.globl _load_config_used
+_load_config_used:
+ .long 256
+ .fill 124, 1, 0
+ .quad __guard_fids_table
+ .quad __guard_fids_count
+ .long __guard_flags
+ .fill 128, 1, 0
diff --git a/test/COFF/Inputs/natvis-1.natvis b/test/COFF/Inputs/natvis-1.natvis
new file mode 100644
index 0000000..8797a36
--- /dev/null
+++ b/test/COFF/Inputs/natvis-1.natvis
@@ -0,0 +1 @@
+1st Natvis Test
diff --git a/test/COFF/Inputs/natvis-2.natvis b/test/COFF/Inputs/natvis-2.natvis
new file mode 100644
index 0000000..50ba443
--- /dev/null
+++ b/test/COFF/Inputs/natvis-2.natvis
@@ -0,0 +1 @@
+Second Natvis Test
diff --git a/test/COFF/Inputs/natvis-3.natvis b/test/COFF/Inputs/natvis-3.natvis
new file mode 100644
index 0000000..a10b1a1
--- /dev/null
+++ b/test/COFF/Inputs/natvis-3.natvis
@@ -0,0 +1 @@
+Third Natvis Test
diff --git a/test/COFF/def-export-stdcall.s b/test/COFF/def-export-stdcall.s
index db25140..fd1ec2c 100644
--- a/test/COFF/def-export-stdcall.s
+++ b/test/COFF/def-export-stdcall.s
@@ -44,7 +44,8 @@
# DECORATED-EXPORTS: Name: vectorcall@@8
-# RUN: echo -e "LIBRARY foo\nEXPORTS\n stdcall@8\n @fastcall@8" > %t.def
+# GNU tools don't support vectorcall at the moment, but test it for completeness.
+# RUN: echo -e "LIBRARY foo\nEXPORTS\n stdcall@8\n @fastcall@8\n vectorcall@@8" > %t.def
# RUN: lld-link -lldmingw -entry:dllmain -dll -def:%t.def %t.obj -out:%t.dll -implib:%t.lib
# RUN: llvm-readobj %t.lib | FileCheck -check-prefix DECORATED-MINGW-IMPLIB %s
# RUN: llvm-readobj -coff-exports %t.dll | FileCheck -check-prefix DECORATED-MINGW-EXPORTS %s
@@ -55,9 +56,39 @@
# DECORATED-MINGW-IMPLIB: Name type: noprefix
# DECORATED-MINGW-IMPLIB-NEXT: __imp__stdcall@8
# DECORATED-MINGW-IMPLIB-NEXT: _stdcall@8
+# GNU tools don't support vectorcall, but this test is just to track that
+# lld's behaviour remains consistent over time.
+# DECORATED-MINGW-IMPLIB: Name type: name
+# DECORATED-MINGW-IMPLIB-NEXT: __imp_vectorcall@@8
+# DECORATED-MINGW-IMPLIB-NEXT: vectorcall@@8
# DECORATED-MINGW-EXPORTS: Name: @fastcall@8
# DECORATED-MINGW-EXPORTS: Name: stdcall@8
+# DECORATED-MINGW-EXPORTS: Name: vectorcall@@8
+
+# RUN: lld-link -lldmingw -kill-at -entry:dllmain -dll -def:%t.def %t.obj -out:%t.dll -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck -check-prefix MINGW-KILL-AT-IMPLIB %s
+# RUN: llvm-readobj -coff-exports %t.dll | FileCheck -check-prefix MINGW-KILL-AT-EXPORTS %s
+
+# RUN: lld-link -lldmingw -kill-at -entry:dllmain -dll %t.obj -out:%t.dll -implib:%t.lib
+# RUN: llvm-readobj %t.lib | FileCheck -check-prefix MINGW-KILL-AT-IMPLIB %s
+# RUN: llvm-readobj -coff-exports %t.dll | FileCheck -check-prefix MINGW-KILL-AT-EXPORTS %s
+
+# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB: __imp__fastcall
+# MINGW-KILL-AT-IMPLIB-NEXT: _fastcall
+# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB-NEXT: __imp__stdcall
+# MINGW-KILL-AT-IMPLIB-NEXT: _stdcall
+# GNU tools don't support vectorcall, but this test is just to track that
+# lld's behaviour remains consistent over time.
+# MINGW-KILL-AT-IMPLIB: Name type: noprefix
+# MINGW-KILL-AT-IMPLIB-NEXT: __imp__vectorcall
+# MINGW-KILL-AT-IMPLIB-NEXT: _vectorcall
+
+# MINGW-KILL-AT-EXPORTS: Name: fastcall
+# MINGW-KILL-AT-EXPORTS: Name: stdcall
+# MINGW-KILL-AT-EXPORTS: Name: vectorcall
.def _stdcall@8;
diff --git a/test/COFF/dll.test b/test/COFF/dll.test
index abd39f4..9bd8698 100644
--- a/test/COFF/dll.test
+++ b/test/COFF/dll.test
@@ -26,7 +26,7 @@
EXPORT2-NEXT: 2 0x101c mangled2
# RUN: llvm-as -o %t.lto.obj %p/Inputs/export.ll
-# RUN: lld-link /out:%t.lto.dll /dll %t.lto.obj /export:exportfn1 /export:exportfn2
+# RUN: lld-link -opt:noicf /out:%t.lto.dll /dll %t.lto.obj /export:exportfn1 /export:exportfn2
# RUN: llvm-objdump -p %t.lto.dll | FileCheck -check-prefix=EXPORT-LTO %s
EXPORT-LTO: Export Table:
diff --git a/test/COFF/error-limit.test b/test/COFF/error-limit.test
index 3eebd12..09c3b9d 100644
--- a/test/COFF/error-limit.test
+++ b/test/COFF/error-limit.test
@@ -3,27 +3,27 @@
DEFAULT: could not open 01
DEFAULT: could not open 20
-DEFAULT-NEXT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
+DEFAULT-NEXT: too many errors emitted, stopping now (use /errorlimit:0 to see all errors)
DEFAULT-NOT: could not open 21
-RUN: not lld-link /ERRORLIMIT:5 01 02 03 04 05 06 07 08 09 10 2>&1 \
+RUN: not lld-link /errorlimit:5 01 02 03 04 05 06 07 08 09 10 2>&1 \
RUN: | FileCheck -check-prefix=LIMIT5 %s
LIMIT5: could not open 01
LIMIT5: could not open 05
-LIMIT5-NEXT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
+LIMIT5-NEXT: too many errors emitted, stopping now (use /errorlimit:0 to see all errors)
LIMIT5-NOT: could not open 06
-RUN: not lld-link /ERRORLIMIT:0 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 \
+RUN: not lld-link /errorlimit:0 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 \
RUN: 16 17 18 19 20 21 22 2>&1 | FileCheck -check-prefix=UNLIMITED %s
UNLIMITED: could not open 01
UNLIMITED: could not open 20
UNLIMITED: could not open 21
UNLIMITED: could not open 22
-UNLIMITED-NOT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
+UNLIMITED-NOT: too many errors emitted, stopping now (use /errorlimit:0 to see all errors)
-RUN: not lld-link /ERRORLIMIT:XYZ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 \
+RUN: not lld-link /errorlimit:XYZ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 \
RUN: 15 16 17 18 19 20 21 22 2>&1 | FileCheck -check-prefix=WRONG %s
-WRONG: /ERRORLIMIT: number expected, but got XYZ
+WRONG: /errorlimit: number expected, but got XYZ
diff --git a/test/COFF/fixed.test b/test/COFF/fixed.test
new file mode 100644
index 0000000..6975fed
--- /dev/null
+++ b/test/COFF/fixed.test
@@ -0,0 +1,24 @@
+# REQUIRES: x86
+# RUN: yaml2obj < %p/Inputs/hello32.yaml > %t.obj
+#
+# RUN: lld-link %t.obj /fixed %p/Inputs/std32.lib /subsystem:console \
+# RUN: /entry:main@0 /debug /out:%t.fixed.exe
+# RUN: llvm-readobj -file-headers %t.fixed.exe | \
+# RUN: FileCheck -check-prefix=EXEFIXED %s
+#
+# RUN: lld-link %t.obj %p/Inputs/std32.lib /subsystem:console \
+# RUN: /entry:main@0 /debug /out:%t.exe
+# RUN: llvm-readobj -file-headers %t.exe | FileCheck -check-prefix=EXEREL %s
+#
+# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
+#
+# RUN: lld-link %t.obj /dll /fixed /debug /out:%t.fixed.dll
+# RUN: llvm-readobj -file-headers %t.fixed.dll | FileCheck -check-prefix=DLLFIXED %s
+#
+# RUN: lld-link %t.obj /dll /debug /out:%t.dll
+# RUN: llvm-readobj -file-headers %t.dll | FileCheck -check-prefix=DLLREL %s
+
+EXEFIXED-NOT: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE
+DLLFIXED-NOT: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE
+EXEREL: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE
+DLLREL: IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE
diff --git a/test/COFF/guardcf-lto.ll b/test/COFF/guardcf-lto.ll
new file mode 100644
index 0000000..8675117
--- /dev/null
+++ b/test/COFF/guardcf-lto.ll
@@ -0,0 +1,39 @@
+; Set up an import library for a DLL that will do the indirect call.
+; RUN: echo -e 'LIBRARY library\nEXPORTS\n do_indirect_call\n' > %t.def
+; RUN: lld-link -lib -def:%t.def -out:%t.lib -machine:x64
+
+; Generate an object that will have the load configuration normally provided by
+; the CRT.
+; RUN: llvm-mc -triple x86_64-windows-msvc -filetype=obj %S/Inputs/loadconfig-cfg-x64.s -o %t.ldcfg.obj
+
+; RUN: llvm-as %s -o %t.bc
+; RUN: lld-link -entry:main -guard:cf -dll %t.bc %t.lib %t.ldcfg.obj -out:%t.dll
+; RUN: llvm-readobj -coff-load-config %t.dll | FileCheck %s
+
+; There must be *two* entries in the table: DLL entry point, and my_handler.
+
+; CHECK: LoadConfig [
+; CHECK: GuardCFFunctionTable: 0x{{[^0].*}}
+; CHECK-NEXT: GuardCFFunctionCount: 2
+; CHECK-NEXT: GuardFlags: 0x10500
+; CHECK: ]
+; CHECK: GuardFidTable [
+; CHECK-NEXT: 0x180{{.*}}
+; CHECK-NEXT: 0x180{{.*}}
+; CHECK-NEXT: ]
+
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.12.25835"
+
+declare dllimport void @do_indirect_call(void ()*)
+
+define dso_local i32 @main() local_unnamed_addr {
+entry:
+ tail call void @do_indirect_call(void ()* nonnull @my_handler)
+ ret i32 0
+}
+
+define dso_local void @my_handler() {
+entry:
+ ret void
+}
diff --git a/test/COFF/hello32.test b/test/COFF/hello32.test
index 2399193..9f3cadc 100644
--- a/test/COFF/hello32.test
+++ b/test/COFF/hello32.test
@@ -11,7 +11,7 @@
HEADER-NEXT: ImageFileHeader {
HEADER-NEXT: Machine: IMAGE_FILE_MACHINE_I386 (0x14C)
HEADER-NEXT: SectionCount: 4
-HEADER-NEXT: TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+HEADER-NEXT: TimeDateStamp:
HEADER-NEXT: PointerToSymbolTable: 0x0
HEADER-NEXT: SymbolCount: 0
HEADER-NEXT: OptionalHeaderSize: 224
@@ -40,7 +40,7 @@
HEADER-NEXT: MajorSubsystemVersion: 6
HEADER-NEXT: MinorSubsystemVersion: 0
HEADER-NEXT: SizeOfImage: 20480
-HEADER-NEXT: SizeOfHeaders: 512
+HEADER-NEXT: SizeOfHeaders: 1024
HEADER-NEXT: Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI (0x3)
HEADER-NEXT: Characteristics [ (0x9540)
HEADER-NEXT: IMAGE_DLL_CHARACTERISTICS_APPCONTAINER (0x1000)
@@ -91,10 +91,10 @@
HEADER-NEXT: }
HEADER-NEXT: DOSHeader {
HEADER-NEXT: Magic: MZ
-HEADER-NEXT: UsedBytesInTheLastPage: 0
-HEADER-NEXT: FileSizeInPages: 0
+HEADER-NEXT: UsedBytesInTheLastPage: 120
+HEADER-NEXT: FileSizeInPages: 1
HEADER-NEXT: NumberOfRelocationItems: 0
-HEADER-NEXT: HeaderSizeInParagraphs: 0
+HEADER-NEXT: HeaderSizeInParagraphs: 4
HEADER-NEXT: MinimumExtraParagraphs: 0
HEADER-NEXT: MaximumExtraParagraphs: 0
HEADER-NEXT: InitialRelativeSS: 0
@@ -106,7 +106,7 @@
HEADER-NEXT: OverlayNumber: 0
HEADER-NEXT: OEMid: 0
HEADER-NEXT: OEMinfo: 0
-HEADER-NEXT: AddressOfNewExeHeader: 64
+HEADER-NEXT: AddressOfNewExeHeader: 120
HEADER-NEXT: }
IMPORTS: Format: COFF-i386
diff --git a/test/COFF/icf-simple.test b/test/COFF/icf-simple.test
index ead7e76..19f13c6 100644
--- a/test/COFF/icf-simple.test
+++ b/test/COFF/icf-simple.test
@@ -23,6 +23,11 @@
# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
# RUN: FileCheck -check-prefix=NOICF %s < %t.log
+# /profile disables ICF.
+# RUN: lld-link /profile /entry:foo /out:%t.exe /subsystem:console \
+# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
+# RUN: FileCheck -check-prefix=NOICF %s < %t.log
+
# /opt:noref disables ICF.
# RUN: lld-link /opt:noref /entry:foo /out:%t.exe /subsystem:console \
# RUN: /include:bar /verbose %t.obj > %t.log 2>&1
diff --git a/test/COFF/incremental.test b/test/COFF/incremental.test
index d5a5ba8..01f2748 100644
--- a/test/COFF/incremental.test
+++ b/test/COFF/incremental.test
@@ -1,4 +1,5 @@
# RUN: yaml2obj < %p/Inputs/export.yaml > %t.obj
+
# RUN: lld-link -out:%t.dll -dll %t.obj 2>&1 \
# RUN: | FileCheck -allow-empty -check-prefix=NOWARN %s
# RUN: touch -t 198002011200.00 %t.lib
@@ -11,6 +12,12 @@
# RUN: lld-link -out:%t.dll -dll -incremental %t.obj
# RUN: ls -l %t.lib | FileCheck -check-prefix=NOKEEP %s
+# RUN: lld-link -out:%t.dll -dll -incremental -opt:noref,noicf %t.obj 2>&1 \
+# RUN: | FileCheck -allow-empty -check-prefix=NOWARN %s
+# RUN: touch -t 198002011200.00 %t.lib
+# RUN: lld-link -out:%t.dll -dll -incremental -opt:noref,noicf %t.obj
+# RUN: ls -l %t.lib | FileCheck -check-prefix=KEEP %s
+
# RUN: lld-link -out:%t.dll -dll -debug %t.obj 2>&1 \
# RUN: | FileCheck -allow-empty -check-prefix=NOWARN %s
# RUN: touch -t 198002011200.00 %t.lib
@@ -53,14 +60,41 @@
# RUN: lld-link -out:%t.dll -dll -incremental -opt:ref %t.obj
# RUN: ls -l %t.lib | FileCheck -check-prefix=NOKEEP %s
+# RUN: touch %t.order
+# RUN: lld-link -out:%t.dll -dll -incremental -opt:noref,noicf %t.obj \
+# RUN: -order:@%t.order 2>&1 | FileCheck -check-prefix=WARN-ORDER %s
+# RUN: touch -t 198002011200.00 %t.lib
+# RUN: lld-link -out:%t.dll -dll -incremental -opt:noref,noicf -order:@%t.order %t.obj
+# RUN: ls -l %t.lib | FileCheck -check-prefix=NOKEEP %s
+
+# RUN: lld-link -out:%t.dll -dll -opt:noref,noicf %t.obj \
+# RUN: -order:@%t.order 2>&1 | FileCheck -allow-empty -check-prefix=NOWARN %s
+# RUN: touch -t 198002011200.00 %t.lib
+# RUN: lld-link -out:%t.dll -dll -opt:noref,noicf -order:@%t.order %t.obj
+# RUN: ls -l %t.lib | FileCheck -check-prefix=NOKEEP %s
+
+# RUN: lld-link -out:%t.dll -dll -incremental -opt:noref,noicf %t.obj \
+# RUN: -profile 2>&1 | FileCheck -check-prefix=WARN-PROFILE %s
+# RUN: touch -t 198002011200.00 %t.lib
+# RUN: lld-link -out:%t.dll -dll -incremental -opt:noref,noicf -profile %t.obj
+# RUN: ls -l %t.lib | FileCheck -check-prefix=NOKEEP %s
+
+# RUN: lld-link -out:%t.dll -dll -opt:noref,noicf %t.obj \
+# RUN: -profile 2>&1 | FileCheck -allow-empty -check-prefix=NOWARN %s
+# RUN: touch -t 198002011200.00 %t.lib
+# RUN: lld-link -out:%t.dll -dll -opt:noref,noicf -profile %t.obj
+# RUN: ls -l %t.lib | FileCheck -check-prefix=NOKEEP %s
+
# RUN: lld-link -out:%t.dll -dll -debug -opt:ref %t.obj 2>&1 \
# RUN: | FileCheck -allow-empty -check-prefix=NOWARN %s
# RUN: touch -t 198002011200.00 %t.lib
# RUN: lld-link -out:%t.dll -dll -debug -opt:ref %t.obj
# RUN: ls -l %t.lib | FileCheck -check-prefix=NOKEEP %s
-# NOWARN-NOT: ignoring '/INCREMENTAL'
-# WARN-ICF: ignoring '/INCREMENTAL' because ICF is enabled; use '/OPT:NOICF' to disable
-# WARN-REF: ignoring '/INCREMENTAL' because REF is enabled; use '/OPT:NOREF' to disable
+# NOWARN-NOT: ignoring '/incremental'
+# WARN-ICF: ignoring '/incremental' because ICF is enabled; use '/opt:noicf' to disable
+# WARN-REF: ignoring '/incremental' because REF is enabled; use '/opt:noref' to disable
+# WARN-ORDER: ignoring '/incremental' due to '/order' specification
+# WARN-PROFILE: ignoring '/incremental' due to '/profile' specification
# KEEP: {{Feb 1 1980|1980-02-01}}
# NOKEEP-NOT: {{Feb 1 1980|1980-02-01}}
diff --git a/test/COFF/largeaddressaware.test b/test/COFF/largeaddressaware.test
index d035e7c..76aadee 100644
--- a/test/COFF/largeaddressaware.test
+++ b/test/COFF/largeaddressaware.test
@@ -9,7 +9,7 @@
HEADER-NEXT: ImageFileHeader {
HEADER-NEXT: Machine: IMAGE_FILE_MACHINE_I386 (0x14C)
HEADER-NEXT: SectionCount: 4
-HEADER-NEXT: TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+HEADER-NEXT: TimeDateStamp:
HEADER-NEXT: PointerToSymbolTable: 0x0
HEADER-NEXT: SymbolCount: 0
HEADER-NEXT: OptionalHeaderSize: 224
diff --git a/test/COFF/lto-comdat.ll b/test/COFF/lto-comdat.ll
index b255f69..9594d30 100644
--- a/test/COFF/lto-comdat.ll
+++ b/test/COFF/lto-comdat.ll
@@ -49,18 +49,17 @@
; TEXT-01: Disassembly of section .text:
; TEXT-01-NEXT: .text:
; TEXT-01-NEXT: subq $40, %rsp
-; TEXT-01-NEXT: callq 39
-; TEXT-01-NEXT: callq 50
+; TEXT-01-NEXT: callq 23
+; TEXT-01-NEXT: callq 18
; TEXT-01-NEXT: callq 13
; TEXT-01-NEXT: xorl %eax, %eax
; TEXT-01-NEXT: addq $40, %rsp
; TEXT-01: retq
; TEXT-01-NOT: callq
; TEXT-01: retq
-; TEXT-01-NOT: callq
-; TEXT-01: retq
-; TEXT-01-NOT: callq
-; TEXT-01: retq
+; TEXT-01: int3
+; TEXT-01: int3
+; TEXT-01: int3
; TEXT-01-NOT: {{.}}
; HEADERS-10: AddressOfEntryPoint: 0x2020
diff --git a/test/COFF/lto-icf.ll b/test/COFF/lto-icf.ll
new file mode 100644
index 0000000..291188f
--- /dev/null
+++ b/test/COFF/lto-icf.ll
@@ -0,0 +1,26 @@
+; Test that ICF works after LTO, i.e. both functions have the same address.
+; Previously, when we didn't enable function sections, ICF didn't work.
+
+; RUN: llvm-as %s -o %t.bc
+; RUN: lld-link -opt:icf -dll -noentry %t.bc -out:%t.dll
+; RUN: llvm-readobj -coff-exports %t.dll | FileCheck %s
+
+; CHECK: Export {
+; CHECK: Export {
+; CHECK: RVA: 0x[[RVA:.*]]
+; CHECK: Export {
+; CHECK: RVA: 0x[[RVA]]
+; CHECK-NOT: Export
+
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.12.25835"
+
+define dllexport i8* @icf_ptr() {
+entry:
+ ret i8* null
+}
+
+define dllexport i64 @icf_int() {
+entry:
+ ret i64 0
+}
diff --git a/test/COFF/lto-parallel.ll b/test/COFF/lto-parallel.ll
index 449e3a0..df0dc25 100644
--- a/test/COFF/lto-parallel.ll
+++ b/test/COFF/lto-parallel.ll
@@ -1,11 +1,12 @@
; RUN: llvm-as -o %t.obj %s
-; RUN: lld-link /out:%t.exe /entry:foo /include:bar /opt:lldltopartitions=2 /subsystem:console /lldmap:%t.map %t.obj
+; RUN: lld-link -opt:noicf /out:%t.exe /entry:foo /include:bar /opt:lldltopartitions=2 /subsystem:console /lldmap:%t.map %t.obj
; RUN: FileCheck %s < %t.map
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc"
; CHECK: lto.tmp
+; CHECK: lto.tmp
; CHECK-NEXT: foo
define void @foo() {
call void @bar()
@@ -13,6 +14,7 @@
}
; CHECK: lto.tmp
+; CHECK: lto.tmp
; CHECK: bar
define void @bar() {
call void @foo()
diff --git a/test/COFF/opt.test b/test/COFF/opt.test
index a8b0e2e..ed43b60 100644
--- a/test/COFF/opt.test
+++ b/test/COFF/opt.test
@@ -1,18 +1,24 @@
# RUN: yaml2obj < %s > %t.obj
# RUN: lld-link /out:%t.exe /entry:main %t.obj \
-# RUN: /verbose >& %t.log
-### FileCheck doesn't like empty input, so write something.
-# RUN: echo dummy >> %t.log
-# RUN: FileCheck -check-prefix=CHECK1 %s < %t.log
+# RUN: /verbose 2>&1 | FileCheck -check-prefix=REF %s
+
+# /debug disables the /opt:ref default...
+# RUN: lld-link /out:%t.exe /debug /entry:main %t.obj \
+# RUN: /verbose 2>&1 | FileCheck -check-prefix=NOREF %s
+
+# ...unless /profile is passed as well.
+# RUN: lld-link /out:%t.exe /profile /debug /entry:main %t.obj \
+# RUN: /verbose 2>&1 | FileCheck -check-prefix=REF %s
+
+# RUN: lld-link /out:%t.exe /opt:ref /debug /entry:main %t.obj \
+# RUN: /verbose 2>&1 | FileCheck -check-prefix=REF %s
# RUN: lld-link /out:%t.exe /entry:main %t.obj \
-# RUN: /verbose /opt:noref >& %t.log
-# RUN: echo dummy >> %t.log
-# RUN: FileCheck -check-prefix=CHECK2 %s < %t.log
+# RUN: /verbose /opt:noref /profile 2>&1 | FileCheck -check-prefix=NOREF %s
-# CHECK1: Discarded unused
-# CHECK2-NOT: Discarded unused
+# REF: Discarded unused
+# NOREF-NOT: Discarded unused
--- !COFF
header:
diff --git a/test/COFF/order.test b/test/COFF/order.test
index a01f820..0006550 100644
--- a/test/COFF/order.test
+++ b/test/COFF/order.test
@@ -16,7 +16,7 @@
# CHECK: unrelated2
# RUN: lld-link -entry:fn1 -subsystem:console -opt:noref -debug %t1.obj %t2.obj \
-# RUN: -lldmap:- -out:%t.exe | FileCheck -check-prefix=DEFAULT %s
+# RUN: -lldmap:- -ignore:4037 -out:%t.exe | FileCheck -check-prefix=DEFAULT %s
# DEFAULT: fn2
# DEFAULT: fn3
# DEFAULT: unrelated1
@@ -35,6 +35,13 @@
# WARN-NOT: f2
# WARN-NOT: f3
# WARN-NOT: f4
+# RUN: lld-link -entry:fn1 -subsystem:console -debug %t1.obj %t2.obj \
+# RUN: -out:%t.exe -order:@%t2.order -ignore:4037 2>&1 | \
+# RUN: FileCheck -allow-empty -check-prefix=NOWARN %s
+# NOWARN-NOT: warning: /order:{{.*}} missing symbol: foo
+# NOWARN-NOT: f2
+# NOWARN-NOT: f3
+# NOWARN-NOT: f4
--- !COFF
header:
diff --git a/test/COFF/pdb-diff.test b/test/COFF/pdb-diff.test
deleted file mode 100644
index 17d26b6..0000000
--- a/test/COFF/pdb-diff.test
+++ /dev/null
@@ -1,215 +0,0 @@
-This test verifies that we produce PDBs compatible with MSVC in various ways.
-We check in a cl-generated object file, PDB, and original source which serve
-as the "baseline" for us to measure against. Then we link the same object
-file with LLD and compare the two PDBs. Since the baseline object file and
-PDB are already checked in, we just run LLD on the object file.
-
-RUN: rm -f %T/pdb-diff-lld.pdb %T/pdb-diff-lld.exe
-RUN: lld-link /debug /pdb:%T/pdb-diff-lld.pdb /out:%T/pdb-diff-lld.exe /nodefaultlib \
-RUN: /entry:main %S/Inputs/pdb-diff.obj
-RUN: llvm-pdbutil diff -result -values=false -left-bin-root=%S -right-bin-root=D:/src/llvm-mono/lld/test/COFF/ \
-RUN: %T/pdb-diff-lld.pdb %S/Inputs/pdb-diff-cl.pdb | FileCheck %s
-
-CHECK: ----------------------
-CHECK-NEXT: | MSF Super Block |
-CHECK-NEXT: |----------------+---|
-CHECK-NEXT: | File | |
-CHECK-NEXT: |----------------+---|
-CHECK-NEXT: | Block Size | I |
-CHECK-NEXT: |----------------+---|
-CHECK-NEXT: | Block Count |
-CHECK-NEXT: |----------------+---|
-CHECK-NEXT: | Unknown 1 | I |
-CHECK-NEXT: |----------------+---|
-CHECK-NEXT: | Directory Size |
-CHECK-NEXT: |----------------+---|
-CHECK-NEXT: ------------------------------------
-CHECK-NEXT: | Stream Directory |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | File | |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Stream Count | I |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Old MSF Directory | I |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | PDB Stream | I |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | TPI Stream | I |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | DBI Stream | I |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | IPI Stream | I |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | New FPO Data | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Section Header Data | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Named Stream "/names" | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Named Stream "/LinkInfo" | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Module "Inputs\pdb-diff.obj" | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Module "* Linker *" | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | TPI Hash | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | IPI Hash | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Public Symbol Hash | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Global Symbol Hash | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Symbol Records | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: ------------------------------------
-CHECK-NEXT: | String Table |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | File | |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Number of Strings | D |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Hash Version | I |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Byte Size |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Signature | I |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | Empty Strings |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | {{.*}}pdb-diff.cpp | {{[EI]}} |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | $T0 $ebp = $...p $T0 8 + = | D |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: | d:\src\llvm-...er internal) | D |
-CHECK-NEXT: |------------------------------+---|
-CHECK-NEXT: ----------------------------
-CHECK-NEXT: | PDB Stream |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | File | |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Stream Size |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Age | I |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Guid | D |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Signature | D |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Version | I |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Features (set) | I |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Feature | I |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Named Stream Size |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | Named Streams (map) | {{[EI]}} |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | /names | {{[EI]}} |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: | /LinkInfo | {{[EI]}} |
-CHECK-NEXT: |----------------------+---|
-CHECK-NEXT: ----------------------------------------------
-CHECK-NEXT: | DBI Stream |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | File | |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Dbi Version | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Age | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Machine | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Flags | D |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Build Major | D |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Build Minor | D |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Build Number | D |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | PDB DLL Version | D |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | PDB DLL RBLD | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (FPO) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (Exception) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (Fixup) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (OmapToSrc) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (OmapFromSrc) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (SectionHdr) | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (TokenRidMap) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (Xdata) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (Pdata) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (NewFPO) | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | DBG (SectionHdrOrig) | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Globals Stream | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Publics Stream | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Symbol Records | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Has CTypes | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Is Incrementally Linked | D |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Is Stripped | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Module Count | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Source File Count | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Module "Inputs\pdb-diff.obj" |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Modi | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Obj File Name | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Debug Stream | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - C11 Byte Size | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - C13 Byte Size | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - # of files | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Pdb File Path Index | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Source File Name Index | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Symbol Byte Size |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | Module "* Linker *" |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Modi | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Obj File Name | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Debug Stream | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - C11 Byte Size | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - C13 Byte Size | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - # of files | I |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Pdb File Path Index | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Source File Name Index | {{[EI]}} |
-CHECK-NEXT: |----------------------------------------+---|
-CHECK-NEXT: | - Symbol Byte Size |
-CHECK-NEXT: |----------------------------------------+---|
-
-
diff --git a/test/COFF/pdb-file-static.test b/test/COFF/pdb-file-static.test
index 1d09823..f08f717 100644
--- a/test/COFF/pdb-file-static.test
+++ b/test/COFF/pdb-file-static.test
@@ -43,9 +43,9 @@
# CHECK: ============================================================
# CHECK-LABEL: Mod 0000 | `{{.*}}a.obj`:
# CHECK: 232 | S_FILESTATIC [size = 16] `x`
-# CHECK-NEXT: type = 0x0074 (int), file name = 1 (D:\src\llvmbuild\cl\Debug\x64\a.obj), flags = enreg global | enreg static
+# CHECK-NEXT: type = 0x0074 (int), file name = 2 (D:\src\llvmbuild\cl\Debug\x64\a.obj), flags = enreg global | enreg static
# CHECK: Mod 0001 | `{{.*}}b.obj`:
# CHECK: 232 | S_FILESTATIC [size = 16] `y`
-# CHECK-NEXT: type = 0x0074 (int), file name = 73 (D:\src\llvmbuild\cl\Debug\x64\b.obj), flags = enreg global | enreg static
+# CHECK-NEXT: type = 0x0074 (int), file name = 74 (D:\src\llvmbuild\cl\Debug\x64\b.obj), flags = enreg global | enreg static
# CHECK-LABEL: Mod 0002 | `* Linker *`:
diff --git a/test/COFF/pdb-lib.s b/test/COFF/pdb-lib.s
index 319d4bc..c970f0b 100644
--- a/test/COFF/pdb-lib.s
+++ b/test/COFF/pdb-lib.s
@@ -13,15 +13,15 @@
# CHECK-NEXT: ============================================================
# CHECK-NEXT: Mod 0000 | `{{.*pdb-lib.s.tmp[/\\]foo.obj}}`:
# CHECK-NEXT: Obj: `{{.*pdb-lib.s.tmp[/\\]foo.obj}}`:
-# CHECK-NEXT: debug stream: 9, # files: 0, has ec info: false
+# CHECK-NEXT: debug stream: 10, # files: 0, has ec info: false
# CHECK-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
# CHECK-NEXT: Mod 0001 | `bar.obj`:
# CHECK-NEXT: Obj: `{{.*pdb-lib.s.tmp[/\\]bar.lib}}`:
-# CHECK-NEXT: debug stream: 10, # files: 0, has ec info: false
+# CHECK-NEXT: debug stream: 11, # files: 0, has ec info: false
# CHECK-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
# CHECK-NEXT: Mod 0002 | `* Linker *`:
# CHECK-NEXT: Obj: ``:
-# CHECK-NEXT: debug stream: 11, # files: 0, has ec info: false
+# CHECK-NEXT: debug stream: 12, # files: 0, has ec info: false
# CHECK-NEXT: pdb file ni: 1 `{{.*foo.pdb}}`, src file ni: 0 ``
.def _main;
diff --git a/test/COFF/pdb-linker-module.test b/test/COFF/pdb-linker-module.test
index 1bb5729..96ca1b4 100644
--- a/test/COFF/pdb-linker-module.test
+++ b/test/COFF/pdb-linker-module.test
@@ -4,7 +4,7 @@
MODS: Mod 0001 | `* Linker *`
MODS-NEXT: Obj: ``:
-MODS-NEXT: debug stream: 10, # files: 0, has ec info: false
+MODS-NEXT: debug stream: 12, # files: 0, has ec info: false
MODS-NEXT: pdb file ni: 1 `{{.*}}pdb-linker-module.test.tmp.pdb`, src file ni: 0 ``
SYMS: Mod 0001 | `* Linker *`
diff --git a/test/COFF/pdb-natvis.test b/test/COFF/pdb-natvis.test
new file mode 100644
index 0000000..2db68b6
--- /dev/null
+++ b/test/COFF/pdb-natvis.test
@@ -0,0 +1,26 @@
+REQUIRES: diasdk
+
+RUN: yaml2obj %p/Inputs/generic.yaml > %t.obj
+RUN: lld-link /DEBUG %t.obj /nodefaultlib /entry:main /NATVIS:%p/Inputs/natvis-1.natvis \
+RUN: /NATVIS:%p/Inputs/natvis-2.natvis /NATVIS:%p/Inputs/natvis-3.natvis /OUT:%t.exe \
+RUN: /PDB:%t.pdb
+RUN: llvm-pdbutil pretty -injected-sources -injected-source-content %t.pdb | FileCheck \
+RUN: --check-prefix=CHECK-FIRST %s
+RUN: llvm-pdbutil pretty -injected-sources -injected-source-content %t.pdb | FileCheck \
+RUN: --check-prefix=CHECK-SECOND %s
+RUN: llvm-pdbutil pretty -injected-sources -injected-source-content %t.pdb | FileCheck \
+RUN: --check-prefix=CHECK-THIRD %s
+
+RUN: lld-link /DEBUG %t.obj /nodefaultlib /entry:main /NATVIS:%p/Inputs/test2.natvis \
+RUN: /OUT:%t.exe /PDB:%t.pdb 2>&1 | FileCheck --check-prefix=CHECK-MISSING %s
+
+CHECK-FIRST: {{.*}}natvis-1.natvis (16 bytes): obj=<null>, vname={{.*}}natvis-1.natvis, crc=355285096, compression=None
+CHECK-FIRST-NEXT: 1st Natvis Test
+
+CHECK-SECOND: {{.*}}natvis-2.natvis (19 bytes): obj=<null>, vname={{.*}}natvis-2.natvis, crc=4252640062, compression=None
+CHECK-SECOND-NEXT: Second Natvis Test
+
+CHECK-THIRD: {{.*}}natvis-3.natvis (18 bytes): obj=<null>, vname={{.*}}natvis-3.natvis, crc=2069719849, compression=None
+CHECK-THIRD-NEXT: Third Natvis Test
+
+CHECK-MISSING: Cannot open input file: {{.*}}test2.natvis
\ No newline at end of file
diff --git a/test/COFF/pdb-same-name.test b/test/COFF/pdb-same-name.test
index 76db69f..352bfc9 100644
--- a/test/COFF/pdb-same-name.test
+++ b/test/COFF/pdb-same-name.test
@@ -15,9 +15,9 @@
RAW-NEXT: ============================================================
RAW-NEXT: Mod 0000 | `foo.obj`:
RAW-NEXT: Obj: `{{.*}}1\foo.lib`:
-RAW-NEXT: debug stream: 9, # files: 1, has ec info: false
+RAW-NEXT: debug stream: 11, # files: 1, has ec info: false
RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
RAW-NEXT: Mod 0001 | `foo.obj`:
RAW-NEXT: Obj: `{{.*}}2\foo.lib`:
-RAW-NEXT: debug stream: 10, # files: 1, has ec info: false
+RAW-NEXT: debug stream: 12, # files: 1, has ec info: false
RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
diff --git a/test/COFF/pdb.test b/test/COFF/pdb.test
index dad6de2..faab5fe 100644
--- a/test/COFF/pdb.test
+++ b/test/COFF/pdb.test
@@ -14,7 +14,7 @@
# CHECK: MSF:
# CHECK-NEXT: SuperBlock:
# CHECK-NEXT: BlockSize: 4096
-# CHECK-NEXT: FreeBlockMap: 1
+# CHECK-NEXT: FreeBlockMap: 2
# CHECK-NEXT: NumBlocks:
# CHECK-NEXT: NumDirectoryBytes:
# CHECK-NEXT: Unknown1: 0
@@ -121,15 +121,15 @@
RAW-NEXT: ============================================================
RAW-NEXT: Mod 0000 | `{{.*}}pdb.test.tmp1.obj`:
RAW-NEXT: Obj: `{{.*}}pdb.test.tmp1.obj`:
-RAW-NEXT: debug stream: 9, # files: 1, has ec info: false
+RAW-NEXT: debug stream: 11, # files: 1, has ec info: false
RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
RAW-NEXT: Mod 0001 | `{{.*}}pdb.test.tmp2.obj`:
RAW-NEXT: Obj: `{{.*}}pdb.test.tmp2.obj`:
-RAW-NEXT: debug stream: 10, # files: 1, has ec info: false
+RAW-NEXT: debug stream: 12, # files: 1, has ec info: false
RAW-NEXT: pdb file ni: 0 ``, src file ni: 0 ``
RAW-NEXT: Mod 0002 | `* Linker *`:
RAW-NEXT: Obj: ``:
-RAW-NEXT: debug stream: 11, # files: 0, has ec info: false
+RAW-NEXT: debug stream: 13, # files: 0, has ec info: false
RAW-NEXT: pdb file ni: 1 `{{.*pdb.test.tmp.pdb}}`, src file ni: 0 ``
RAW: Types (TPI Stream)
RAW-NEXT: ============================================================
diff --git a/test/COFF/rsds.test b/test/COFF/rsds.test
index 1765977..10c2c56 100644
--- a/test/COFF/rsds.test
+++ b/test/COFF/rsds.test
@@ -18,7 +18,7 @@
# CHECK: DebugDirectory [
# CHECK: DebugEntry {
# CHECK: Characteristics: 0x0
-# CHECK: TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+# CHECK: TimeDateStamp:
# CHECK: MajorVersion: 0x0
# CHECK: MinorVersion: 0x0
# CHECK: Type: CodeView (0x2)
@@ -37,7 +37,7 @@
# CHECK: DebugDirectory [
# CHECK: DebugEntry {
# CHECK: Characteristics: 0x0
-# CHECK: TimeDateStamp: 1970-01-01 00:00:00 (0x0)
+# CHECK: TimeDateStamp:
# CHECK: MajorVersion: 0x0
# CHECK: MinorVersion: 0x0
# CHECK: Type: CodeView (0x2)
diff --git a/test/COFF/string-tail-merge.s b/test/COFF/string-tail-merge.s
new file mode 100644
index 0000000..f55041f
--- /dev/null
+++ b/test/COFF/string-tail-merge.s
@@ -0,0 +1,87 @@
+# REQUIRES: x86
+# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t.obj %s
+# RUN: lld-link %t.obj /out:%t.exe /entry:main /subsystem:console
+# RUN: llvm-objdump -s %t.exe | FileCheck %s
+
+# CHECK: Contents of section .rdata:
+# CHECK-NEXT: 140002000 68656c6c 6f20776f 726c6400 6fa26ca4 hello world.o.l.
+# CHECK-NEXT: 140002010 0068656c 6c6f2077 6f726c64 00006865 .hello world..he
+# CHECK-NEXT: 140002020 6c6c6f20 776f726c 64006800 65006c00 llo world.h.e.l.
+# CHECK-NEXT: 140002030 6c006f00 20007700 6f007200 6c006400 l.o. .w.o.r.l.d.
+# CHECK-NEXT: 140002040 0000 ..
+
+# CHECK: Contents of section .text:
+.globl main
+main:
+# CHECK-NEXT: 140003000 11200040 01000000 17200040 01000000
+.8byte "??_C@_0M@LACCCNMM@hello?5world?$AA@"
+.8byte "??_C@_05MCBCHHEJ@world?$AA@"
+# CHECK-NEXT: 140003010 2a200040 01000000 36200040 01000000
+.8byte "??_C@_1BI@HHJHKLLN@?$AAh?$AAe?$AAl?$AAl?$AAo?$AA?5?$AAw?$AAo?$AAr?$AAl?$AAd?$AA?$AA@"
+.8byte "??_C@_1M@NBBDDHIO@?$AAw?$AAo?$AAr?$AAl?$AAd?$AA?$AA@"
+# CHECK-NEXT: 140003020 00200040 01000000 0c200040 01000000
+.8byte "??_D@not_a_string_literal"
+.8byte "??_C@string_literal_with_relocs"
+# CHECK-NEXT: 140003030 00100040 01000000 1e200040 01000000
+.8byte "??_C@string_literal_in_wrong_section"
+.8byte "??_C@overaligned_string_literal"
+
+.section .rdata,"dr",discard,"??_C@_0M@LACCCNMM@hello?5world?$AA@"
+.globl "??_C@_0M@LACCCNMM@hello?5world?$AA@"
+"??_C@_0M@LACCCNMM@hello?5world?$AA@":
+.asciz "hello world"
+
+.section .rdata,"dr",discard,"??_C@_05MCBCHHEJ@world?$AA@"
+.globl "??_C@_05MCBCHHEJ@world?$AA@"
+"??_C@_05MCBCHHEJ@world?$AA@":
+.asciz "world"
+
+.section .rdata,"dr",discard,"??_C@_1BI@HHJHKLLN@?$AAh?$AAe?$AAl?$AAl?$AAo?$AA?5?$AAw?$AAo?$AAr?$AAl?$AAd?$AA?$AA@"
+.globl "??_C@_1BI@HHJHKLLN@?$AAh?$AAe?$AAl?$AAl?$AAo?$AA?5?$AAw?$AAo?$AAr?$AAl?$AAd?$AA?$AA@"
+.p2align 1
+"??_C@_1BI@HHJHKLLN@?$AAh?$AAe?$AAl?$AAl?$AAo?$AA?5?$AAw?$AAo?$AAr?$AAl?$AAd?$AA?$AA@":
+.short 104
+.short 101
+.short 108
+.short 108
+.short 111
+.short 32
+.short 119
+.short 111
+.short 114
+.short 108
+.short 100
+.short 0
+
+.section .rdata,"dr",discard,"??_C@_1M@NBBDDHIO@?$AAw?$AAo?$AAr?$AAl?$AAd?$AA?$AA@"
+.globl "??_C@_1M@NBBDDHIO@?$AAw?$AAo?$AAr?$AAl?$AAd?$AA?$AA@"
+.p2align 1
+"??_C@_1M@NBBDDHIO@?$AAw?$AAo?$AAr?$AAl?$AAd?$AA?$AA@":
+.short 119
+.short 111
+.short 114
+.short 108
+.short 100
+.short 0
+
+.section .data,"drw",discard,"??_C@string_literal_in_wrong_section"
+.globl "??_C@string_literal_in_wrong_section"
+"??_C@string_literal_in_wrong_section":
+.asciz "hello world"
+
+.section .rdata,"dr",discard,"??_D@not_a_string_literal"
+.globl "??_D@not_a_string_literal"
+"??_D@not_a_string_literal":
+.asciz "hello world"
+
+.section .rdata,"dr",discard,"??_C@string_literal_with_relocs"
+.globl "??_C@string_literal_with_relocs"
+"??_C@string_literal_with_relocs":
+.4byte main + 111 + (114 << 8) + (108 << 16) + (100 << 24) # main + "orld"
+.byte 0
+
+.section .rdata,"dr",discard,"??_C@overaligned_string_literal"
+.globl "??_C@overaligned_string_literal"
+.p2align 1
+"??_C@overaligned_string_literal":
+.asciz "hello world"
diff --git a/test/COFF/weak-external.test b/test/COFF/weak-external.test
index 7bdadd9..141d5ed 100644
--- a/test/COFF/weak-external.test
+++ b/test/COFF/weak-external.test
@@ -5,6 +5,7 @@
# RUN: FileCheck %s < %t2.map
# CHECK: lto.tmp
+# CHECK-NEXT: lto.tmp
# CHECK-NEXT: 0 g
--- !COFF
diff --git a/test/COFF/weak-external3.test b/test/COFF/weak-external3.test
index a06ce48..8d2bde2 100644
--- a/test/COFF/weak-external3.test
+++ b/test/COFF/weak-external3.test
@@ -6,6 +6,7 @@
# RUN: FileCheck --check-prefix=CHECK2 %s < %t2.map
# CHECK1: lto.tmp
+# CHECK1: lto.tmp
# CHECK1-NEXT: 0 g
# CHECK2: weak-external3.test.tmp.obj
diff --git a/test/ELF/Inputs/arm-long-thunk-converge.lds b/test/ELF/Inputs/arm-long-thunk-converge.lds
new file mode 100644
index 0000000..592d400
--- /dev/null
+++ b/test/ELF/Inputs/arm-long-thunk-converge.lds
@@ -0,0 +1,4 @@
+SECTIONS {
+ .foo : { *(.foo) }
+ .bar 0x2000000 : { *(.bar) }
+}
diff --git a/test/ELF/Inputs/conflict-debug.s b/test/ELF/Inputs/conflict-debug.s
index 03fb013..c38771e 100644
--- a/test/ELF/Inputs/conflict-debug.s
+++ b/test/ELF/Inputs/conflict-debug.s
@@ -3,3 +3,24 @@
.loc 1 4
zed:
nop
+
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+
+ .section .debug_info,"",@progbits
+ .long .Lend0 - .Lbegin0 # Length of Unit
+.Lbegin0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit
+ .long .debug_line # DW_AT_stmt_list
+.Lend0:
+ .section .debug_line,"",@progbits
diff --git a/test/ELF/Inputs/far-long-arm-abs.s b/test/ELF/Inputs/far-long-arm-abs.s
new file mode 100644
index 0000000..10d9d02
--- /dev/null
+++ b/test/ELF/Inputs/far-long-arm-abs.s
@@ -0,0 +1,13 @@
+.global far
+.type far,%function
+far = 0x201001c
+
+.global too_far1
+.type too_far1,%function
+too_far1 = 0x2020014
+.global too_far2
+.type too_far2,%function
+too_far2 = 0x2020020
+.global too_far3
+.type too_far3,%function
+too_far3 = 0x202002c
diff --git a/test/ELF/Inputs/i386-pic-plt.s b/test/ELF/Inputs/i386-pic-plt.s
new file mode 100644
index 0000000..a7a8121
--- /dev/null
+++ b/test/ELF/Inputs/i386-pic-plt.s
@@ -0,0 +1,4 @@
+ .global foo
+ .type foo, @function
+foo:
+ nop
diff --git a/test/ELF/Inputs/map-file2.s b/test/ELF/Inputs/map-file2.s
index d46b06f..b830bbc 100644
--- a/test/ELF/Inputs/map-file2.s
+++ b/test/ELF/Inputs/map-file2.s
@@ -1,5 +1,7 @@
foo:
+.cfi_startproc
nop
+.cfi_endproc
.global bar
bar:
nop
diff --git a/test/ELF/Inputs/multiple-cu.s b/test/ELF/Inputs/multiple-cu.s
new file mode 100644
index 0000000..ff29d36
--- /dev/null
+++ b/test/ELF/Inputs/multiple-cu.s
@@ -0,0 +1,24 @@
+ .file 1 "test2.c"
+ .loc 1 2 0
+ jmp bar
+
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+
+ .section .debug_info,"",@progbits
+ .long .Lend0 - .Lbegin0 # Length of Unit
+.Lbegin0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit
+ .long .debug_line # DW_AT_stmt_list
+.Lend0:
+ .section .debug_line,"",@progbits
diff --git a/test/ELF/Inputs/undef-bad-debug.s b/test/ELF/Inputs/undef-bad-debug.s
new file mode 100644
index 0000000..c887b28
--- /dev/null
+++ b/test/ELF/Inputs/undef-bad-debug.s
@@ -0,0 +1,44 @@
+.section .text,"ax"
+sym:
+ .quad zed6
+
+.section .debug_info,"",@progbits
+ .long .Lcu_end - .Lcu_start # Length of Unit
+.Lcu_start:
+ .short 4 # DWARF version number
+ .long .Lsection_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x79 DW_TAG_compile_unit
+ .byte 2 # Abbrev [2] 0x2a:0x15 DW_TAG_variable
+ .long .Linfo_string # DW_AT_name
+ # DW_AT_external
+ .byte 1 # DW_AT_decl_file
+ .byte 3 # DW_AT_decl_line
+ .byte 0 # End Of Children Mark
+.Lcu_end:
+
+.section .debug_abbrev,"",@progbits
+.Lsection_abbrev:
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 2 # Abbreviation Code
+ .byte 52 # DW_TAG_variable
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 63 # DW_AT_external
+ .byte 25 # DW_FORM_flag_present
+ .byte 58 # DW_AT_decl_file
+ .byte 11 # DW_FORM_data1
+ .byte 59 # DW_AT_decl_line
+ .byte 11 # DW_FORM_data1
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+
+.section .debug_str,"MS",@progbits,1
+.Linfo_string:
+ .asciz "sym"
diff --git a/test/ELF/Inputs/undef-debug.s b/test/ELF/Inputs/undef-debug.s
index db8aaf1..46c1c92 100644
--- a/test/ELF/Inputs/undef-debug.s
+++ b/test/ELF/Inputs/undef-debug.s
@@ -9,3 +9,24 @@
.section .text.2,"ax"
.loc 1 11
.quad zed5
+
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+
+ .section .debug_info,"",@progbits
+ .long .Lend0 - .Lbegin0 # Length of Unit
+.Lbegin0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit
+ .long .debug_line # DW_AT_stmt_list
+.Lend0:
+ .section .debug_line,"",@progbits
diff --git a/test/ELF/aarch64-fpic-add_abs_lo12_nc.s b/test/ELF/aarch64-fpic-add_abs_lo12_nc.s
index 9e13fd1..9f56081 100644
--- a/test/ELF/aarch64-fpic-add_abs_lo12_nc.s
+++ b/test/ELF/aarch64-fpic-add_abs_lo12_nc.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_ADD_ABS_LO12_NC against symbol: dat
+// CHECK: can't create dynamic relocation R_AARCH64_ADD_ABS_LO12_NC against symbol: dat in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK: >>> defined in {{.*}}.o
// CHECK: >>> referenced by {{.*}}.o:(.text+0x0)
diff --git a/test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s b/test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s
index 02b75a5..b263fe7 100644
--- a/test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s
+++ b/test/ELF/aarch64-fpic-ldst32_abs_lo12_nc.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_LDST32_ABS_LO12_NC against symbol: dat
+// CHECK: can't create dynamic relocation R_AARCH64_LDST32_ABS_LO12_NC against symbol: dat in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK: >>> defined in {{.*}}.o
// CHECK: >>> referenced by {{.*}}.o:(.text+0x0)
diff --git a/test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s b/test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s
index 45e4f20..3739ab3 100644
--- a/test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s
+++ b/test/ELF/aarch64-fpic-ldst64_abs_lo12_nc.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_LDST64_ABS_LO12_NC against symbol: dat
+// CHECK: can't create dynamic relocation R_AARCH64_LDST64_ABS_LO12_NC against symbol: dat in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK: >>> defined in {{.*}}.o
// CHECK: >>> referenced by {{.*}}.o:(.text+0x0)
diff --git a/test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s b/test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s
index 16e7df1..4354ffd 100644
--- a/test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s
+++ b/test/ELF/aarch64-fpic-ldst8_abs_lo12_nc.s
@@ -1,7 +1,7 @@
// REQUIRES: aarch64
// RUN: llvm-mc -filetype=obj -triple=aarch64-none-freebsd %s -o %t.o
// RUN: not ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_AARCH64_LDST8_ABS_LO12_NC against symbol: dat
+// CHECK: can't create dynamic relocation R_AARCH64_LDST8_ABS_LO12_NC against symbol: dat in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK: >>> defined in {{.*}}.o
// CHECK: >>> referenced by {{.*}}.o:(.text+0x0)
diff --git a/test/ELF/allow-multiple-definition.s b/test/ELF/allow-multiple-definition.s
index 551c54b..06684f4 100644
--- a/test/ELF/allow-multiple-definition.s
+++ b/test/ELF/allow-multiple-definition.s
@@ -4,13 +4,13 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/allow-multiple-definition.s -o %t2
# RUN: not ld.lld %t1 %t2 -o %t3
# RUN: not ld.lld --allow-multiple-definition --no-allow-multiple-definition %t1 %t2 -o %t3
-# RUN: ld.lld --allow-multiple-definition %t1 %t2 -o %t3
-# RUN: ld.lld --allow-multiple-definition %t2 %t1 -o %t4
+# RUN: ld.lld --allow-multiple-definition --fatal-warnings %t1 %t2 -o %t3
+# RUN: ld.lld --allow-multiple-definition --fatal-warnings %t2 %t1 -o %t4
# RUN: llvm-objdump -d %t3 | FileCheck %s
# RUN: llvm-objdump -d %t4 | FileCheck -check-prefix=REVERT %s
-# RUN: ld.lld -z muldefs %t1 %t2 -o %t3
-# RUN: ld.lld -z muldefs %t2 %t1 -o %t4
+# RUN: ld.lld -z muldefs --fatal-warnings %t1 %t2 -o %t3
+# RUN: ld.lld -z muldefs --fatal-warnings %t2 %t1 -o %t4
# RUN: llvm-objdump -d %t3 | FileCheck %s
# RUN: llvm-objdump -d %t4 | FileCheck -check-prefix=REVERT %s
diff --git a/test/ELF/arm-branch-rangethunk.s b/test/ELF/arm-branch-rangethunk.s
index c61ec89..e541f98 100644
--- a/test/ELF/arm-branch-rangethunk.s
+++ b/test/ELF/arm-branch-rangethunk.s
@@ -1,7 +1,10 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-arm-abs.s -o %tfar
// RUN: ld.lld %t %tfar -o %t2 2>&1
-// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck --check-prefix=SHORT %s
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/far-long-arm-abs.s -o %tfarlong
+// RUN: ld.lld %t %tfarlong -o %t3 2>&1
+// RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t3 | FileCheck --check-prefix=LONG %s
// REQUIRES: arm
.syntax unified
.section .text, "ax",%progbits
@@ -15,20 +18,32 @@
b too_far2
beq too_far3
-// CHECK: Disassembly of section .text:
-// CHECK-NEXT: _start:
-// CHECK-NEXT: 20000: 01 00 00 eb bl #4 <__ARMv7ABSLongThunk_too_far1>
-// CHECK-NEXT: 20004: 03 00 00 ea b #12 <__ARMv7ABSLongThunk_too_far2>
-// CHECK-NEXT: 20008: 05 00 00 0a beq #20 <__ARMv7ABSLongThunk_too_far3>
-// CHECK: __ARMv7ABSLongThunk_too_far1:
-// CHECK-NEXT: 2000c: 08 c0 00 e3 movw r12, #8
-// CHECK-NEXT: 20010: 02 c2 40 e3 movt r12, #514
-// CHECK-NEXT: 20014: 1c ff 2f e1 bx r12
-// CHECK: __ARMv7ABSLongThunk_too_far2:
-// CHECK-NEXT: 20018: 0c c0 00 e3 movw r12, #12
-// CHECK-NEXT: 2001c: 02 c2 40 e3 movt r12, #514
-// CHECK-NEXT: 20020: 1c ff 2f e1 bx r12
-// CHECK: __ARMv7ABSLongThunk_too_far3:
-// CHECK-NEXT: 20024: 10 c0 00 e3 movw r12, #16
-// CHECK-NEXT: 20028: 02 c2 40 e3 movt r12, #514
-// CHECK-NEXT: 2002c: 1c ff 2f e1 bx r12
+// SHORT: Disassembly of section .text:
+// SHORT-NEXT: _start:
+// SHORT-NEXT: 20000: 01 00 00 eb bl #4 <__ARMv7ABSLongThunk_too_far1>
+// SHORT-NEXT: 20004: 01 00 00 ea b #4 <__ARMv7ABSLongThunk_too_far2>
+// SHORT-NEXT: 20008: 01 00 00 0a beq #4 <__ARMv7ABSLongThunk_too_far3>
+// SHORT: __ARMv7ABSLongThunk_too_far1:
+// SHORT-NEXT: 2000c: fd ff 7f ea b #33554420 <__ARMv7ABSLongThunk_too_far3+0x1fffff4>
+// SHORT: __ARMv7ABSLongThunk_too_far2:
+// SHORT-NEXT: 20010: fd ff 7f ea b #33554420 <__ARMv7ABSLongThunk_too_far3+0x1fffff8>
+// SHORT: __ARMv7ABSLongThunk_too_far3:
+// SHORT-NEXT: 20014: fd ff 7f ea b #33554420 <__ARMv7ABSLongThunk_too_far3+0x1fffffc>
+
+// LONG: Disassembly of section .text:
+// LONG-NEXT: _start:
+// LONG-NEXT: 20000: 01 00 00 eb bl #4 <__ARMv7ABSLongThunk_too_far1>
+// LONG-NEXT: 20004: 03 00 00 ea b #12 <__ARMv7ABSLongThunk_too_far2>
+// LONG-NEXT: 20008: 05 00 00 0a beq #20 <__ARMv7ABSLongThunk_too_far3>
+// LONG: __ARMv7ABSLongThunk_too_far1:
+// LONG-NEXT: 2000c: 14 c0 00 e3 movw r12, #20
+// LONG-NEXT: 20010: 02 c2 40 e3 movt r12, #514
+// LONG-NEXT: 20014: 1c ff 2f e1 bx r12
+// LONG: __ARMv7ABSLongThunk_too_far2:
+// LONG-NEXT: 20018: 20 c0 00 e3 movw r12, #32
+// LONG-NEXT: 2001c: 02 c2 40 e3 movt r12, #514
+// LONG-NEXT: 20020: 1c ff 2f e1 bx r12
+// LONG: __ARMv7ABSLongThunk_too_far3:
+// LONG-NEXT: 20024: 2c c0 00 e3 movw r12, #44
+// LONG-NEXT: 20028: 02 c2 40 e3 movt r12, #514
+// LONG-NEXT: 2002c: 1c ff 2f e1 bx r12
diff --git a/test/ELF/arm-execute-only.s b/test/ELF/arm-execute-only.s
index c88ac29..655a2c6 100644
--- a/test/ELF/arm-execute-only.s
+++ b/test/ELF/arm-execute-only.s
@@ -14,7 +14,7 @@
// RUN: llvm-readelf -l %t.so | FileCheck --check-prefix=DIFF %s
// CHECK-NOT: LOAD
-// CHECK: LOAD 0x000000 0x00000000 0x00000000 0x00169 0x00169 R 0x1000
+// CHECK: LOAD 0x000000 0x00000000 0x00000000 0x0016d 0x0016d R 0x1000
// CHECK: LOAD 0x001000 0x00001000 0x00001000 0x{{.*}} 0x{{.*}} R E 0x1000
// CHECK: LOAD 0x002000 0x00002000 0x00002000 0x{{.*}} 0x{{.*}} E 0x1000
// CHECK: LOAD 0x003000 0x00003000 0x00003000 0x00038 0x00038 RW 0x1000
@@ -26,7 +26,7 @@
// CHECK: 04 .dynamic
// DIFF-NOT: LOAD
-// DIFF: LOAD 0x000000 0x00000000 0x00000000 0x00149 0x00149 R 0x1000
+// DIFF: LOAD 0x000000 0x00000000 0x00000000 0x0014d 0x0014d R 0x1000
// DIFF: LOAD 0x001000 0x00001000 0x00001000 0x0000c 0x0000c R E 0x1000
// DIFF: LOAD 0x002000 0x00002000 0x00002000 0x00038 0x00038 RW 0x1000
// DIFF-NOT: LOAD
diff --git a/test/ELF/arm-long-thunk-converge.s b/test/ELF/arm-long-thunk-converge.s
new file mode 100644
index 0000000..dadc7e5
--- /dev/null
+++ b/test/ELF/arm-long-thunk-converge.s
@@ -0,0 +1,29 @@
+// REQUIRES: arm
+// RUN: llvm-mc -triple armv7-unknown-gnu -filetype=obj -o %t %s
+// RUN: ld.lld %t %S/Inputs/arm-long-thunk-converge.lds -o %t2
+// RUN: llvm-objdump -d -start-address=0x00000000 -stop-address=0x00000010 -triple=armv7a-linux-gnueabihf %t2 | FileCheck --check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d -start-address=0x02000000 -stop-address=0x02000010 -triple=armv7a-linux-gnueabihf %t2 | FileCheck --check-prefix=CHECK2 %s
+// RUN: rm -f %t2
+
+// CHECK1: __ARMv7ABSLongThunk_bar:
+// CHECK1-NEXT: 0: 0c c0 00 e3 movw r12, #12
+// CHECK1-NEXT: 4: 00 c2 40 e3 movt r12, #512
+// CHECK1-NEXT: 8: 1c ff 2f e1 bx r12
+// CHECK1: foo:
+// CHECK1-NEXT: c: fb ff ff eb bl #-20
+
+.section .foo,"ax",%progbits,unique,1
+foo:
+bl bar
+
+// CHECK2: __ARMv7ABSLongThunk_foo:
+// CHECK2-NEXT: 2000000: 0c c0 00 e3 movw r12, #12
+// CHECK2-NEXT: 2000004: 00 c0 40 e3 movt r12, #0
+// CHECK2-NEXT: 2000008: 1c ff 2f e1 bx r12
+// CHECK2: bar:
+// CHECK2-NEXT: 200000c: fb ff ff eb bl #-20 <__ARMv7ABSLongThunk_foo>
+
+.section .bar,"ax",%progbits,unique,1
+bar:
+bl foo
+.zero 0x1000000
diff --git a/test/ELF/arm-target1.s b/test/ELF/arm-target1.s
index e77fa57..2fc0b8b 100644
--- a/test/ELF/arm-target1.s
+++ b/test/ELF/arm-target1.s
@@ -31,6 +31,6 @@
// RELATIVE: SYMBOL TABLE:
// RELATIVE: 00001004 .text 00000000 patatino
-// ABS: can't create dynamic relocation R_ARM_TARGET1 against symbol: patatino
+// ABS: can't create dynamic relocation R_ARM_TARGET1 against symbol: patatino in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// ABS: >>> defined in {{.*}}.o
// ABS: >>> referenced by {{.*}}.o:(.text+0x0)
diff --git a/test/ELF/arm-thumb-condbranch-thunk.s b/test/ELF/arm-thumb-condbranch-thunk.s
index c527e5d..c9365ef 100644
--- a/test/ELF/arm-thumb-condbranch-thunk.s
+++ b/test/ELF/arm-thumb-condbranch-thunk.s
@@ -38,13 +38,9 @@
// CHECK1-NEXT: 80000: 70 47 bx lr
// CHECK1-NEXT: 80002: 7f f3 ff d7 bl #16252926
// CHECK1: __Thumbv7ABSLongThunk_tfunc05:
-// CHECK1-NEXT: 80008: 40 f2 01 0c movw r12, #1
-// CHECK1-NEXT: 8000c: c0 f2 30 0c movt r12, #48
-// CHECK1-NEXT: 80010: 60 47 bx r12
+// CHECK1-NEXT: 80008: 7f f2 fa bf b.w #2621428 <tfunc05>
// CHECK1: __Thumbv7ABSLongThunk_tfunc00:
-// CHECK1-NEXT: 80012: 40 f2 01 0c movw r12, #1
-// CHECK1-NEXT: 80016: c0 f2 08 0c movt r12, #8
-// CHECK1-NEXT: 8001a: 60 47 bx r12
+// CHECK1-NEXT: 8000c: ff f7 f8 bf b.w #-16 <tfunc00>
FUNCTION 01
// tfunc02 is within range of tfunc02
beq.w tfunc02
@@ -61,7 +57,7 @@
beq.w tfunc00
// CHECK3: 180000: 70 47 bx lr
// CHECK3-NEXT: 180002: 40 f4 01 80 bne.w #-1048574 <__Thumbv7ABSLongThunk_tfunc05>
-// CHECK3-NEXT: 180006: 00 f4 04 80 beq.w #-1048568 <__Thumbv7ABSLongThunk_tfunc00>
+// CHECK3-NEXT: 180006: 00 f4 01 80 beq.w #-1048574 <__Thumbv7ABSLongThunk_tfunc00>
FUNCTION 03
FUNCTION 04
FUNCTION 05
@@ -70,9 +66,7 @@
FUNCTION 08
FUNCTION 09
// CHECK4: __Thumbv7ABSLongThunk_tfunc03:
-// CHECK4-NEXT: 500004: 40 f2 01 0c movw r12, #1
-// CHECK4-NEXT: 500008: c0 f2 20 0c movt r12, #32
-// CHECK4-NEXT: 50000c: 60 47 bx r12
+// CHECK4-NEXT: 500004: ff f4 fc bf b.w #-3145736 <tfunc03>
FUNCTION 10
// We can't reach any Thunk Section, create a new one
beq.w tfunc03
@@ -101,17 +95,13 @@
FUNCTION 30
FUNCTION 31
// CHECK6: __Thumbv7ABSLongThunk_tfunc33:
-// CHECK6-NEXT: 1000004: 40 f2 01 0c movw r12, #1
-// CHECK6-NEXT: 1000008: c0 f2 10 1c movt r12, #272
-// CHECK6-NEXT: 100000c: 60 47 bx r12
+// CHECK6-NEXT: 1000004: ff f0 fc bf b.w #1048568 <tfunc33>
// CHECK6: __Thumbv7ABSLongThunk_tfunc00:
-// CHECK6-NEXT: 100000e: 40 f2 01 0c movw r12, #1
-// CHECK6-NEXT: 1000012: c0 f2 08 0c movt r12, #8
-// CHECK6-NEXT: 1000016: 60 47 bx r12
+// CHECK6-NEXT: 1000008: 7f f4 fa 97 b.w #-16252940 <tfunc00>
FUNCTION 32
FUNCTION 33
// We should be able to reach an existing ThunkSection.
b.w tfunc00
// CHECK7: tfunc33:
// CHECK7-NEXT: 1100000: 70 47 bx lr
-// CHECK7-NEXT: 1100002: 00 f7 04 b8 b.w #-1048568 <__Thumbv7ABSLongThunk_tfunc00>
+// CHECK7-NEXT: 1100002: 00 f7 01 b8 b.w #-1048574 <__Thumbv7ABSLongThunk_tfunc00>
diff --git a/test/ELF/arm-thumb-mix-range-thunk-os.s b/test/ELF/arm-thumb-mix-range-thunk-os.s
index beff414..b5db256 100644
--- a/test/ELF/arm-thumb-mix-range-thunk-os.s
+++ b/test/ELF/arm-thumb-mix-range-thunk-os.s
@@ -11,7 +11,7 @@
// RUN: llvm-objdump -d %t2 -start-address=35651584 -stop-address=35651590 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK6 %s
// RUN: llvm-objdump -d %t2 -start-address=36700160 -stop-address=36700168 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK7 %s
// RUN: llvm-objdump -d %t2 -start-address=48234500 -stop-address=48234512 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK8 %s
-// RUN: llvm-objdump -d %t2 -start-address=63963140 -stop-address=63963160 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK9 %s
+// RUN: llvm-objdump -d %t2 -start-address=53477380 -stop-address=53477392 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK9 %s
// RUN: llvm-objdump -d %t2 -start-address=68157440 -stop-address=68157452 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK10 %s
// RUN: llvm-objdump -d %t2 -start-address=69206016 -stop-address=69206024 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK11 %s
@@ -155,6 +155,13 @@
ARMFUNCTION 48
THUMBFUNCTION 49
ARMFUNCTION 50
+// Expect precreated Thunk Section here
+// CHECK9: __Thumbv7ABSLongThunk_afunc34:
+// CHECK9-NEXT: 3300004: 40 f2 00 0c movw r12, #0
+// CHECK9-NEXT: 3300008: c0 f2 30 2c movt r12, #560
+// CHECK9-NEXT: 330000c: 60 47 bx r12
+// CHECK9: __Thumbv7ABSLongThunk_tfunc35:
+// CHECK9-NEXT: 330000e: ff f4 f7 97 b.w #-15728658 <tfunc35>
THUMBFUNCTION 51
ARMFUNCTION 52
THUMBFUNCTION 53
@@ -165,15 +172,6 @@
ARMFUNCTION 58
THUMBFUNCTION 59
ARMFUNCTION 60
-// Expect precreated Thunk Section here
-// CHECK9: __Thumbv7ABSLongThunk_afunc34:
-// CHECK9-NEXT: 3d00004: 40 f2 00 0c movw r12, #0
-// CHECK9-NEXT: 3d00008: c0 f2 30 2c movt r12, #560
-// CHECK9-NEXT: 3d0000c: 60 47 bx r12
-// CHECK9: __Thumbv7ABSLongThunk_tfunc35:
-// CHECK9-NEXT: 3d0000e: 40 f2 01 0c movw r12, #1
-// CHECK9-NEXT: 3d00012: c0 f2 40 2c movt r12, #576
-// CHECK9-NEXT: 3d00016: 60 47 bx r12
THUMBFUNCTION 61
ARMFUNCTION 62
THUMBFUNCTION 63
@@ -191,5 +189,5 @@
bl tfunc35
// CHECK11: tfunc65:
// CHECK11: 4200000: 70 47 bx lr
-// CHECK11-NEXT: 4200002: ff f6 ff f7 bl #-5242882
-// CHECK11-NEXT: 4200006: 00 f7 02 f0 bl #-5242876
+// CHECK11-NEXT: 4200002: ff f4 ff d7 bl #-15728642
+// CHECK11-NEXT: 4200006: 00 f5 02 d0 bl #-15728636
diff --git a/test/ELF/arm-thumb-plt-range-thunk-os.s b/test/ELF/arm-thumb-plt-range-thunk-os.s
index f412faa..080160b 100644
--- a/test/ELF/arm-thumb-plt-range-thunk-os.s
+++ b/test/ELF/arm-thumb-plt-range-thunk-os.s
@@ -1,12 +1,12 @@
// REQUIRES: arm
// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
-// RUN: ld.lld %t --shared -o %t.so
+// RUN: ld.lld %t --shared --icf=all -o %t.so
// The output file is large, most of it zeroes. We dissassemble only the
// parts we need to speed up the test and avoid a large output file
-// RUN: llvm-objdump -d %t.so -start-address=8388608 -stop-address=8388624 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
-// RUN: llvm-objdump -d %t.so -start-address=16777216 -stop-address=16777256 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
-// RUN: llvm-objdump -d %t.so -start-address=25165824 -stop-address=25165828 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
-// RUN: llvm-objdump -d %t.so -start-address=25165828 -stop-address=25165924 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK4 %s
+// RUN: llvm-objdump -d %t.so -start-address=0x2000000 -stop-address=0x2000018 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
+// RUN: llvm-objdump -d %t.so -start-address=0x2800004 -stop-address=0x2800034 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t.so -start-address=0x4000000 -stop-address=0x4000010 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d %t.so -start-address=0x4000010 -stop-address=0x4000100 -triple=armv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK4 %s
.syntax unified
.thumb
@@ -19,74 +19,96 @@
.type preemptible, %function
.global far_preemptible
.type far_preemptible, %function
+ .global far_nonpreemptible
+ .hidden far_nonpreemptible
+ .type far_nonpreemptible, %function
+ .global far_nonpreemptible_alias
+ .hidden far_nonpreemptible_alias
+ .type far_nonpreemptible_alias, %function
sym1:
bl elsewhere
bl preemptible
bx lr
preemptible:
bl far_preemptible
+ bl far_nonpreemptible
+ bl far_nonpreemptible_alias
bx lr
// CHECK1: Disassembly of section .text:
// CHECK1-NEXT: sym1:
-// CHECK1-NEXT: 800000: 00 f0 00 d8 bl #8388608
-// CHECK1-NEXT: 800004: 00 f0 04 d8 bl #8388616
-// CHECK1-NEXT: 800008: 70 47 bx lr
+// CHECK1-NEXT: 2000000: 00 f0 00 d8 bl #8388608
+// CHECK1-NEXT: 2000004: 00 f0 04 d8 bl #8388616
+// CHECK1-NEXT: 2000008: 70 47 bx lr
// CHECK1: preemptible:
-// CHECK1-NEXT: 80000a: 00 f0 07 d8 bl #8388622
-// CHECK1-NEXT: 80000e: 70 47 bx lr
+// CHECK1-NEXT: 200000a: 00 f0 07 d8 bl #8388622
+// CHECK1-NEXT: 200000e: 00 f0 0b d8 bl #8388630
+// CHECK1-NEXT: 2000012: 00 f0 09 d8 bl #8388626
+// CHECK1-NEXT: 2000016: 70 47 bx lr
.section .text.2, "ax", %progbits
.balign 0x0800000
bx lr
// CHECK2: __ThumbV7PILongThunk_elsewhere:
-// CHECK2-NEXT: 1000004: 40 f2 20 0c movw r12, #32
-// CHECK2-NEXT: 1000008: c0 f2 80 0c movt r12, #128
-// CHECK2-NEXT: 100000c: fc 44 add r12, pc
-// CHECK2-NEXT: 100000e: 60 47 bx r12
+// CHECK2-NEXT: 2800004: 40 f2 20 0c movw r12, #32
+// CHECK2-NEXT: 2800008: c0 f2 80 1c movt r12, #384
+// CHECK2-NEXT: 280000c: fc 44 add r12, pc
+// CHECK2-NEXT: 280000e: 60 47 bx r12
// CHECK2: __ThumbV7PILongThunk_preemptible:
-// CHECK2-NEXT: 1000010: 40 f2 24 0c movw r12, #36
-// CHECK2-NEXT: 1000014: c0 f2 80 0c movt r12, #128
-// CHECK2-NEXT: 1000018: fc 44 add r12, pc
-// CHECK2-NEXT: 100001a: 60 47 bx r12
+// CHECK2-NEXT: 2800010: 40 f2 24 0c movw r12, #36
+// CHECK2-NEXT: 2800014: c0 f2 80 1c movt r12, #384
+// CHECK2-NEXT: 2800018: fc 44 add r12, pc
+// CHECK2-NEXT: 280001a: 60 47 bx r12
// CHECK2: __ThumbV7PILongThunk_far_preemptible:
-// CHECK2-NEXT: 100001c: 40 f2 28 0c movw r12, #40
-// CHECK2-NEXT: 1000020: c0 f2 80 0c movt r12, #128
-// CHECK2-NEXT: 1000024: fc 44 add r12, pc
-// CHECK2-NEXT: 1000026: 60 47 bx r12
+// CHECK2-NEXT: 280001c: 40 f2 28 0c movw r12, #40
+// CHECK2-NEXT: 2800020: c0 f2 80 1c movt r12, #384
+// CHECK2-NEXT: 2800024: fc 44 add r12, pc
+// CHECK2-NEXT: 2800026: 60 47 bx r12
+// CHECK2: __ThumbV7PILongThunk_far_nonpreemptible:
+// CHECK2-NEXT: 2800028: 4f f6 cd 7c movw r12, #65485
+// CHECK2-NEXT: 280002c: c0 f2 7f 1c movt r12, #383
+// CHECK2-NEXT: 2800030: fc 44 add r12, pc
+// CHECK2-NEXT: 2800032: 60 47 bx r12
.section .text.3, "ax", %progbits
-.balign 0x0800000
+.balign 0x2000000
far_preemptible:
+far_nonpreemptible:
bl elsewhere
+
+ .section .text.4, "ax", %progbits
+.balign 0x2000000
+far_nonpreemptible_alias:
+ bl elsewhere
+
// CHECK3: far_preemptible:
-// CHECK3: 1800000: 00 f0 16 e8 blx #44
+// CHECK3: 4000000: 00 f0 16 e8 blx #44
// CHECK4: Disassembly of section .plt:
// CHECK4-NEXT: $a:
-// CHECK4-NEXT: 1800010: 04 e0 2d e5 str lr, [sp, #-4]!
-// CHECK4-NEXT: 1800014: 00 e6 8f e2 add lr, pc, #0, #12
-// CHECK4-NEXT: 1800018: 00 ea 8e e2 add lr, lr, #0, #20
-// CHECK4-NEXT: 180001c: ec ff be e5 ldr pc, [lr, #4076]!
+// CHECK4-NEXT: 4000010: 04 e0 2d e5 str lr, [sp, #-4]!
+// CHECK4-NEXT: 4000014: 00 e6 8f e2 add lr, pc, #0, #12
+// CHECK4-NEXT: 4000018: 00 ea 8e e2 add lr, lr, #0, #20
+// CHECK4-NEXT: 400001c: ec ff be e5 ldr pc, [lr, #4076]!
// CHECK4: $d:
-// CHECK4-NEXT: 1800020: d4 d4 d4 d4 .word 0xd4d4d4d4
-// CHECK4-NEXT: 1800024: d4 d4 d4 d4 .word 0xd4d4d4d4
-// CHECK4-NEXT: 1800028: d4 d4 d4 d4 .word 0xd4d4d4d4
-// CHECK4-NEXT: 180002c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 4000020: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 4000024: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 4000028: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 400002c: d4 d4 d4 d4 .word 0xd4d4d4d4
// CHECK4: $a:
-// CHECK4-NEXT: 1800030: 00 c6 8f e2 add r12, pc, #0, #12
-// CHECK4-NEXT: 1800034: 00 ca 8c e2 add r12, r12, #0, #20
-// CHECK4-NEXT: 1800038: d4 ff bc e5 ldr pc, [r12, #4052]!
+// CHECK4-NEXT: 4000030: 00 c6 8f e2 add r12, pc, #0, #12
+// CHECK4-NEXT: 4000034: 00 ca 8c e2 add r12, r12, #0, #20
+// CHECK4-NEXT: 4000038: d4 ff bc e5 ldr pc, [r12, #4052]!
// CHECK4: $d:
-// CHECK4-NEXT: 180003c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 400003c: d4 d4 d4 d4 .word 0xd4d4d4d4
// CHECK4: $a:
-// CHECK4-NEXT: 1800040: 00 c6 8f e2 add r12, pc, #0, #12
-// CHECK4-NEXT: 1800044: 00 ca 8c e2 add r12, r12, #0, #20
-// CHECK4-NEXT: 1800048: c8 ff bc e5 ldr pc, [r12, #4040]!
+// CHECK4-NEXT: 4000040: 00 c6 8f e2 add r12, pc, #0, #12
+// CHECK4-NEXT: 4000044: 00 ca 8c e2 add r12, r12, #0, #20
+// CHECK4-NEXT: 4000048: c8 ff bc e5 ldr pc, [r12, #4040]!
// CHECK4: $d:
-// CHECK4-NEXT: 180004c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 400004c: d4 d4 d4 d4 .word 0xd4d4d4d4
// CHECK4: $a:
-// CHECK4-NEXT: 1800050: 00 c6 8f e2 add r12, pc, #0, #12
-// CHECK4-NEXT: 1800054: 00 ca 8c e2 add r12, r12, #0, #20
-// CHECK4-NEXT: 1800058: bc ff bc e5 ldr pc, [r12, #4028]!
+// CHECK4-NEXT: 4000050: 00 c6 8f e2 add r12, pc, #0, #12
+// CHECK4-NEXT: 4000054: 00 ca 8c e2 add r12, r12, #0, #20
+// CHECK4-NEXT: 4000058: bc ff bc e5 ldr pc, [r12, #4028]!
// CHECK4: $d:
-// CHECK4-NEXT: 180005c: d4 d4 d4 d4 .word 0xd4d4d4d4
+// CHECK4-NEXT: 400005c: d4 d4 d4 d4 .word 0xd4d4d4d4
diff --git a/test/ELF/arm-thumb-range-thunk-os.s b/test/ELF/arm-thumb-range-thunk-os.s
index 588539d..182b18d 100644
--- a/test/ELF/arm-thumb-range-thunk-os.s
+++ b/test/ELF/arm-thumb-range-thunk-os.s
@@ -9,8 +9,8 @@
// RUN: llvm-objdump -d %t2 -start-address=4194304 -stop-address=4194310 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK4 %s
// RUN: llvm-objdump -d %t2 -start-address=16777216 -stop-address=16777270 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK5 %s
// RUN: llvm-objdump -d %t2 -start-address=17825792 -stop-address=17825808 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK6 %s
-// RUN: llvm-objdump -d %t2 -start-address=31457280 -stop-address=31457286 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK7 %s
-// RUN: llvm-objdump -d %t2 -start-address=32505860 -stop-address=32505880 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK8 %s
+// RUN: llvm-objdump -d %t2 -start-address=20971524 -stop-address=20971532 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK7 %s
+// RUN: llvm-objdump -d %t2 -start-address=31457280 -stop-address=31457286 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK8 %s
// RUN: llvm-objdump -d %t2 -start-address=35651584 -stop-address=35651594 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK9 %s
// RUN: llvm-objdump -d %t2 -start-address=36700160 -stop-address=36700170 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK10 %s
@@ -60,7 +60,7 @@
b.w tfunc28
// CHECK4: tfunc02:
// CHECK4-NEXT: 400000: 70 47 bx lr
-// CHECK4-NEXT: 400002: 00 f0 04 90 b.w #12582920 <__Thumbv7ABSLongThunk_tfunc28>
+// CHECK4-NEXT: 400002: 00 f0 01 90 b.w #12582914 <__Thumbv7ABSLongThunk_tfunc28>
FUNCTION 03
FUNCTION 04
FUNCTION 05
@@ -75,25 +75,19 @@
FUNCTION 14
// Expect precreated ThunkSection here
// CHECK5: __Thumbv7ABSLongThunk_tfunc16:
-// CHECK5-NEXT: 1000004: 40 f2 01 0c movw r12, #1
-// CHECK5-NEXT: 1000008: c0 f2 20 1c movt r12, #288
-// CHECK5-NEXT: 100000c: 60 47 bx r12
+// CHECK5-NEXT: 1000004: ff f1 fc bf b.w #2097144 <tfunc16>
// CHECK5: __Thumbv7ABSLongThunk_tfunc28:
-// CHECK5-NEXT: 100000e: 40 f2 01 0c movw r12, #1
-// CHECK5-NEXT: 1000012: c0 f2 e0 1c movt r12, #480
-// CHECK5-NEXT: 1000016: 60 47 bx r12
+// CHECK5-NEXT: 1000008: ff f1 fa 97 b.w #14680052 <tfunc28>
// CHECK5: __Thumbv7ABSLongThunk_tfunc32:
-// CHECK5-NEXT: 1000018: 40 f2 01 0c movw r12, #1
-// CHECK5-NEXT: 100001c: c0 f2 20 2c movt r12, #544
-// CHECK5-NEXT: 1000020: 60 47 bx r12
+// CHECK5-NEXT: 100000c: 40 f2 01 0c movw r12, #1
+// CHECK5-NEXT: 1000010: c0 f2 20 2c movt r12, #544
+// CHECK5-NEXT: 1000014: 60 47 bx r12
// CHECK5: __Thumbv7ABSLongThunk_tfunc33:
-// CHECK5-NEXT: 1000022: 40 f2 01 0c movw r12, #1
-// CHECK5-NEXT: 1000026: c0 f2 30 2c movt r12, #560
-// CHECK5-NEXT: 100002a: 60 47 bx r12
+// CHECK5-NEXT: 1000016: 40 f2 01 0c movw r12, #1
+// CHECK5-NEXT: 100001a: c0 f2 30 2c movt r12, #560
+// CHECK5-NEXT: 100001e: 60 47 bx r12
// CHECK5: __Thumbv7ABSLongThunk_tfunc02:
-// CHECK5-NEXT: 100002c: 40 f2 01 0c movw r12, #1
-// CHECK5-NEXT: 1000030: c0 f2 40 0c movt r12, #64
-// CHECK5-NEXT: 1000034: 60 47 bx r12
+// CHECK5-NEXT: 1000020: ff f7 ee 97 b.w #-12582948 <tfunc02>
FUNCTION 15
// tfunc00 and tfunc01 are < 16Mb away, expect no range extension thunks
bl tfunc00
@@ -106,11 +100,16 @@
// CHECK6-NEXT: 1100000: 70 47 bx lr
// CHECK6-NEXT: 1100002: ff f4 fd d7 bl #-15728646
// CHECK6-NEXT: 1100006: ff f5 fb d7 bl #-14680074
-// CHECK6-NEXT: 110000a: 00 f7 05 f8 bl #-1048566
-// CHECK6-NEXT: 110000e: 00 f7 08 f8 bl #-1048560
+// CHECK6-NEXT: 110000a: ff f6 ff ff bl #-1048578
+// CHECK6-NEXT: 110000e: 00 f7 02 f8 bl #-1048572
FUNCTION 16
FUNCTION 17
FUNCTION 18
+// Expect another precreated thunk section here
+// CHECK7: __Thumbv7ABSLongThunk_tfunc15:
+// CHECK7-NEXT: 1400004: ff f4 fc bf b.w #-3145736 <tfunc15>
+// CHECK7: __Thumbv7ABSLongThunk_tfunc16:
+// CHECK7-NEXT: 1400008: ff f5 fa bf b.w #-2097164 <tfunc16>
FUNCTION 19
FUNCTION 20
FUNCTION 21
@@ -123,21 +122,12 @@
FUNCTION 28
// tfunc02 is > 16Mb away, expect range extension thunks in precreated thunk
// section
-// CHECK7: tfunc28:
-// CHECK7-NEXT: 1e00000: 70 47 bx lr
-// CHECK7-NEXT: 1e00002: 00 f6 13 90 b.w #-14680026 <__Thumbv7ABSLongThunk_tfunc02>
+// CHECK8: tfunc28:
+// CHECK8-NEXT: 1e00000: 70 47 bx lr
+// CHECK8-NEXT: 1e00002: 00 f6 0d 90 b.w #-14680038 <__Thumbv7ABSLongThunk_tfunc02>
b.w tfunc02
FUNCTION 29
-// Expect another precreated thunk section here
-// CHECK8: __Thumbv7ABSLongThunk_tfunc15:
-// CHECK8-NEXT: 1f00004: 40 f2 01 0c movw r12, #1
-// CHECK8-NEXT: 1f00008: c0 f2 10 1c movt r12, #272
-// CHECK8-NEXT: 1f0000c: 60 47 bx r12
-// CHECK8: __Thumbv7ABSLongThunk_tfunc16:
-// CHECK8-NEXT: 1f0000e: 40 f2 01 0c movw r12, #1
-// CHECK8-NEXT: 1f00012: c0 f2 20 1c movt r12, #288
-// CHECK8-NEXT: 1f00016: 60 47 bx r12
FUNCTION 30
FUNCTION 31
FUNCTION 32
@@ -147,13 +137,13 @@
bl tfunc16
// CHECK9: tfunc32:
// CHECK9: 2200000: 70 47 bx lr
-// CHECK9-NEXT: 2200002: ff f4 ff ff bl #-3145730
-// CHECK9-NEXT: 2200006: 00 f5 02 f8 bl #-3145724
+// CHECK9-NEXT: 2200002: ff f5 ff d7 bl #-14680066
+// CHECK9-NEXT: 2200006: ff f5 ff d7 bl #-14680066
FUNCTION 33
bl tfunc15
bl tfunc16
// CHECK10: tfunc33:
// CHECK10: 2300000: 70 47 bx lr
-// CHECK10-NEXT: 2300002: ff f7 ff f7 bl #-4194306
-// CHECK10-NEXT: 2300006: 00 f4 02 f8 bl #-4194300
+// CHECK10-NEXT: 2300002: ff f4 ff d7 bl #-15728642
+// CHECK10-NEXT: 2300006: ff f4 ff d7 bl #-15728642
diff --git a/test/ELF/arm-thumb-thunk-empty-pass.s b/test/ELF/arm-thumb-thunk-empty-pass.s
index 9ff6ed6..ab9da1b 100644
--- a/test/ELF/arm-thumb-thunk-empty-pass.s
+++ b/test/ELF/arm-thumb-thunk-empty-pass.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
// RUN: ld.lld %t -o %t2 2>&1
// RUN: llvm-objdump -d %t2 -start-address=69632 -stop-address=69646 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
-// RUN: llvm-objdump -d %t2 -start-address=16846860 -stop-address=16846874 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
+// RUN: llvm-objdump -d %t2 -start-address=16846856 -stop-address=16846874 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
.syntax unified
.global _start, foo
.type _start, %function
@@ -20,13 +20,11 @@
// CHECK1-NEXT: _start:
// CHECK1-NEXT: 11000: ff f7 fe ff bl #-4
// CHECK1: __Thumbv7ABSLongThunk__start:
-// CHECK1-NEXT: 11004: 41 f2 01 0c movw r12, #4097
-// CHECK1-NEXT: 11008: c0 f2 01 0c movt r12, #1
-// CHECK1-NEXT: 1100c: 60 47 bx r12
+// CHECK1-NEXT: 11004: ff f7 fc bf b.w #-8 <_start>
// CHECK2: __Thumbv7ABSLongThunk__start:
-// CHECK2: 101100c: 41 f2 01 0c movw r12, #4097
-// CHECK2-NEXT: 1011010: c0 f2 01 0c movt r12, #1
-// CHECK2-NEXT: 1011014: 60 47 bx r12
+// CHECK2: 1011008: 41 f2 01 0c movw r12, #4097
+// CHECK2-NEXT: 101100c: c0 f2 01 0c movt r12, #1
+// CHECK2-NEXT: 1011010: 60 47 bx r12
// CHECK2: foo:
-// CHECK2-NEXT: 1011016: ff f7 f9 ff bl #-14
+// CHECK2-NEXT: 1011012: ff f7 f9 ff bl #-14
diff --git a/test/ELF/arm-thunk-largesection.s b/test/ELF/arm-thunk-largesection.s
index 950f789..5e882a9 100644
--- a/test/ELF/arm-thunk-largesection.s
+++ b/test/ELF/arm-thunk-largesection.s
@@ -2,9 +2,9 @@
// RUN: ld.lld %t -o %t2 2>&1
// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=69632 -stop-address=69636 %t2 | FileCheck -check-prefix=CHECK1 %s
// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=73732 -stop-address=73742 %t2 | FileCheck -check-prefix=CHECK2 %s
-// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=16850944 -stop-address=16850948 %t2 | FileCheck -check-prefix=CHECK3 %s
-// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=33628160 -stop-address=33628164 %t2 | FileCheck -check-prefix=CHECK4 %s
-// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=50405364 -stop-address=50405376 %t2 | FileCheck -check-prefix=CHECK5 %s
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=16850936 -stop-address=16850940 %t2 | FileCheck -check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=33628152 -stop-address=33628156 %t2 | FileCheck -check-prefix=CHECK4 %s
+// RUN: llvm-objdump -d -triple=thumbv7a-none-linux-gnueabi -start-address=50405356 -stop-address=50405376 %t2 | FileCheck -check-prefix=CHECK5 %s
// REQUIRES: arm
.syntax unified
.balign 0x1000
@@ -21,9 +21,7 @@
// CHECK1-NEXT: 11002: 00 00 movs r0, r0
// CHECK2: __Thumbv7ABSLongThunk__start:
-// CHECK2-NEXT: 12004: 41 f2 01 0c movw r12, #4097
-// CHECK2-NEXT: 12008: c0 f2 01 0c movt r12, #1
-// CHECK2-NEXT: 1200c: 60 47 bx r12
+// CHECK2-NEXT: 12004: fe f7 fc bf b.w #-4104 <_start>
// Gigantic section where we need a ThunkSection either side of it
.section .text.large1, "ax", %progbits
@@ -33,10 +31,10 @@
.space (16 * 1024 * 1024) - 4
bl _start
.space (16 * 1024 * 1024) - 16
-// CHECK3: 1012000: 00 f4 00 d0 bl #-16777216
-// CHECK4: 2012000: ff f3 f8 d7 bl #16777200
+// CHECK3: 1011ff8: 00 f4 04 d0 bl #-16777208
+// CHECK4: 2011ff8: ff f3 f8 d7 bl #16777200
// CHECK5: __Thumbv7ABSLongThunk__start:
-// CHECK5-NEXT: 3011ff4: 41 f2 01 0c movw r12, #4097
-// CHECK5-NEXT: 3011ff8: c0 f2 01 0c movt r12, #1
-// CHECK5-NEXT: 3011ffc: 60 47 bx r12
+// CHECK5-NEXT: 3011fec: 41 f2 01 0c movw r12, #4097
+// CHECK5-NEXT: 3011ff0: c0 f2 01 0c movt r12, #1
+// CHECK5-NEXT: 3011ff4: 60 47 bx r12
diff --git a/test/ELF/arm-thunk-linkerscript-large.s b/test/ELF/arm-thunk-linkerscript-large.s
index 07cd1dd..839d771 100644
--- a/test/ELF/arm-thunk-linkerscript-large.s
+++ b/test/ELF/arm-thunk-linkerscript-large.s
@@ -79,9 +79,7 @@
FUNCTIONL 08
FUNCTIONL 09
// CHECK3: __Thumbv7ABSLongThunk_tfuncl24:
-// CHECK3-NEXT: b00004: 40 f2 01 0c movw r12, #1
-// CHECK3-NEXT: b00008: c0 f2 a0 1c movt r12, #416
-// CHECK3-NEXT: b0000c: 60 47 bx r12
+// CHECK3-NEXT: b00004: ff f2 fc 97 b.w #15728632 <tfuncl24>
FUNCTIONL 10
FUNCTIONL 11
FUNCTIONL 12
diff --git a/test/ELF/arm-thunk-linkerscript-sort.s b/test/ELF/arm-thunk-linkerscript-sort.s
index 69d1767..62ea413 100644
--- a/test/ELF/arm-thunk-linkerscript-sort.s
+++ b/test/ELF/arm-thunk-linkerscript-sort.s
@@ -41,9 +41,7 @@
FUNCTION 16
FUNCTION 15
// CHECK2: __Thumbv7ABSLongThunk_tfunc31:
-// CHECK2-NEXT: 1000004: 40 f2 01 0c movw r12, #1
-// CHECK2-NEXT: 1000008: c0 f2 00 2c movt r12, #512
-// CHECK2-NEXT: 100000c: 60 47 bx r12
+// CHECK2-NEXT: 1000004: ff f3 fc 97 b.w #16777208 <tfunc31>
FUNCTION 14
FUNCTION 13
FUNCTION 12
diff --git a/test/ELF/arm-thunk-multipass.s b/test/ELF/arm-thunk-multipass.s
index 25bf523..b353bb1 100644
--- a/test/ELF/arm-thunk-multipass.s
+++ b/test/ELF/arm-thunk-multipass.s
@@ -5,7 +5,7 @@
// parts we need to speed up the test and avoid a large output file
// RUN: llvm-objdump -d %t2 -start-address=1048578 -stop-address=1048586 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK1 %s
// RUN: llvm-objdump -d %t2 -start-address=16777224 -stop-address=16777254 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK2 %s
-// RUN: llvm-objdump -d %t2 -start-address=17825818 -stop-address=17825828 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
+// RUN: llvm-objdump -d %t2 -start-address=17825812 -stop-address=17825826 -triple=thumbv7a-linux-gnueabihf | FileCheck -check-prefix=CHECK3 %s
// In this test case a branch that is in range and does not need its range
// extended can be pushed out of range by another Thunk, necessitating another
// pass
@@ -64,19 +64,15 @@
// CHECK2-NEXT: 100000c: c0 f2 00 1c movt r12, #256
// CHECK2-NEXT: 1000010: 60 47 bx r12
// CHECK2: __Thumbv7ABSLongThunk_target:
-// CHECK2-NEXT: 1000012: 40 f2 1b 0c movw r12, #27
-// CHECK2-NEXT: 1000016: c0 f2 10 1c movt r12, #272
-// CHECK2-NEXT: 100001a: 60 47 bx r12
+// CHECK2-NEXT: 1000012: ff f0 ff bf b.w #1048574 <target>
// CHECK2: __Thumbv7ABSLongThunk_target2:
-// CHECK2-NEXT: 100001c: 40 f2 13 0c movw r12, #19
-// CHECK2-NEXT: 1000020: c0 f2 10 0c movt r12, #16
-// CHECK2-NEXT: 1000024: 60 47 bx r12
+// CHECK2-NEXT: 1000016: ff f4 fc 97 b.w #-15728648 <target2>
.section .text.17, "ax", %progbits
// Just enough space so that bl target is in range if no extension thunks are
// generated.
- .space 0x100000 - 12
+ .space 0x100000 - 6
.section .text.18, "ax", %progbits
.thumb
@@ -90,7 +86,7 @@
nop
bx lr
// CHECK3: target:
-// CHECK3-NEXT: 110001a: ff f6 ff ff bl #-1048578
-// CHECK3-NEXT: 110001e: 00 bf nop
-// CHECK3-NEXT: 1100020: 00 bf nop
-// CHECK3-NEXT: 1100022: 70 47 bx lr
+// CHECK3-NEXT: 1100014: ff f6 ff ff bl #-1048578
+// CHECK3-NEXT: 1100018: 00 bf nop
+// CHECK3-NEXT: 110001a: 00 bf nop
+// CHECK3-NEXT: 110001c: 70 47 bx lr
diff --git a/test/ELF/basic-ppc64.s b/test/ELF/basic-ppc64.s
new file mode 100644
index 0000000..5b9896d
--- /dev/null
+++ b/test/ELF/basic-ppc64.s
@@ -0,0 +1,323 @@
+# # RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t
+# RUN: ld.lld --hash-style=sysv -discard-all -shared %t -o %t2
+# RUN: llvm-readobj -file-headers -sections -section-data -program-headers %t2 | FileCheck %s
+# REQUIRES: ppc
+.abiversion 2
+# Exits with return code 55 on linux.
+.text
+ li 0,1
+ li 3,55
+ sc
+
+// CHECK:Format: ELF64-ppc64
+// CHECK-NEXT:Arch: powerpc64le
+// CHECK-NEXT:AddressSize: 64bit
+// CHECK-NEXT:LoadName:
+// CHECK-NEXT:ElfHeader {
+// CHECK-NEXT: Ident {
+// CHECK-NEXT: Magic: (7F 45 4C 46)
+// CHECK-NEXT: Class: 64-bit (0x2)
+// CHECK-NEXT: DataEncoding: LittleEndian (0x1)
+// CHECK-NEXT: FileVersion: 1
+// CHECK-NEXT: OS/ABI: SystemV (0x0)
+// CHECK-NEXT: ABIVersion: 0
+// CHECK-NEXT: Unused: (00 00 00 00 00 00 00)
+// CHECK-NEXT: }
+// CHECK-NEXT: Type: SharedObject (0x3)
+// CHECK-NEXT: Machine: EM_PPC64 (0x15)
+// CHECK-NEXT: Version: 1
+// CHECK-NEXT: Entry: 0x10000
+// CHECK-NEXT: ProgramHeaderOffset: 0x40
+// CHECK-NEXT: SectionHeaderOffset:
+// CHECK-NEXT: Flags [ (0x2)
+// CHECK-NEXT: 0x2
+// CHECK-NEXT: ]
+// CHECK-NEXT: HeaderSize: 64
+// CHECK-NEXT: ProgramHeaderEntrySize: 56
+// CHECK-NEXT: ProgramHeaderCount: 7
+// CHECK-NEXT: SectionHeaderEntrySize: 64
+// CHECK-NEXT: SectionHeaderCount: 10
+// CHECK-NEXT: StringTableSectionIndex: 8
+// CHECK-NEXT:}
+// CHECK-NEXT:Sections [
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 0
+// CHECK-NEXT: Name: (0)
+// CHECK-NEXT: Type: SHT_NULL (0x0)
+// CHECK-NEXT: Flags [ (0x0)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x0
+// CHECK-NEXT: Offset: 0x0
+// CHECK-NEXT: Size: 0
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 0
+// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 1
+// CHECK-NEXT: Name: .dynsym (1)
+// CHECK-NEXT: Type: SHT_DYNSYM (0xB)
+// CHECK-NEXT: Flags [ (0x2)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x1C8
+// CHECK-NEXT: Offset: 0x1C8
+// CHECK-NEXT: Size: 24
+// CHECK-NEXT: Link: 3
+// CHECK-NEXT: Info: 1
+// CHECK-NEXT: AddressAlignment: 8
+// CHECK-NEXT: EntrySize: 24
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 00000000 00000000 00000000 00000000 |................|
+// CHECK-NEXT: 0010: 00000000 00000000 |........|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 2
+// CHECK-NEXT: Name: .hash (9)
+// CHECK-NEXT: Type: SHT_HASH (0x5)
+// CHECK-NEXT: Flags [ (0x2)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x1E0
+// CHECK-NEXT: Offset: 0x1E0
+// CHECK-NEXT: Size: 16
+// CHECK-NEXT: Link: 1
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 4
+// CHECK-NEXT: EntrySize: 4
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 01000000 01000000 00000000 00000000 |................|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 3
+// CHECK-NEXT: Name: .dynstr (15)
+// CHECK-NEXT: Type: SHT_STRTAB (0x3)
+// CHECK-NEXT: Flags [ (0x2)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x1F0
+// CHECK-NEXT: Offset: 0x1F0
+// CHECK-NEXT: Size: 1
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 1
+// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 00 |.|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 4
+// CHECK-NEXT: Name: .text (23)
+// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT: Flags [ (0x6)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_EXECINSTR (0x4)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x10000
+// CHECK-NEXT: Offset: 0x10000
+// CHECK-NEXT: Size: 12
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 4
+// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 01000038 37006038 02000044 |...87.`8...D|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 5
+// CHECK-NEXT: Name: .dynamic (29)
+// CHECK-NEXT: Type: SHT_DYNAMIC (0x6)
+// CHECK-NEXT: Flags [ (0x3)
+// CHECK-NEXT: SHF_ALLOC (0x2)
+// CHECK-NEXT: SHF_WRITE (0x1)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x20000
+// CHECK-NEXT: Offset: 0x20000
+// CHECK-NEXT: Size: 96
+// CHECK-NEXT: Link: 3
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 8
+// CHECK-NEXT: EntrySize: 16
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 06000000 00000000 C8010000 00000000 |................|
+// CHECK-NEXT: 0010: 0B000000 00000000 18000000 00000000 |................|
+// CHECK-NEXT: 0020: 05000000 00000000 F0010000 00000000 |................|
+// CHECK-NEXT: 0030: 0A000000 00000000 01000000 00000000 |................|
+// CHECK-NEXT: 0040: 04000000 00000000 E0010000 00000000 |................|
+// CHECK-NEXT: 0050: 00000000 00000000 00000000 00000000 |................|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 6
+// CHECK-NEXT: Name: .comment (38)
+// CHECK-NEXT: Type: SHT_PROGBITS (0x1)
+// CHECK-NEXT: Flags [ (0x30)
+// CHECK-NEXT: SHF_MERGE (0x10)
+// CHECK-NEXT: SHF_STRINGS (0x20)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x0
+// CHECK-NEXT: Offset: 0x20060
+// CHECK-NEXT: Size: 8
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 1
+// CHECK-NEXT: EntrySize: 1
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 4C4C4420 312E3000 |LLD 1.0.|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 7
+// CHECK-NEXT: Name: .symtab (47)
+// CHECK-NEXT: Type: SHT_SYMTAB (0x2)
+// CHECK-NEXT: Flags [ (0x0)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x0
+// CHECK-NEXT: Offset: 0x20068
+// CHECK-NEXT: Size: 48
+// CHECK-NEXT: Link: 9
+// CHECK-NEXT: Info: 2
+// CHECK-NEXT: AddressAlignment: 8
+// CHECK-NEXT: EntrySize: 24
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 00000000 00000000 00000000 00000000 |................|
+// CHECK-NEXT: 0010: 00000000 00000000 01000000 00020500 |................|
+// CHECK-NEXT: 0020: 00000200 00000000 00000000 00000000 |................|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 8
+// CHECK-NEXT: Name: .shstrtab (55)
+// CHECK-NEXT: Type: SHT_STRTAB (0x3)
+// CHECK-NEXT: Flags [ (0x0)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x0
+// CHECK-NEXT: Offset: 0x20098
+// CHECK-NEXT: Size: 73
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 1
+// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 002E6479 6E73796D 002E6861 7368002E |..dynsym..hash..|
+// CHECK-NEXT: 0010: 64796E73 7472002E 74657874 002E6479 |dynstr..text..dy|
+// CHECK-NEXT: 0020: 6E616D69 63002E63 6F6D6D65 6E74002E |namic..comment..|
+// CHECK-NEXT: 0030: 73796D74 6162002E 73687374 72746162 |symtab..shstrtab|
+// CHECK-NEXT: 0040: 002E7374 72746162 00 |..strtab.|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT: Section {
+// CHECK-NEXT: Index: 9
+// CHECK-NEXT: Name: .strtab (65)
+// CHECK-NEXT: Type: SHT_STRTAB (0x3)
+// CHECK-NEXT: Flags [ (0x0)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x0
+// CHECK-NEXT: Offset: 0x200E1
+// CHECK-NEXT: Size: 10
+// CHECK-NEXT: Link: 0
+// CHECK-NEXT: Info: 0
+// CHECK-NEXT: AddressAlignment: 1
+// CHECK-NEXT: EntrySize: 0
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 005F4459 4E414D49 4300 |._DYNAMIC.|
+// CHECK-NEXT: )
+// CHECK-NEXT: }
+// CHECK-NEXT:]
+// CHECK-NEXT:ProgramHeaders [
+// CHECK-NEXT: ProgramHeader {
+// CHECK-NEXT: Type: PT_PHDR (0x6)
+// CHECK-NEXT: Offset: 0x40
+// CHECK-NEXT: VirtualAddress: 0x40
+// CHECK-NEXT: PhysicalAddress: 0x40
+// CHECK-NEXT: FileSize: 392
+// CHECK-NEXT: MemSize: 392
+// CHECK-NEXT: Flags [ (0x4)
+// CHECK-NEXT: PF_R (0x4)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Alignment: 8
+// CHECK-NEXT: }
+// CHECK-NEXT: ProgramHeader {
+// CHECK-NEXT: Type: PT_LOAD (0x1)
+// CHECK-NEXT: Offset: 0x0
+// CHECK-NEXT: VirtualAddress: 0x0
+// CHECK-NEXT: PhysicalAddress: 0x0
+// CHECK-NEXT: FileSize: 497
+// CHECK-NEXT: MemSize: 497
+// CHECK-NEXT: Flags [ (0x4)
+// CHECK-NEXT: PF_R (0x4)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Alignment: 65536
+// CHECK-NEXT: }
+// CHECK-NEXT: ProgramHeader {
+// CHECK-NEXT: Type: PT_LOAD (0x1)
+// CHECK-NEXT: Offset: 0x10000
+// CHECK-NEXT: VirtualAddress: 0x10000
+// CHECK-NEXT: PhysicalAddress: 0x10000
+// CHECK-NEXT: FileSize: 12
+// CHECK-NEXT: MemSize: 12
+// CHECK-NEXT: Flags [ (0x5)
+// CHECK-NEXT: PF_R (0x4)
+// CHECK-NEXT: PF_X (0x1)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Alignment: 65536
+// CHECK-NEXT: }
+// CHECK-NEXT: ProgramHeader {
+// CHECK-NEXT: Type: PT_LOAD (0x1)
+// CHECK-NEXT: Offset: 0x20000
+// CHECK-NEXT: VirtualAddress: 0x20000
+// CHECK-NEXT: PhysicalAddress: 0x20000
+// CHECK-NEXT: FileSize: 96
+// CHECK-NEXT: MemSize: 96
+// CHECK-NEXT: Flags [ (0x6)
+// CHECK-NEXT: PF_R (0x4)
+// CHECK-NEXT: PF_W (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Alignment: 65536
+// CHECK-NEXT: }
+// CHECK-NEXT: ProgramHeader {
+// CHECK-NEXT: Type: PT_DYNAMIC (0x2)
+// CHECK-NEXT: Offset: 0x20000
+// CHECK-NEXT: VirtualAddress: 0x20000
+// CHECK-NEXT: PhysicalAddress: 0x20000
+// CHECK-NEXT: FileSize: 96
+// CHECK-NEXT: MemSize: 96
+// CHECK-NEXT: Flags [ (0x6)
+// CHECK-NEXT: PF_R (0x4)
+// CHECK-NEXT: PF_W (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Alignment: 8
+// CHECK-NEXT: }
+// CHECK-NEXT: ProgramHeader {
+// CHECK-NEXT: Type: PT_GNU_RELRO (0x6474E552)
+// CHECK-NEXT: Offset: 0x20000
+// CHECK-NEXT: VirtualAddress: 0x20000
+// CHECK-NEXT: PhysicalAddress: 0x20000
+// CHECK-NEXT: FileSize: 96
+// CHECK-NEXT: MemSize: 4096
+// CHECK-NEXT: Flags [ (0x4)
+// CHECK-NEXT: PF_R (0x4)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Alignment: 1
+// CHECK-NEXT: }
+// CHECK-NEXT: ProgramHeader {
+// CHECK-NEXT: Type: PT_GNU_STACK (0x6474E551)
+// CHECK-NEXT: Offset: 0x0
+// CHECK-NEXT: VirtualAddress: 0x0
+// CHECK-NEXT: PhysicalAddress: 0x0
+// CHECK-NEXT: FileSize: 0
+// CHECK-NEXT: MemSize: 0
+// CHECK-NEXT: Flags [ (0x6)
+// CHECK-NEXT: PF_R (0x4)
+// CHECK-NEXT: PF_W (0x2)
+// CHECK-NEXT: ]
+// CHECK-NEXT: Alignment: 0
+// CHECK-NEXT: }
+// CHECK-NEXT:]
diff --git a/test/ELF/basic64be.s b/test/ELF/basic64be.s
index d16f4a0..03a4a1f 100644
--- a/test/ELF/basic64be.s
+++ b/test/ELF/basic64be.s
@@ -39,7 +39,8 @@
# CHECK-NEXT: Entry: 0x10020040
# CHECK-NEXT: ProgramHeaderOffset: 0x40
# CHECK-NEXT: SectionHeaderOffset: 0x30080
-# CHECK-NEXT: Flags [ (0x0)
+# CHECK-NEXT: Flags [ (0x1)
+# CHECK-NEXT: 0x1
# CHECK-NEXT: ]
# CHECK-NEXT: HeaderSize: 64
# CHECK-NEXT: ProgramHeaderEntrySize: 56
diff --git a/test/ELF/compressed-debug-conflict.s b/test/ELF/compressed-debug-conflict.s
index c67bc92..f7ddd2d 100644
--- a/test/ELF/compressed-debug-conflict.s
+++ b/test/ELF/compressed-debug-conflict.s
@@ -5,11 +5,11 @@
# OBJ: Sections [
# OBJ: Section {
-# OBJ: Index: 3
-# OBJ-NEXT: Name: .debug_line (16)
-# OBJ-NEXT: Type: SHT_PROGBITS (0x1)
-# OBJ-NEXT: Flags [ (0x800)
-# OBJ-NEXT: SHF_COMPRESSED (0x800)
+# OBJ: Index:
+# OBJ: Name: .debug_line
+# OBJ-NEXT: Type: SHT_PROGBITS
+# OBJ-NEXT: Flags [
+# OBJ-NEXT: SHF_COMPRESSED
# OBJ-NEXT: ]
# ERROR: error: duplicate symbol: main
@@ -27,3 +27,24 @@
xorl %eax, %eax
retl
.file 2 "/tmp/repeat/repeat/repeat/repeat" "repeat.h"
+
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+
+ .section .debug_info,"",@progbits
+ .long .Lend0 - .Lbegin0 # Length of Unit
+.Lbegin0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 4 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit
+ .long .debug_line # DW_AT_stmt_list
+.Lend0:
+ .section .debug_line,"",@progbits
diff --git a/test/ELF/conflict-debug-variable.s b/test/ELF/conflict-debug-variable.s
index 297ed4b..409c38e 100644
--- a/test/ELF/conflict-debug-variable.s
+++ b/test/ELF/conflict-debug-variable.s
@@ -7,14 +7,14 @@
# INPUT-NEXT: DW_AT_name ("foo")
# INPUT-NEXT: DW_AT_decl_file ("1.c")
# INPUT-NEXT: DW_AT_decl_line (1)
-# INPUT-NEXT: DW_AT_type (cu + 0x0032 "int")
+# INPUT-NEXT: DW_AT_type (0x00000032 "int")
# INPUT-NEXT: DW_AT_external (true)
# INPUT-NEXT: DW_AT_location (DW_OP_addr 0x0)
# INPUT: DW_TAG_variable
# INPUT-NEXT: DW_AT_name ("bar")
# INPUT-NEXT: DW_AT_decl_file ("1.c")
# INPUT-NEXT: DW_AT_decl_line (2)
-# INPUT-NEXT: DW_AT_type (cu + 0x0032 "int")
+# INPUT-NEXT: DW_AT_type (0x00000032 "int")
# INPUT-NEXT: DW_AT_external (true)
# INPUT-NEXT: DW_AT_location (DW_OP_addr 0x0)
diff --git a/test/ELF/copy-in-shared.s b/test/ELF/copy-in-shared.s
index 7043985..6af54c1 100644
--- a/test/ELF/copy-in-shared.s
+++ b/test/ELF/copy-in-shared.s
@@ -4,7 +4,7 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o
// RUN: not ld.lld %t2.o %t1.so -o %t2.so -shared 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: foo in readonly segment
+// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: foo in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK: >>> defined in {{.*}}.so
// CHECK: >>> referenced by {{.*}}.o:(.text+0x0)
diff --git a/test/ELF/copy-rel-pie-error.s b/test/ELF/copy-rel-pie-error.s
index 6f7677e..e2fd5b5 100644
--- a/test/ELF/copy-rel-pie-error.s
+++ b/test/ELF/copy-rel-pie-error.s
@@ -3,11 +3,11 @@
// RUN: ld.lld %t2.o -o %t2.so -shared
// RUN: not ld.lld %t.o %t2.so -o %t.exe -pie 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: bar
+// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: bar in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK: >>> defined in {{.*}}.so
// CHECK: >>> referenced by {{.*}}.o:(.text+0x0)
-// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: foo
+// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: foo in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK: >>> defined in {{.*}}.so
// CHECK: >>> referenced by {{.*}}.o:(.text+0x8)
diff --git a/test/ELF/cref.s b/test/ELF/cref.s
new file mode 100644
index 0000000..01516fa
--- /dev/null
+++ b/test/ELF/cref.s
@@ -0,0 +1,26 @@
+// REQUIRES: x86
+
+// RUN: echo '.global foo; foo:' | llvm-mc -filetype=obj -triple=x86_64-pc-linux - -o %t1.o
+// RUN: echo '.global foo, bar; bar:' | llvm-mc -filetype=obj -triple=x86_64-pc-linux - -o %t2.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t3.o
+// RUN: ld.lld -shared -o %t1.so %t1.o -gc-sections
+// RUN: ld.lld -o /dev/null %t1.so %t2.o %t3.o -cref | FileCheck -strict-whitespace %s
+
+// CHECK: Symbol File
+// CHECK-NEXT: bar {{.*}}2.o
+// CHECK-NEXT: {{.*}}3.o
+// CHECK-NEXT: foo {{.*}}1.so
+// CHECK-NEXT: {{.*}}2.o
+// CHECK-NEXT: {{.*}}3.o
+// CHECK-NEXT: _start {{.*}}3.o
+// CHECK-NEXT: baz {{.*}}3.o
+
+.global _start, foo, bar, baz
+_start:
+ call foo
+ call bar
+localsym:
+baz:
+
+.section .text.a,"ax",@progbits
+discarded:
diff --git a/test/ELF/dynamic-got.s b/test/ELF/dynamic-got.s
index 385394b..844e4f4 100644
--- a/test/ELF/dynamic-got.s
+++ b/test/ELF/dynamic-got.s
@@ -3,6 +3,23 @@
// RUN: ld.lld --hash-style=sysv %t.o -o %t.so -shared
// RUN: llvm-readobj -s -l -section-data -r %t.so | FileCheck %s
+// CHECK: Name: .got.plt
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address:
+// CHECK-NEXT: Offset:
+// CHECK-NEXT: Size:
+// CHECK-NEXT: Link:
+// CHECK-NEXT: Info:
+// CHECK-NEXT: AddressAlignment:
+// CHECK-NEXT: EntrySize:
+// CHECK-NEXT: SectionData (
+// CHECK-NEXT: 0000: 00300000 00000000 00000000
+// CHECK-NEXT: )
+
// CHECK: Name: .got
// CHECK-NEXT: Type: SHT_PROGBITS
// CHECK-NEXT: Flags [
@@ -17,19 +34,19 @@
// CHECK-NEXT: AddressAlignment:
// CHECK-NEXT: EntrySize:
// CHECK-NEXT: SectionData (
-// CHECK-NEXT: 0000: 00200000 |
+// CHECK-NEXT: 0000: 00300000
// CHECK-NEXT: )
// CHECK: Relocations [
// CHECK-NEXT: Section ({{.*}}) .rel.dyn {
-// CHECK-NEXT: 0x2050 R_386_RELATIVE - 0x0
+// CHECK-NEXT: 0x3050 R_386_RELATIVE - 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
// CHECK: Type: PT_DYNAMIC
-// CHECK-NEXT: Offset: 0x2000
-// CHECK-NEXT: VirtualAddress: 0x2000
-// CHECK-NEXT: PhysicalAddress: 0x2000
+// CHECK-NEXT: Offset: 0x3000
+// CHECK-NEXT: VirtualAddress: 0x3000
+// CHECK-NEXT: PhysicalAddress: 0x3000
calll .L0$pb
.L0$pb:
diff --git a/test/ELF/dynamic-no-rosegment.s b/test/ELF/dynamic-no-rosegment.s
index e5ad26e..f2b5f35 100644
--- a/test/ELF/dynamic-no-rosegment.s
+++ b/test/ELF/dynamic-no-rosegment.s
@@ -7,9 +7,9 @@
# CHECK-NEXT: Tag Type Name/Value
# CHECK-NEXT: 0x0000000000000006 SYMTAB 0x120
# CHECK-NEXT: 0x000000000000000B SYMENT 24 (bytes)
-# CHECK-NEXT: 0x0000000000000005 STRTAB 0x1D0
+# CHECK-NEXT: 0x0000000000000005 STRTAB 0x1D8
# CHECK-NEXT: 0x000000000000000A STRSZ 1 (bytes)
# CHECK-NEXT: 0x000000006FFFFEF5 GNU_HASH 0x138
-# CHECK-NEXT: 0x0000000000000004 HASH 0x150
+# CHECK-NEXT: 0x0000000000000004 HASH 0x154
# CHECK-NEXT: 0x0000000000000000 NULL 0x0
# CHECK-NEXT: ]
diff --git a/test/ELF/dynamic-reloc-in-ro.s b/test/ELF/dynamic-reloc-in-ro.s
index ecdbfeb..4dee4f6 100644
--- a/test/ELF/dynamic-reloc-in-ro.s
+++ b/test/ELF/dynamic-reloc-in-ro.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_X86_64_64 against local symbol in readonly segment; recompile object files with -fPIC
+// CHECK: can't create dynamic relocation R_X86_64_64 against local symbol in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK-NEXT: >>> defined in {{.*}}.o
// CHECK-NEXT: >>> referenced by {{.*}}.o:(.text+0x0)
diff --git a/test/ELF/eh-frame-dyn-rel.s b/test/ELF/eh-frame-dyn-rel.s
index 289e6c0..a244ac6 100644
--- a/test/ELF/eh-frame-dyn-rel.s
+++ b/test/ELF/eh-frame-dyn-rel.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: not ld.lld %t.o %t.o -o %t -shared 2>&1 | FileCheck %s
-// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: foo
+// CHECK: can't create dynamic relocation R_X86_64_64 against symbol: foo in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
// CHECK: >>> defined in {{.*}}.o
// CHECK: >>> referenced by {{.*}}.o:(.eh_frame+0x12)
diff --git a/test/ELF/eh-frame-hdr-augmentation.s b/test/ELF/eh-frame-hdr-augmentation.s
index 135f811..934f920 100644
--- a/test/ELF/eh-frame-hdr-augmentation.s
+++ b/test/ELF/eh-frame-hdr-augmentation.s
@@ -11,6 +11,7 @@
// CHECK-NEXT: Code alignment factor: 1
// CHECK-NEXT: Data alignment factor: -8
// CHECK-NEXT: Return address column: 16
+// CHECK-NEXT: Personality Address: 00000dad
// CHECK-NEXT: Augmentation data:
// CHECK: DW_CFA_def_cfa: reg7 +8
@@ -19,6 +20,7 @@
// CHECK-NEXT: DW_CFA_nop:
// CHECK: 00000020 00000014 00000024 FDE cie=00000024 pc=00000d98...00000d98
+// CHECK-NEXT: LSDA Address: 00000d8f
// CHECK-NEXT: DW_CFA_nop:
// CHECK-NEXT: DW_CFA_nop:
// CHECK-NEXT: DW_CFA_nop:
diff --git a/test/ELF/elf-header.s b/test/ELF/elf-header.s
new file mode 100644
index 0000000..e188650
--- /dev/null
+++ b/test/ELF/elf-header.s
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t1
+# RUN: llvm-readobj -file-headers %t1 | FileCheck %s
+
+# RUN: ld.lld %t.o -no-rosegment -o %t2
+# RUN: llvm-readobj -file-headers %t2 | FileCheck %s
+
+# CHECK: ElfHeader {
+# CHECK-NEXT: Ident {
+# CHECK-NEXT: Magic: (7F 45 4C 46)
+# CHECK-NEXT: Class: 64-bit (0x2)
+# CHECK-NEXT: DataEncoding: LittleEndian (0x1)
+# CHECK-NEXT: FileVersion: 1
+# CHECK-NEXT: OS/ABI: SystemV (0x0)
+# CHECK-NEXT: ABIVersion: 0
+# CHECK-NEXT: Unused: (00 00 00 00 00 00 00)
+# CHECK-NEXT: }
diff --git a/test/ELF/emit-relocs-eh-frame.s b/test/ELF/emit-relocs-eh-frame.s
new file mode 100644
index 0000000..4df8858
--- /dev/null
+++ b/test/ELF/emit-relocs-eh-frame.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+# RUN: ld.lld --emit-relocs %t1.o -o %t
+# RUN: llvm-readobj -r %t | FileCheck %s
+
+# CHECK: Relocations [
+# CHECK-NEXT: Section {{.*}} .rela.eh_frame {
+# CHECK-NEXT: 0x{{.*}} R_X86_64_PC32 .text 0x0
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+
+.text
+.globl foo
+foo:
+ .cfi_startproc
+ .cfi_endproc
diff --git a/test/ELF/emit-relocs-shared.s b/test/ELF/emit-relocs-shared.s
index 65a12c1..cb87d9f 100644
--- a/test/ELF/emit-relocs-shared.s
+++ b/test/ELF/emit-relocs-shared.s
@@ -7,10 +7,10 @@
.quad foo
# CHECK: Relocations [
-# CHECK-NEXT: Section (4) .rela.dyn {
+# CHECK-NEXT: Section {{.*}} .rela.dyn {
# CHECK-NEXT: 0x1000 R_X86_64_64 foo 0x0
# CHECK-NEXT: }
-# CHECK-NEXT: Section (8) .rela.data {
+# CHECK-NEXT: Section {{.*}} .rela.data {
# CHECK-NEXT: 0x1000 R_X86_64_64 foo 0x0
# CHECK-NEXT: }
# CHECK-NEXT: ]
diff --git a/test/ELF/emit-relocs.s b/test/ELF/emit-relocs.s
index 95e70d8..b91bb9a 100644
--- a/test/ELF/emit-relocs.s
+++ b/test/ELF/emit-relocs.s
@@ -13,7 +13,7 @@
# CHECK: Section {
# CHECK: Index: 2
-# CHECK-NEXT: Name: .rela.text
+# CHECK: Name: .rela.text
# CHECK-NEXT: Type: SHT_RELA
# CHECK-NEXT: Flags [
# CHECK-NEXT: SHF_INFO_LINK
diff --git a/test/ELF/emulation.s b/test/ELF/emulation.s
index b3d0434..c176cc1 100644
--- a/test/ELF/emulation.s
+++ b/test/ELF/emulation.s
@@ -208,7 +208,8 @@
# PPC64-NEXT: Entry:
# PPC64-NEXT: ProgramHeaderOffset: 0x40
# PPC64-NEXT: SectionHeaderOffset:
-# PPC64-NEXT: Flags [ (0x0)
+# PPC64-NEXT: Flags [ (0x1)
+# PPC64-NEXT: 0x1
# PPC64-NEXT: ]
# PPC64-NEXT: HeaderSize: 64
# PPC64-NEXT: ProgramHeaderEntrySize: 56
@@ -218,6 +219,38 @@
# PPC64-NEXT: StringTableSectionIndex:
# PPC64-NEXT: }
+# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %tppc64le
+# RUN: ld.lld -m elf64lppc %tppc64le -o %t2ppc64le
+# RUN: llvm-readobj -file-headers %t2ppc64le | FileCheck --check-prefix=PPC64LE %s
+# RUN: ld.lld %tppc64le -o %t3ppc64le
+# RUN: llvm-readobj -file-headers %t3ppc64le | FileCheck --check-prefix=PPC64LE %s
+# PPC64LE: ElfHeader {
+# PPC64LE-NEXT: Ident {
+# PPC64LE-NEXT: Magic: (7F 45 4C 46)
+# PPC64LE-NEXT: Class: 64-bit (0x2)
+# PPC64LE-NEXT: DataEncoding: LittleEndian (0x1)
+# PPC64LE-NEXT: FileVersion: 1
+# PPC64LE-NEXT: OS/ABI: SystemV (0x0)
+# PPC64LE-NEXT: ABIVersion: 0
+# PPC64LE-NEXT: Unused: (00 00 00 00 00 00 00)
+# PPC64LE-NEXT: }
+# PPC64LE-NEXT: Type: Executable (0x2)
+# PPC64LE-NEXT: Machine: EM_PPC64 (0x15)
+# PPC64LE-NEXT: Version: 1
+# PPC64LE-NEXT: Entry:
+# PPC64LE-NEXT: ProgramHeaderOffset: 0x40
+# PPC64LE-NEXT: SectionHeaderOffset:
+# PPC64LE-NEXT: Flags [ (0x2)
+# PPC64LE-NEXT: 0x2
+# PPC64LE-NEXT: ]
+# PPC64LE-NEXT: HeaderSize: 64
+# PPC64LE-NEXT: ProgramHeaderEntrySize: 56
+# PPC64LE-NEXT: ProgramHeaderCount:
+# PPC64LE-NEXT: SectionHeaderEntrySize: 64
+# PPC64LE-NEXT: SectionHeaderCount:
+# PPC64LE-NEXT: StringTableSectionIndex:
+# PPC64LE-NEXT: }
+
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %tmips
# RUN: ld.lld -m elf32btsmip -e _start %tmips -o %t2mips
# RUN: llvm-readobj -file-headers %t2mips | FileCheck --check-prefix=MIPS %s
diff --git a/test/ELF/global-offset-table-position-aarch64.s b/test/ELF/global-offset-table-position-aarch64.s
index 68bc4a4..8e26913 100644
--- a/test/ELF/global-offset-table-position-aarch64.s
+++ b/test/ELF/global-offset-table-position-aarch64.s
@@ -20,11 +20,11 @@
.long _GLOBAL_OFFSET_TABLE_ - .
// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (11)
-// CHECK-NEXT: Value: 0x30090
+// CHECK-NEXT: Value: 0x20008
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: None (0x0)
// CHECK-NEXT: Other [ (0x2)
// CHECK-NEXT: STV_HIDDEN (0x2)
// CHECK-NEXT: ]
-// CHECK-NEXT: Section: .got
+// CHECK-NEXT: Section: .got.plt
diff --git a/test/ELF/global-offset-table-position-i386.s b/test/ELF/global-offset-table-position-i386.s
index 9f778e1..8d50b49 100644
--- a/test/ELF/global-offset-table-position-i386.s
+++ b/test/ELF/global-offset-table-position-i386.s
@@ -3,7 +3,8 @@
// RUN: llvm-readobj -t %t2 | FileCheck %s
// REQUIRES: x86
-// The X86 _GLOBAL_OFFSET_TABLE_ is defined at the end of the .got section.
+// The X86 _GLOBAL_OFFSET_TABLE_ is defined at the start of the .got.plt
+// section.
.globl a
.type a,@object
.comm a,4,4
@@ -21,11 +22,11 @@
calll f@PLT
// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (1)
-// CHECK-NEXT: Value: 0x306C
+// CHECK-NEXT: Value: 0x2000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: None (0x0)
// CHECK-NEXT: Other [ (0x2)
// CHECK-NEXT: STV_HIDDEN (0x2)
// CHECK-NEXT: ]
-// CHECK-NEXT: Section: .got (0xA)
+// CHECK-NEXT: Section: .got.plt
diff --git a/test/ELF/global-offset-table-position.s b/test/ELF/global-offset-table-position.s
index f1195b2..57fe6c8 100644
--- a/test/ELF/global-offset-table-position.s
+++ b/test/ELF/global-offset-table-position.s
@@ -3,7 +3,8 @@
// RUN: llvm-readobj -t %t2 | FileCheck %s
// REQUIRES: x86
-// The X86_64 _GLOBAL_OFFSET_TABLE_ is defined at the end of the .got section.
+// The X86_64 _GLOBAL_OFFSET_TABLE_ is defined at the start of the .got.plt
+// section.
.globl a
.type a,@object
.comm a,4,4
@@ -21,11 +22,11 @@
.long _GLOBAL_OFFSET_TABLE_ - .
// CHECK: Name: _GLOBAL_OFFSET_TABLE_
-// CHECK-NEXT: Value: 0x30D8
+// CHECK-NEXT: Value: 0x2008
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None (0x0)
// CHECK-NEXT: Other [
// CHECK-NEXT: STV_HIDDEN
// CHECK-NEXT: ]
-// CHECK-NEXT: Section: .got
+// CHECK-NEXT: Section: .got.plt
diff --git a/test/ELF/global_offset_table_shared.s b/test/ELF/global_offset_table_shared.s
index 03af02e..06bd7b3 100644
--- a/test/ELF/global_offset_table_shared.s
+++ b/test/ELF/global_offset_table_shared.s
@@ -4,11 +4,11 @@
.long _GLOBAL_OFFSET_TABLE_ - .
// CHECK: Name: _GLOBAL_OFFSET_TABLE_
-// CHECK-NEXT: Value: 0x2060
+// CHECK-NEXT: Value: 0x2000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
// CHECK-NEXT: Other [ (0x2)
// CHECK-NEXT: STV_HIDDEN (0x2)
// CHECK-NEXT: ]
-// CHECK-NEXT: Section: .got
+// CHECK-NEXT: Section: .got.plt
diff --git a/test/ELF/gnu-hash-table.s b/test/ELF/gnu-hash-table.s
index fa68ba2..e8fd059 100644
--- a/test/ELF/gnu-hash-table.s
+++ b/test/ELF/gnu-hash-table.s
@@ -51,12 +51,12 @@
# EMPTY-NEXT: }
# EMPTY-NEXT: ]
# EMPTY: GnuHashTable {
-# EMPTY-NEXT: Num Buckets: 0
+# EMPTY-NEXT: Num Buckets: 1
# EMPTY-NEXT: First Hashed Symbol Index: 2
# EMPTY-NEXT: Num Mask Words: 1
-# EMPTY-NEXT: Shift Count: 5
+# EMPTY-NEXT: Shift Count: 6
# EMPTY-NEXT: Bloom Filter: [0x0]
-# EMPTY-NEXT: Buckets: []
+# EMPTY-NEXT: Buckets: [0]
# EMPTY-NEXT: Values: []
# EMPTY-NEXT: }
@@ -113,8 +113,8 @@
# I386-NEXT: Num Buckets: 1
# I386-NEXT: First Hashed Symbol Index: 4
# I386-NEXT: Num Mask Words: 1
-# I386-NEXT: Shift Count: 5
-# I386-NEXT: Bloom Filter: [0x14000220]
+# I386-NEXT: Shift Count: 6
+# I386-NEXT: Bloom Filter: [0x4004204]
# I386-NEXT: Buckets: [4]
# I386-NEXT: Values: [0xB8860BA, 0xB887389]
# I386-NEXT: }
diff --git a/test/ELF/gnu-ifunc-plt-i386.s b/test/ELF/gnu-ifunc-plt-i386.s
index 243eff6..d0d21e7 100644
--- a/test/ELF/gnu-ifunc-plt-i386.s
+++ b/test/ELF/gnu-ifunc-plt-i386.s
@@ -70,7 +70,7 @@
.globl _start
_start:
- call foo
- call bar
- call bar2
- call zed2
+ call foo@plt
+ call bar@plt
+ call bar2@plt
+ call zed2@plt
diff --git a/test/ELF/got32-i386.s b/test/ELF/got32-i386.s
index 00c7c0d..dce50d0 100644
--- a/test/ELF/got32-i386.s
+++ b/test/ELF/got32-i386.s
@@ -20,4 +20,4 @@
# CHECK: .got 00000004 0000000000012000
# RUN: not ld.lld %t.o -o %t -pie 2>&1 | FileCheck %s --check-prefix=ERR
-# ERR: error: can't create dynamic relocation R_386_GOT32 against symbol: foo in readonly segment; recompile object files with -fPIC
+# ERR: error: can't create dynamic relocation R_386_GOT32 against symbol: foo in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
diff --git a/test/ELF/got32x-i386.s b/test/ELF/got32x-i386.s
index 1311472..610051e 100644
--- a/test/ELF/got32x-i386.s
+++ b/test/ELF/got32x-i386.s
@@ -33,15 +33,15 @@
## 73728 == 0x12000 == ADDR(.got)
# CHECK: _start:
-# CHECK-NEXT: 11001: 8b 05 {{.*}} movl 73728, %eax
-# CHECK-NEXT: 11007: 8b 1d {{.*}} movl 73728, %ebx
+# CHECK-NEXT: 11001: 8b 05 {{.*}} movl 77824, %eax
+# CHECK-NEXT: 11007: 8b 1d {{.*}} movl 77824, %ebx
# CHECK-NEXT: 1100d: 8b 80 {{.*}} movl -4(%eax), %eax
# CHECK-NEXT: 11013: 8b 83 {{.*}} movl -4(%ebx), %eax
# CHECK: Sections:
# CHECK: Name Size Address
-# CHECK: .got 00000004 0000000000012000
+# CHECK: .got 00000004 0000000000013000
# RUN: not ld.lld %S/Inputs/i386-got32x-baseless.elf -o %t1 -pie 2>&1 | \
# RUN: FileCheck %s --check-prefix=ERR
-# ERR: error: can't create dynamic relocation R_386_GOT32X against symbol: foo in readonly segment; recompile object files with -fPIC
-# ERR: error: can't create dynamic relocation R_386_GOT32X against symbol: foo in readonly segment; recompile object files with -fPIC
+# ERR: error: can't create dynamic relocation R_386_GOT32X against symbol: foo in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
+# ERR: error: can't create dynamic relocation R_386_GOT32X against symbol: foo in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
diff --git a/test/ELF/i386-gotpc.s b/test/ELF/i386-gotpc.s
index d2c5ef3..af8380b 100644
--- a/test/ELF/i386-gotpc.s
+++ b/test/ELF/i386-gotpc.s
@@ -6,15 +6,23 @@
movl $_GLOBAL_OFFSET_TABLE_, %eax
+// CHECK: Name: .got.plt
+// CHECK-NEXT: Type: SHT_PROGBITS
+// CHECK-NEXT: Flags [
+// CHECK-NEXT: SHF_ALLOC
+// CHECK-NEXT: SHF_WRITE
+// CHECK-NEXT: ]
+// CHECK-NEXT: Address: 0x2000
+
// CHECK: Name: .got
// CHECK-NEXT: Type: SHT_PROGBITS
// CHECK-NEXT: Flags [
// CHECK-NEXT: SHF_ALLOC
// CHECK-NEXT: SHF_WRITE
// CHECK-NEXT: ]
-// CHECK-NEXT: Address: 0x2030
+// CHECK-NEXT: Address: 0x3030
// DISASM: Disassembly of section .text:
// DISASM-NEXT: .text:
-// DISASM-NEXT: 1000: {{.*}} movl $4144, %eax
-// 0x2030 - 0x1000 = 4144
+// DISASM-NEXT: 1000: {{.*}} movl $8240, %eax
+// 0x3030 - 0x1000 = 0x2030
diff --git a/test/ELF/i386-pic-plt.s b/test/ELF/i386-pic-plt.s
new file mode 100644
index 0000000..0d32436
--- /dev/null
+++ b/test/ELF/i386-pic-plt.s
@@ -0,0 +1,12 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t.o
+// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %p/Inputs/i386-pic-plt.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+// RUN: ld.lld %t.o %t2.so -o %t
+// RUN: not ld.lld %t.o %t2.so -o %t -pie 2>&1 | FileCheck %s
+
+// CHECK: error: symbol 'foo' cannot be preempted; recompile with -fPIE
+
+.global _start
+_start:
+ call foo
diff --git a/test/ELF/i386-reloc-16.s b/test/ELF/i386-reloc-16.s
index d69e6fb..e9d4fbb 100644
--- a/test/ELF/i386-reloc-16.s
+++ b/test/ELF/i386-reloc-16.s
@@ -4,9 +4,10 @@
// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %S/Inputs/x86-64-reloc-16-error.s -o %t2
// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t
// RUN: ld.lld -shared %t %t1 -o %t3
+// RUN: llvm-objdump -s %t3 | FileCheck %s
// CHECK: Contents of section .text:
-// CHECK-NEXT: 200000 42
+// CHECK-NEXT: 1000 42
// RUN: not ld.lld -shared %t %t2 -o %t4 2>&1 | FileCheck --check-prefix=ERROR %s
// ERROR: relocation R_386_16 out of range: 65536 is not in [0, 65535]
diff --git a/test/ELF/i386-reloc-8.s b/test/ELF/i386-reloc-8.s
index c6ae671..e0e255b 100644
--- a/test/ELF/i386-reloc-8.s
+++ b/test/ELF/i386-reloc-8.s
@@ -4,9 +4,10 @@
// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %S/Inputs/i386-reloc-8-error.s -o %t2
// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t
// RUN: ld.lld -shared %t %t1 -o %t3
+// RUN: llvm-objdump -s %t3 | FileCheck %s
// CHECK: Contents of section .text:
-// CHECK-NEXT: 200000 42
+// CHECK-NEXT: 1000 ff
// RUN: not ld.lld -shared %t %t2 -o %t4 2>&1 | FileCheck --check-prefix=ERROR %s
// ERROR: relocation R_386_8 out of range: 256 is not in [0, 255]
diff --git a/test/ELF/i386-retpoline-nopic-linkerscript.s b/test/ELF/i386-retpoline-nopic-linkerscript.s
new file mode 100644
index 0000000..0c7d1fc
--- /dev/null
+++ b/test/ELF/i386-retpoline-nopic-linkerscript.s
@@ -0,0 +1,67 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+
+// RUN: echo "SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .plt : { *(.plt) } \
+// RUN: .got.plt : { *(.got.plt) } \
+// RUN: .dynstr : { *(.dynstr) } \
+// RUN: }" > %t.script
+// RUN: ld.lld %t1.o %t2.so -o %t.exe -z retpolineplt --script %t.script
+// RUN: llvm-objdump -d -s %t.exe | FileCheck %s
+
+// CHECK: Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10: ff 35 fc 00 00 00 pushl 252
+// CHECK-NEXT: 16: 50 pushl %eax
+// CHECK-NEXT: 17: a1 00 01 00 00 movl 256, %eax
+// CHECK-NEXT: 1c: e8 0f 00 00 00 calll 15 <.plt+0x20>
+// CHECK-NEXT: 21: f3 90 pause
+// CHECK-NEXT: 23: 0f ae e8 lfence
+// CHECK-NEXT: 26: eb f9 jmp -7 <.plt+0x11>
+// CHECK-NEXT: 28: cc int3
+// CHECK-NEXT: 29: cc int3
+// CHECK-NEXT: 2a: cc int3
+// CHECK-NEXT: 2b: cc int3
+// CHECK-NEXT: 2c: cc int3
+// CHECK-NEXT: 2d: cc int3
+// CHECK-NEXT: 2e: cc int3
+// CHECK-NEXT: 2f: cc int3
+// CHECK-NEXT: 30: 89 0c 24 movl %ecx, (%esp)
+// CHECK-NEXT: 33: 8b 4c 24 04 movl 4(%esp), %ecx
+// CHECK-NEXT: 37: 89 44 24 04 movl %eax, 4(%esp)
+// CHECK-NEXT: 3b: 89 c8 movl %ecx, %eax
+// CHECK-NEXT: 3d: 59 popl %ecx
+// CHECK-NEXT: 3e: c3 retl
+// CHECK-NEXT: 3f: cc int3
+// CHECK-NEXT: 40: 50 pushl %eax
+// CHECK-NEXT: 41: a1 04 01 00 00 movl 260, %eax
+// CHECK-NEXT: 46: e8 e5 ff ff ff calll -27 <.plt+0x20>
+// CHECK-NEXT: 4b: e9 d1 ff ff ff jmp -47 <.plt+0x11>
+// CHECK-NEXT: 50: 68 00 00 00 00 pushl $0
+// CHECK-NEXT: 55: e9 b6 ff ff ff jmp -74 <.plt>
+// CHECK-NEXT: 5a: cc int3
+// CHECK-NEXT: 5b: cc int3
+// CHECK-NEXT: 5c: cc int3
+// CHECK-NEXT: 5d: cc int3
+// CHECK-NEXT: 5e: cc int3
+// CHECK-NEXT: 5f: cc int3
+// CHECK-NEXT: 60: 50 pushl %eax
+// CHECK-NEXT: 61: a1 08 01 00 00 movl 264, %eax
+// CHECK-NEXT: 66: e8 c5 ff ff ff calll -59 <.plt+0x20>
+// CHECK-NEXT: 6b: e9 b1 ff ff ff jmp -79 <.plt+0x11>
+// CHECK-NEXT: 70: 68 08 00 00 00 pushl $8
+// CHECK-NEXT: 75: e9 96 ff ff ff jmp -106 <.plt>
+// CHECK-NEXT: 7a: cc int3
+// CHECK-NEXT: 7b: cc int3
+// CHECK-NEXT: 7c: cc int3
+// CHECK-NEXT: 7d: cc int3
+// CHECK-NEXT: 7e: cc int3
+// CHECK-NEXT: 7f: cc int3
+
+.global _start
+_start:
+ jmp bar@PLT
+ jmp zed@PLT
diff --git a/test/ELF/i386-retpoline-pic-linkerscript.s b/test/ELF/i386-retpoline-pic-linkerscript.s
new file mode 100644
index 0000000..049e53f
--- /dev/null
+++ b/test/ELF/i386-retpoline-pic-linkerscript.s
@@ -0,0 +1,64 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux -position-independent %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=i386-unknown-linux -position-independent %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+
+// RUN: echo "SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .plt : { *(.plt) } \
+// RUN: .got.plt : { *(.got.plt) } \
+// RUN: .dynstr : { *(.dynstr) } \
+// RUN: }" > %t.script
+// RUN: ld.lld %t1.o %t2.so -o %t.exe -z retpolineplt -pie --script %t.script
+// RUN: llvm-objdump -d -s %t.exe | FileCheck %s
+
+// CHECK: Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10: ff b3 fc 00 00 00 pushl 252(%ebx)
+// CHECK-NEXT: 16: 50 pushl %eax
+// CHECK-NEXT: 17: 8b 83 00 01 00 00 movl 256(%ebx), %eax
+// CHECK-NEXT: 1d: e8 0e 00 00 00 calll 14 <.plt+0x20>
+// CHECK-NEXT: 22: f3 90 pause
+// CHECK-NEXT: 24: 0f ae e8 lfence
+// CHECK-NEXT: 27: eb f9 jmp -7 <.plt+0x12>
+// CHECK-NEXT: 29: cc int3
+// CHECK-NEXT: 2a: cc int3
+// CHECK-NEXT: 2b: cc int3
+// CHECK-NEXT: 2c: cc int3
+// CHECK-NEXT: 2d: cc int3
+// CHECK-NEXT: 2e: cc int3
+// CHECK-NEXT: 2f: cc int3
+// CHECK-NEXT: 30: 89 0c 24 movl %ecx, (%esp)
+// CHECK-NEXT: 33: 8b 4c 24 04 movl 4(%esp), %ecx
+// CHECK-NEXT: 37: 89 44 24 04 movl %eax, 4(%esp)
+// CHECK-NEXT: 3b: 89 c8 movl %ecx, %eax
+// CHECK-NEXT: 3d: 59 popl %ecx
+// CHECK-NEXT: 3e: c3 retl
+// CHECK-NEXT: 3f: cc int3
+// CHECK-NEXT: 40: 50 pushl %eax
+// CHECK-NEXT: 41: 8b 83 04 01 00 00 movl 260(%ebx), %eax
+// CHECK-NEXT: 47: e8 e4 ff ff ff calll -28 <.plt+0x20>
+// CHECK-NEXT: 4c: e9 d1 ff ff ff jmp -47 <.plt+0x12>
+// CHECK-NEXT: 51: 68 00 00 00 00 pushl $0
+// CHECK-NEXT: 56: e9 b5 ff ff ff jmp -75 <.plt>
+// CHECK-NEXT: 5b: cc int3
+// CHECK-NEXT: 5c: cc int3
+// CHECK-NEXT: 5d: cc int3
+// CHECK-NEXT: 5e: cc int3
+// CHECK-NEXT: 5f: cc int3
+// CHECK-NEXT: 60: 50 pushl %eax
+// CHECK-NEXT: 61: 8b 83 08 01 00 00 movl 264(%ebx), %eax
+// CHECK-NEXT: 67: e8 c4 ff ff ff calll -60 <.plt+0x20>
+// CHECK-NEXT: 6c: e9 b1 ff ff ff jmp -79 <.plt+0x12>
+// CHECK-NEXT: 71: 68 08 00 00 00 pushl $8
+// CHECK-NEXT: 76: e9 95 ff ff ff jmp -107 <.plt>
+// CHECK-NEXT: 7b: cc int3
+// CHECK-NEXT: 7c: cc int3
+// CHECK-NEXT: 7d: cc int3
+// CHECK-NEXT: 7e: cc int3
+// CHECK-NEXT: 7f: cc int3
+
+.global _start
+_start:
+ jmp bar@PLT
+ jmp zed@PLT
diff --git a/test/ELF/icf-merge2.s b/test/ELF/icf-merge2.s
new file mode 100644
index 0000000..47c4c88
--- /dev/null
+++ b/test/ELF/icf-merge2.s
@@ -0,0 +1,23 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t --icf=all
+# RUN: llvm-objdump -d %t | FileCheck %s
+
+# Test that we don't merge these.
+# CHECK: leaq
+# CHECK: leaq
+
+ .section .merge1, "aM", @progbits, 8
+.Lfoo:
+ .quad 42
+
+ .section .merge2, "aM", @progbits, 4
+.Lbar:
+ .long 41
+
+ .section .text.foo, "ax", @progbits
+ leaq .Lfoo(%rip), %rax
+
+ .section .text.bar, "ax", @progbits
+ leaq .Lbar(%rip), %rax
diff --git a/test/ELF/icf-merged-sections.s b/test/ELF/icf-merged-sections.s
new file mode 100644
index 0000000..1f6e77e
--- /dev/null
+++ b/test/ELF/icf-merged-sections.s
@@ -0,0 +1,37 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t --icf=all --ignore-data-address-equality --print-icf-sections | FileCheck -allow-empty --check-prefix=NOICF %s
+# RUN: llvm-readobj -s -section-data %t | FileCheck %s
+
+# Check that merge synthetic sections are not merged by ICF.
+
+# NOICF-NOT: selected section <internal>:(.rodata)
+
+# CHECK: Name: .rodata
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment: 8
+# CHECK-NEXT: EntrySize: 0
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 67452301 10325476 67452301 10325476
+
+.section .rodata.cst4,"aM",@progbits,4
+rodata4:
+ .long 0x01234567
+ .long 0x76543210
+ .long 0x01234567
+ .long 0x76543210
+
+.section .rodata.cst8,"aM",@progbits,8
+rodata8:
+ .long 0x01234567
+ .long 0x76543210
diff --git a/test/ELF/invalid/reloc-section-reordered.test b/test/ELF/invalid/reloc-section-reordered.test
new file mode 100644
index 0000000..7ff4ed6
--- /dev/null
+++ b/test/ELF/invalid/reloc-section-reordered.test
@@ -0,0 +1,30 @@
+# REQUIRES: x86
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: not ld.lld %t.o -o %t.exe 2>&1 | FileCheck %s
+# CHECK: unsupported relocation reference
+
+## YAML below lists .rela.text before .text, we do not support it.
+
+!ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ OSABI: ELFOSABI_FREEBSD
+ Type: ET_REL
+ Machine: EM_X86_64
+Sections:
+ - Type: SHT_REL
+ Name: .rela.text
+ Link: .symtab
+ Info: .text
+ AddressAlign: 0x04
+ Relocations:
+ - Offset: 0
+ Symbol: .text
+ Type: R_X86_64_NONE
+ - Type: SHT_PROGBITS
+ Name: .text
+ Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
+ AddressAlign: 0x04
+ Content: "FFFFFFFFFFFFFFFF"
diff --git a/test/ELF/just-symbols-cref.s b/test/ELF/just-symbols-cref.s
new file mode 100644
index 0000000..8581c53
--- /dev/null
+++ b/test/ELF/just-symbols-cref.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t1.exe -Ttext=0x10000
+
+# RUN: ld.lld -just-symbols=%t1.exe -o %t2.exe -cref | FileCheck %s
+
+# CHECK: Symbol File
+# CHECK-NEXT: bar {{.*exe}}
+# CHECK-NEXT: foo {{.*exe}}
+
+.globl foo, bar
+foo:
+ ret
+
+.section .data
+.type bar, @object
+.size bar, 40
+bar:
+ .zero 40
diff --git a/test/ELF/just-symbols.s b/test/ELF/just-symbols.s
new file mode 100644
index 0000000..856cf8c
--- /dev/null
+++ b/test/ELF/just-symbols.s
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld %t.o -o %t1.exe -Ttext=0x10000
+
+# RUN: ld.lld -just-symbols=%t1.exe -o %t2.exe
+# RUN: llvm-readelf -symbols %t2.exe | FileCheck %s
+
+# CHECK: 0000000000011000 40 OBJECT GLOBAL DEFAULT ABS bar
+# CHECK: 0000000000010000 0 NOTYPE GLOBAL DEFAULT ABS foo
+
+.globl foo, bar
+foo:
+ ret
+
+.section .data
+.type bar, @object
+.size bar, 40
+bar:
+ .zero 40
diff --git a/test/ELF/linkerscript/Inputs/insert-after.s b/test/ELF/linkerscript/Inputs/insert-after.s
new file mode 100644
index 0000000..88a6044
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/insert-after.s
@@ -0,0 +1,11 @@
+.section .foo.text,"ax"
+.quad 0
+
+.section .foo.data,"aw"
+.quad 0
+
+.section .text.1,"ax"
+.quad 0
+
+.section .data.1,"aw"
+.quad 0
diff --git a/test/ELF/linkerscript/Inputs/insert-after.script b/test/ELF/linkerscript/Inputs/insert-after.script
new file mode 100644
index 0000000..cb95878
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/insert-after.script
@@ -0,0 +1,4 @@
+SECTIONS {
+ .text : { *(.text.*) }
+ .data : { *(.data.*) }
+}
diff --git a/test/ELF/linkerscript/Inputs/sections-va-overflow.s b/test/ELF/linkerscript/Inputs/sections-va-overflow.s
new file mode 100644
index 0000000..6bb0490
--- /dev/null
+++ b/test/ELF/linkerscript/Inputs/sections-va-overflow.s
@@ -0,0 +1,6 @@
+.global _start
+_start:
+ retq
+
+.bss
+.space 0x2000
diff --git a/test/ELF/linkerscript/bss-fill.s b/test/ELF/linkerscript/bss-fill.s
deleted file mode 100644
index 92f9fdf..0000000
--- a/test/ELF/linkerscript/bss-fill.s
+++ /dev/null
@@ -1,7 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "SECTIONS { .bss : { . += 0x10000; *(.bss) } =0xFF };" > %t.script
-# RUN: ld.lld -o %t --script %t.script %t.o
-
-.section .bss,"",@nobits
-.short 0
diff --git a/test/ELF/linkerscript/bss-fill.test b/test/ELF/linkerscript/bss-fill.test
new file mode 100644
index 0000000..b7ed476
--- /dev/null
+++ b/test/ELF/linkerscript/bss-fill.test
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+# RUN: echo '.section .bss,"",@nobits; .short 0' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o
+# RUN: ld.lld -o %t --script %s %t.o
+
+## Check we do not crash.
+
+SECTIONS {
+ .bss : {
+ . += 0x10000;
+ *(.bss)
+ } =0xFF
+}
diff --git a/test/ELF/linkerscript/common-filespec.s b/test/ELF/linkerscript/common-filespec.test
similarity index 88%
rename from test/ELF/linkerscript/common-filespec.s
rename to test/ELF/linkerscript/common-filespec.test
index 25bb486..2afd91d 100644
--- a/test/ELF/linkerscript/common-filespec.s
+++ b/test/ELF/linkerscript/common-filespec.test
@@ -1,11 +1,17 @@
# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfile0.o
+# RUN: echo '.long 0; .comm common_uniq_0,4,4; .comm common_multiple,8,8' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %tfile0.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec1.s -o %tfile1.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec2.s -o %tfile2.o
-# RUN: echo "SECTIONS { .common_0 : { *file0.o(COMMON) } .common_1 : { *file1.o(COMMON) } .common_2 : { *file2.o(COMMON) } }" > %t.script
-# RUN: ld.lld -o %t1 --script %t.script %tfile0.o %tfile1.o %tfile2.o
+# RUN: ld.lld -o %t1 --script %s %tfile0.o %tfile1.o %tfile2.o
# RUN: llvm-readobj -s -t %t1 | FileCheck %s
+SECTIONS {
+ .common_0 : { *file0.o(COMMON) }
+ .common_1 : { *file1.o(COMMON) }
+ .common_2 : { *file2.o(COMMON) }
+}
+
# Make sure all 3 sections are allocated and they have sizes and alignments
# corresponding to the commons assigned to them
# CHECK: Section {
@@ -96,10 +102,3 @@
# CHECK-NEXT: Other: 0
# CHECK-NEXT: Section: .common_2
# CHECK-NEXT: }
-
-.globl _start
-_start:
- jmp _start
-
-.comm common_uniq_0,4,4
-.comm common_multiple,8,8
diff --git a/test/ELF/linkerscript/constructor.s b/test/ELF/linkerscript/constructor.s
deleted file mode 100644
index acb86fd..0000000
--- a/test/ELF/linkerscript/constructor.s
+++ /dev/null
@@ -1,13 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "SECTIONS { foo : { *(.foo) CONSTRUCTORS } }" > %t.script
-# RUN: ld.lld -o %t1 --script %t.script %t.o
-
-# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
-# CHECK: Sections:
-# CHECK-NEXT: Idx Name Size
-# CHECK-NEXT: 0 00000000
-# CHECK-NEXT: 1 foo 00000001
-
-.section foo, "a"
-.byte 0
diff --git a/test/ELF/linkerscript/constructor.test b/test/ELF/linkerscript/constructor.test
new file mode 100644
index 0000000..edd2cd2
--- /dev/null
+++ b/test/ELF/linkerscript/constructor.test
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+# RUN: echo '.section foo, "a"; .byte 0' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o
+# RUN: ld.lld -o %t1 --script %s %t.o
+
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size
+# CHECK-NEXT: 0 00000000
+# CHECK-NEXT: 1 foo 00000001
+
+SECTIONS {
+ foo : {
+ *(.foo)
+ CONSTRUCTORS
+ }
+}
diff --git a/test/ELF/linkerscript/discard-gnu-hash.s b/test/ELF/linkerscript/discard-gnu-hash.s
new file mode 100644
index 0000000..77f168d
--- /dev/null
+++ b/test/ELF/linkerscript/discard-gnu-hash.s
@@ -0,0 +1,23 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+
+# RUN: ld.lld --hash-style both -shared -o %t1 %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# CHECK: .gnu.hash
+# CHECK: .hash
+
+# RUN: echo "SECTIONS { /DISCARD/ : { *(.hash) } }" > %t.script
+# RUN: ld.lld --hash-style both -shared -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 \
+# RUN: | FileCheck %s --check-prefix=HASH
+# HASH-NOT: .hash
+# HASH: .gnu.hash
+# HASH-NOT: .hash
+
+# RUN: echo "SECTIONS { /DISCARD/ : { *(.gnu.hash) } }" > %t.script
+# RUN: ld.lld --hash-style both -shared -o %t1 --script %t.script %t
+# RUN: llvm-objdump -section-headers %t1 \
+# RUN: | FileCheck %s --check-prefix=GNUHASH
+# GNUHASH-NOT: .gnu.hash
+# GNUHASH: .hash
+# GNUHASH-NOT: .gnu.hash
diff --git a/test/ELF/linkerscript/discard-section-err.s b/test/ELF/linkerscript/discard-section-err.s
index 8ad5b48..f1d3b96 100644
--- a/test/ELF/linkerscript/discard-section-err.s
+++ b/test/ELF/linkerscript/discard-section-err.s
@@ -22,4 +22,14 @@
# RUN: FileCheck -check-prefix=DYNSTR %s
# DYNSTR: discarding .dynstr section is not allowed
+# RUN: echo "SECTIONS { /DISCARD/ : { *(.rela.plt) } }" > %t.script
+# RUN: not ld.lld -pie -o %t --script %t.script %t.o 2>&1 | \
+# RUN: FileCheck -check-prefix=RELAPLT %s
+# RELAPLT: discarding .rela.plt section is not allowed
+
+# RUN: echo "SECTIONS { /DISCARD/ : { *(.rela.dyn) } }" > %t.script
+# RUN: not ld.lld -pie -o %t --script %t.script %t.o 2>&1 | \
+# RUN: FileCheck -check-prefix=RELADYN %s
+# RELADYN: discarding .rela.dyn section is not allowed
+
.comm foo,4,4
diff --git a/test/ELF/linkerscript/double-bss.s b/test/ELF/linkerscript/double-bss.s
deleted file mode 100644
index c24332f..0000000
--- a/test/ELF/linkerscript/double-bss.s
+++ /dev/null
@@ -1,21 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: echo "SECTIONS { . = SIZEOF_HEADERS; " > %t.script
-# RUN: echo ".text : { *(.text*) }" >> %t.script
-# RUN: echo ".bss1 : { *(.bss) }" >> %t.script
-# RUN: echo ".bss2 : { *(COMMON) }" >> %t.script
-# RUN: echo "}" >> %t.script
-
-# RUN: ld.lld -o %t1 --script %t.script %t
-# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
-# CHECK: .bss1 00000004 0000000000000122 BSS
-# CHECK-NEXT: .bss2 00000080 0000000000000128 BSS
-
-.globl _start
-_start:
- jmp _start
-
-.bss
-.zero 4
-
-.comm q,128,8
diff --git a/test/ELF/linkerscript/double-bss.test b/test/ELF/linkerscript/double-bss.test
new file mode 100644
index 0000000..32b796d
--- /dev/null
+++ b/test/ELF/linkerscript/double-bss.test
@@ -0,0 +1,14 @@
+# REQUIRES: x86
+# RUN: echo '.short 0; .bss; .zero 4; .comm q,128,8' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t
+# RUN: ld.lld -o %t1 --script %s %t
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# CHECK: .bss1 00000004 0000000000000122 BSS
+# CHECK-NEXT: .bss2 00000080 0000000000000128 BSS
+
+SECTIONS {
+ . = SIZEOF_HEADERS;
+ .text : { *(.text*) }
+ .bss1 : { *(.bss) }
+ .bss2 : { *(COMMON) }
+}
diff --git a/test/ELF/linkerscript/eh-frame-emit-relocs.s b/test/ELF/linkerscript/eh-frame-emit-relocs.s
new file mode 100644
index 0000000..d951cbc
--- /dev/null
+++ b/test/ELF/linkerscript/eh-frame-emit-relocs.s
@@ -0,0 +1,13 @@
+# REQUIRES: x86
+# RUN: echo "SECTIONS { .foo : { *(.eh_frame) } }" > %t.script
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
+# RUN: ld.lld --emit-relocs %t.o -T %t.script -o %t
+# RUN: llvm-objdump -section-headers %t | FileCheck %s
+
+# CHECK-NOT: eh_frame
+# CHECK: .rela.foo
+# CHECK-NOT: eh_frame
+
+.text
+ .cfi_startproc
+ .cfi_endproc
diff --git a/test/ELF/linkerscript/empty-link-order.test b/test/ELF/linkerscript/empty-link-order.test
new file mode 100644
index 0000000..b63b60c
--- /dev/null
+++ b/test/ELF/linkerscript/empty-link-order.test
@@ -0,0 +1,21 @@
+# REQUIRES: arm
+# RUN: llvm-mc -filetype=obj -triple=arm-arm-none-eabi -o %t.o < /dev/null
+
+SECTIONS {
+ .foo : {
+ bar = .;
+ *(.ARM.exidx*)
+ }
+}
+
+# RUN: ld.lld %t.o -o %t --script %s
+
+## Check we do not crash and do not set SHF_LINK_ORDER flag for .foo
+# RUN: llvm-readobj -s %t | FileCheck %s
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_ARM_EXIDX
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
diff --git a/test/ELF/linkerscript/empty-tls.s b/test/ELF/linkerscript/empty-tls.s
deleted file mode 100644
index 0ef2e7b..0000000
--- a/test/ELF/linkerscript/empty-tls.s
+++ /dev/null
@@ -1,14 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "PHDRS { ph_tls PT_TLS; }" > %t.script
-# RUN: ld.lld -o %t.so -T %t.script %t.o -shared
-# RUN: llvm-readobj -l %t.so | FileCheck %s
-
-# test that we don't crash with an empty PT_TLS
-
-# CHECK: Type: PT_TLS
-# CHECK-NEXT: Offset: 0x0
-# CHECK-NEXT: VirtualAddress: 0x0
-# CHECK-NEXT: PhysicalAddress: 0x0
-# CHECK-NEXT: FileSize: 0
-# CHECK-NEXT: MemSize: 0
diff --git a/test/ELF/linkerscript/empty-tls.test b/test/ELF/linkerscript/empty-tls.test
new file mode 100644
index 0000000..2f473cb
--- /dev/null
+++ b/test/ELF/linkerscript/empty-tls.test
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux /dev/null -o %t.o
+# RUN: ld.lld -o %t.so -T %s %t.o -shared
+# RUN: llvm-readobj -l %t.so | FileCheck %s
+
+PHDRS {
+ ph_tls PT_TLS;
+}
+
+# Test that we don't crash with an empty PT_TLS
+
+# CHECK: Type: PT_TLS
+# CHECK-NEXT: Offset: 0x0
+# CHECK-NEXT: VirtualAddress: 0x0
+# CHECK-NEXT: PhysicalAddress: 0x0
+# CHECK-NEXT: FileSize: 0
+# CHECK-NEXT: MemSize: 0
diff --git a/test/ELF/linkerscript/exidx-crash.s b/test/ELF/linkerscript/exidx-crash.s
deleted file mode 100644
index c29d013..0000000
--- a/test/ELF/linkerscript/exidx-crash.s
+++ /dev/null
@@ -1,7 +0,0 @@
-# REQUIRES: aarch64
-
-# We used to crash on this.
-
-# RUN: llvm-mc %s -o %t.o -filetype=obj -triple=aarch64-pc-linux
-# RUN: echo "SECTIONS { .ARM.exidx : { *(.foo) } }" > %t.script
-# RUN: ld.lld -T %t.script %t.o -o %t
diff --git a/test/ELF/linkerscript/exidx-crash.test b/test/ELF/linkerscript/exidx-crash.test
new file mode 100644
index 0000000..4d765f4
--- /dev/null
+++ b/test/ELF/linkerscript/exidx-crash.test
@@ -0,0 +1,10 @@
+# REQUIRES: aarch64
+
+# We used to crash on this.
+
+# RUN: llvm-mc /dev/null -o %t.o -filetype=obj -triple=aarch64-pc-linux
+# RUN: ld.lld -T %s %t.o -o %t
+
+SECTIONS {
+ .ARM.exidx : { *(.foo) }
+}
diff --git a/test/ELF/linkerscript/expr-invalid-sec.s b/test/ELF/linkerscript/expr-invalid-sec.s
deleted file mode 100644
index 5687751..0000000
--- a/test/ELF/linkerscript/expr-invalid-sec.s
+++ /dev/null
@@ -1,6 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "SECTIONS { foo = ADDR(.text) + ADDR(.text); };" > %t.script
-# RUN: not ld.lld -o %t.so --script %t.script %t.o -shared 2>&1 | FileCheck %s
-
-# CHECK: error: {{.*}}.script:1: at least one side of the expression must be absolute
diff --git a/test/ELF/linkerscript/expr-invalid-sec.test b/test/ELF/linkerscript/expr-invalid-sec.test
new file mode 100644
index 0000000..946062a
--- /dev/null
+++ b/test/ELF/linkerscript/expr-invalid-sec.test
@@ -0,0 +1,9 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t.o
+# RUN: not ld.lld -o %t.so --script %s %t.o -shared 2>&1 | FileCheck %s
+
+# CHECK: error: {{.*}}.test:8: at least one side of the expression must be absolute
+
+SECTIONS {
+ foo = ADDR(.text) + ADDR(.text);
+};
diff --git a/test/ELF/linkerscript/header-addr.s b/test/ELF/linkerscript/header-addr.test
similarity index 73%
rename from test/ELF/linkerscript/header-addr.s
rename to test/ELF/linkerscript/header-addr.test
index 70e6f67..7994c0f 100644
--- a/test/ELF/linkerscript/header-addr.s
+++ b/test/ELF/linkerscript/header-addr.test
@@ -1,13 +1,15 @@
# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "PHDRS {all PT_LOAD PHDRS;} \
-# RUN: SECTIONS { \
-# RUN: . = 0x2000 + SIZEOF_HEADERS; \
-# RUN: .text : {*(.text)} :all \
-# RUN: }" > %t.script
-# RUN: ld.lld --hash-style=sysv -o %t.so --script %t.script %t.o -shared
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux /dev/null -o %t.o
+# RUN: ld.lld --hash-style=sysv -o %t.so --script %s %t.o -shared
# RUN: llvm-readobj -program-headers %t.so | FileCheck %s
+PHDRS { all PT_LOAD PHDRS; }
+
+SECTIONS {
+ . = 0x2000 + SIZEOF_HEADERS;
+ .text : {*(.text)} :all
+}
+
# CHECK: ProgramHeaders [
# CHECK-NEXT: ProgramHeader {
# CHECK-NEXT: Type: PT_LOAD
@@ -25,7 +27,7 @@
# CHECK-NEXT: }
# CHECK-NEXT: ]
-# RUN: ld.lld --hash-style=sysv -o %t2.so --script %t.script %t.o -shared -z max-page-size=0x2000
+# RUN: ld.lld --hash-style=sysv -o %t2.so --script %s %t.o -shared -z max-page-size=0x2000
# RUN: llvm-readobj -program-headers %t2.so \
# RUN: | FileCheck --check-prefix=MAXPAGE %s
diff --git a/test/ELF/linkerscript/header-phdr.s b/test/ELF/linkerscript/header-phdr.s
deleted file mode 100644
index 8c9097d..0000000
--- a/test/ELF/linkerscript/header-phdr.s
+++ /dev/null
@@ -1,13 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "PHDRS { foobar PT_LOAD FILEHDR PHDRS; } \
-# RUN: SECTIONS { . = 0x1000; .abc : { *(.zed) } : foobar }" > %t.script
-# RUN: ld.lld --script %t.script %t.o -o %t
-# RUN: llvm-readelf -l -S -W %t | FileCheck %s
-
-.section .zed, "a"
-.zero 4
-
-
-# CHECK: [ 1] .abc PROGBITS 0000000000001000 001000 000004 00 A 0 0 1
-# CHECK: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x001004 0x001004 R E 0x1000
diff --git a/test/ELF/linkerscript/header-phdr.test b/test/ELF/linkerscript/header-phdr.test
new file mode 100644
index 0000000..72be15b
--- /dev/null
+++ b/test/ELF/linkerscript/header-phdr.test
@@ -0,0 +1,15 @@
+# REQUIRES: x86
+# RUN: echo '.section .zed, "a"; .zero 4' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o
+# RUN: ld.lld --script %s %t.o -o %t
+# RUN: llvm-readelf -l -S -W %t | FileCheck %s
+
+# CHECK: [ 1] .abc PROGBITS 0000000000001000 001000 000004 00 A 0 0 1
+# CHECK: LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x001004 0x001004 R E 0x1000
+
+PHDRS { foobar PT_LOAD FILEHDR PHDRS; }
+
+SECTIONS {
+ . = 0x1000;
+ .abc : { *(.zed) } : foobar
+}
diff --git a/test/ELF/linkerscript/huge-temporary-file.s b/test/ELF/linkerscript/huge-temporary-file.s
index d58709c..e30153c 100644
--- a/test/ELF/linkerscript/huge-temporary-file.s
+++ b/test/ELF/linkerscript/huge-temporary-file.s
@@ -3,7 +3,7 @@
# RUN: echo "SECTIONS { .text 0x2000 : {. = 0x10 ; *(.text) } }" > %t.script
# RUN: not ld.lld %t --script %t.script -o %t1
-## This inputs previously created a 4gb temporarily fine under 32 bit
+## This inputs previously created a 4gb temporarily file under 32 bit
## configuration. Issue was fixed. There is no clean way to check that from here.
## This testcase added for documentation purposes.
diff --git a/test/ELF/linkerscript/implicit-program-header.s b/test/ELF/linkerscript/implicit-program-header.s
deleted file mode 100644
index 682f202..0000000
--- a/test/ELF/linkerscript/implicit-program-header.s
+++ /dev/null
@@ -1,22 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "PHDRS { \
-# RUN: ph_write PT_LOAD FLAGS(2); \
-# RUN: ph_exec PT_LOAD FLAGS(1); \
-# RUN: } \
-# RUN: SECTIONS { \
-# RUN: .bar : { *(.bar) } : ph_exec \
-# RUN: .foo : { *(.foo) } \
-# RUN: .text : { *(.text) } : ph_write \
-# RUN: }" > %t.script
-# RUN: ld.lld --hash-style=sysv -o %t1 --script %t.script \
-# RUN: %t.o -shared
-# RUN: llvm-readobj -elf-output-style=GNU -l %t1 | FileCheck %s
-
-# CHECK: Segment Sections...
-# CHECK-NEXT: 00 .text .dynsym .hash .dynstr .dynamic
-# CHECK-NEXT: 01 .bar .foo
-
-.quad 0
-.section .foo,"ax"
-.quad 0
diff --git a/test/ELF/linkerscript/implicit-program-header.test b/test/ELF/linkerscript/implicit-program-header.test
new file mode 100644
index 0000000..ea5c873
--- /dev/null
+++ b/test/ELF/linkerscript/implicit-program-header.test
@@ -0,0 +1,23 @@
+# REQUIRES: x86
+
+# RUN: echo '.section .text,"ax"; .quad 0' > %t.s
+# RUN: echo '.section .foo,"ax"; .quad 0' >> %t.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t.s -o %t.o
+# RUN: ld.lld --hash-style=sysv -o %t1 --script %s \
+# RUN: %t.o -shared
+# RUN: llvm-readobj -elf-output-style=GNU -l %t1 | FileCheck %s
+
+# CHECK: Segment Sections...
+# CHECK-NEXT: 00 .text .dynsym .hash .dynstr .dynamic
+# CHECK-NEXT: 01 .bar .foo
+
+PHDRS {
+ ph_write PT_LOAD FLAGS(2);
+ ph_exec PT_LOAD FLAGS(1);
+}
+
+SECTIONS {
+ .bar : { *(.bar) } : ph_exec
+ .foo : { *(.foo) }
+ .text : { *(.text) } : ph_write
+}
diff --git a/test/ELF/linkerscript/insert-after.test b/test/ELF/linkerscript/insert-after.test
new file mode 100644
index 0000000..ab5ba18
--- /dev/null
+++ b/test/ELF/linkerscript/insert-after.test
@@ -0,0 +1,29 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/insert-after.s -o %t1.o
+
+## Main linker script contains .text and .data sections. Here
+## we check that can use INSERT AFTER to insert sections .foo.data
+## and .foo.text at the right places.
+
+SECTIONS {
+ .foo.data : { *(.foo.data) }
+} INSERT AFTER .data;
+
+SECTIONS {
+ .foo.text : { *(.foo.text) }
+} INSERT AFTER .text;
+
+# RUN: ld.lld %t1.o -o %t1 --script %p/Inputs/insert-after.script --script %s
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address Type
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .text 00000008 0000000000000000 TEXT DATA
+# CHECK-NEXT: 2 .foo.text 00000008 0000000000000008 TEXT DATA
+# CHECK-NEXT: 3 .data 00000008 0000000000000010 DATA
+# CHECK-NEXT: 4 .foo.data 00000008 0000000000000018 DATA
+
+# RUN: not ld.lld %t1.o -o %t1 --script %s 2>&1 \
+# RUN: | FileCheck %s --check-prefix=ERR
+# ERR-DAG: error: unable to INSERT AFTER/BEFORE .text: section not defined
+# ERR-DAG: error: unable to INSERT AFTER/BEFORE .data: section not defined
diff --git a/test/ELF/linkerscript/insert-before.test b/test/ELF/linkerscript/insert-before.test
new file mode 100644
index 0000000..507e8bd
--- /dev/null
+++ b/test/ELF/linkerscript/insert-before.test
@@ -0,0 +1,29 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/insert-after.s -o %t1.o
+
+## Main linker script contains .text and .data sections. Here
+## we check that can use INSERT BEFORE to insert sections .foo.data
+## and .foo.text at the right places.
+
+SECTIONS {
+ .foo.data : { *(.foo.data) }
+} INSERT BEFORE .data;
+
+SECTIONS {
+ .foo.text : { *(.foo.text) }
+} INSERT BEFORE .text;
+
+# RUN: ld.lld %t1.o -o %t1 --script %p/Inputs/insert-after.script --script %s
+# RUN: llvm-objdump -section-headers %t1 | FileCheck %s
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address Type
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK-NEXT: 1 .foo.text 00000008 0000000000000000 TEXT DATA
+# CHECK-NEXT: 2 .text 00000008 0000000000000008 TEXT DATA
+# CHECK-NEXT: 3 .foo.data 00000008 0000000000000010 DATA
+# CHECK-NEXT: 4 .data 00000008 0000000000000018 DATA
+
+# RUN: not ld.lld %t1.o -o %t1 --script %s 2>&1 \
+# RUN: | FileCheck %s --check-prefix=ERR
+# ERR-DAG: error: unable to INSERT AFTER/BEFORE .text: section not defined
+# ERR-DAG: error: unable to INSERT AFTER/BEFORE .data: section not defined
diff --git a/test/ELF/linkerscript/insert-broken.test b/test/ELF/linkerscript/insert-broken.test
new file mode 100644
index 0000000..9a29562
--- /dev/null
+++ b/test/ELF/linkerscript/insert-broken.test
@@ -0,0 +1,6 @@
+SECTIONS {
+ .foo : { *(.bar) }
+} INSERT .data;
+
+# RUN: not ld.lld -o %t1 --script %s 2>&1 | FileCheck %s
+# CHECK: {{.*}}:3: expected AFTER/BEFORE, but got '.data'
diff --git a/test/ELF/linkerscript/lazy-symbols.s b/test/ELF/linkerscript/lazy-symbols.test
similarity index 68%
rename from test/ELF/linkerscript/lazy-symbols.s
rename to test/ELF/linkerscript/lazy-symbols.test
index 22dffee..579df93 100644
--- a/test/ELF/linkerscript/lazy-symbols.s
+++ b/test/ELF/linkerscript/lazy-symbols.test
@@ -1,11 +1,12 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/lazy-symbols.s -o %t1
# RUN: llvm-ar rcs %tar %t1
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2
-# RUN: echo "foo = 1;" > %t.script
-# RUN: ld.lld %t2 %tar --script %t.script -o %tout
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t2
+# RUN: ld.lld %t2 %tar --script %s -o %tout
# RUN: llvm-readobj -symbols %tout | FileCheck %s
+foo = 1;
+
# This test is to ensure a linker script can define a symbol which have the same
# name as a lazy symbol.
diff --git a/test/ELF/linkerscript/lma-overflow.test b/test/ELF/linkerscript/lma-overflow.test
new file mode 100644
index 0000000..e572c05
--- /dev/null
+++ b/test/ELF/linkerscript/lma-overflow.test
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+
+# RUN: echo '.section .foo,"a"; .quad 1' | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o
+# RUN: not ld.lld -o %t %t.o --script %s 2>&1 | FileCheck %s
+# CHECK: error: section '.foo' will not fit in region 'flash': overflowed by 264 bytes
+
+MEMORY {
+ ram (rwx) : org = 0x1000, len = 0x300
+ flash (rwx) : org = 0x1000, len = 0x100
+}
+SECTIONS {
+ .foo : {
+ *(.foo)
+ . += 0x200;
+ } > ram AT>flash
+}
diff --git a/test/ELF/linkerscript/locationcountererr.s b/test/ELF/linkerscript/locationcountererr.s
deleted file mode 100644
index 113e102..0000000
--- a/test/ELF/linkerscript/locationcountererr.s
+++ /dev/null
@@ -1,11 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-
-# RUN: echo "SECTIONS {" > %t.script
-# RUN: echo ".text 0x2000 : {. = 0x10 ; *(.text) } }" >> %t.script
-# RUN: not ld.lld %t --script %t.script -o %t1 2>&1 | FileCheck %s
-# CHECK: {{.*}}.script:2: unable to move location counter backward for: .text
-
-.globl _start
-_start:
-nop
diff --git a/test/ELF/linkerscript/locationcountererr.test b/test/ELF/linkerscript/locationcountererr.test
new file mode 100644
index 0000000..6a1b213
--- /dev/null
+++ b/test/ELF/linkerscript/locationcountererr.test
@@ -0,0 +1,11 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t
+# RUN: not ld.lld %t --script %s -o %t1 2>&1 | FileCheck %s
+# CHECK: {{.*}}.test:8: unable to move location counter backward for: .text
+
+SECTIONS {
+ .text 0x2000 : {
+ . = 0x10;
+ *(.text)
+ }
+}
diff --git a/test/ELF/linkerscript/map-file.test b/test/ELF/linkerscript/map-file.test
new file mode 100644
index 0000000..5ab3d01
--- /dev/null
+++ b/test/ELF/linkerscript/map-file.test
@@ -0,0 +1,37 @@
+# REQUIRES: x86
+
+# RUN: echo '.section .foo.1, "a"; .quad 1' > %t.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t.s -o %t.o
+
+# RUN: ld.lld -o %t %t.o -Map=%t.map --script %s
+# RUN: FileCheck -strict-whitespace %s < %t.map
+
+SECTIONS {
+ . = 0x1000;
+ .foo : {
+ BYTE(0x11)
+ SHORT(0x1122)
+ LONG(0x11223344)
+ QUAD(0x1122334455667788)
+ . += 0x1000;
+ *(.foo.1)
+ . += 0x123 *
+ (1 + 1);
+ foo = .;
+ bar = 0x42 - 0x26;
+ }
+}
+
+# CHECK: Address Size Align Out In Symbol
+# CHECK-NEXT: 0000000000001000 000000000000125d 1 .foo
+# CHECK-NEXT: 0000000000001000 0000000000000001 1 BYTE ( 0x11 )
+# CHECK-NEXT: 0000000000001001 0000000000000002 1 SHORT ( 0x1122 )
+# CHECK-NEXT: 0000000000001003 0000000000000004 1 LONG ( 0x11223344 )
+# CHECK-NEXT: 0000000000001007 0000000000000008 1 QUAD ( 0x1122334455667788 )
+# CHECK-NEXT: 000000000000100f 0000000000001000 1 . += 0x1000
+# CHECK-NEXT: 000000000000200f 0000000000000008 1 {{.*}}{{/|\\}}map-file.test.tmp.o:(.foo.1)
+# CHECK-NEXT: 0000000000002017 0000000000000246 1 . += 0x123 * ( 1 + 1 )
+# CHECK-NEXT: 000000000000225d 0000000000000000 1 foo = .
+# CHECK-NEXT: 000000000000225d 0000000000000000 1 bar = 0x42 - 0x26
+# CHECK-NEXT: 0000000000002260 0000000000000000 4 .text
+# CHECK-NEXT: 0000000000002260 0000000000000000 4 {{.*}}{{/|\\}}map-file.test.tmp.o:(.text)
diff --git a/test/ELF/linkerscript/memory-at.s b/test/ELF/linkerscript/memory-at.s
deleted file mode 100644
index 9e56dbd..0000000
--- a/test/ELF/linkerscript/memory-at.s
+++ /dev/null
@@ -1,46 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-# RUN: echo "MEMORY { \
-# RUN: FLASH (rx) : ORIGIN = 0x1000, LENGTH = 0x100 \
-# RUN: RAM (rwx) : ORIGIN = 0x2000, LENGTH = 0x100 } \
-# RUN: SECTIONS { \
-# RUN: .text : { *(.text*) } > FLASH \
-# RUN: __etext = .; \
-# RUN: .data : AT (__etext) { *(.data*) } > RAM \
-# RUN: }" > %t.script
-# RUN: ld.lld %t --script %t.script -o %t2
-# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
-
-# CHECK: ProgramHeaders [
-# CHECK-NEXT: ProgramHeader {
-# CHECK-NEXT: Type: PT_LOAD
-# CHECK-NEXT: Offset: 0x1000
-# CHECK-NEXT: VirtualAddress: 0x1000
-# CHECK-NEXT: PhysicalAddress: 0x1000
-# CHECK-NEXT: FileSize: 8
-# CHECK-NEXT: MemSize: 8
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: PF_R
-# CHECK-NEXT: PF_X
-# CHECK-NEXT: ]
-# CHECK-NEXT: Alignment:
-# CHECK-NEXT: }
-# CHECK-NEXT: ProgramHeader {
-# CHECK-NEXT: Type: PT_LOAD
-# CHECK-NEXT: Offset: 0x2000
-# CHECK-NEXT: VirtualAddress: 0x2000
-# CHECK-NEXT: PhysicalAddress: 0x1008
-# CHECK-NEXT: FileSize: 8
-# CHECK-NEXT: MemSize: 8
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: PF_R
-# CHECK-NEXT: PF_W
-# CHECK-NEXT: ]
-# CHECK-NEXT: Alignment:
-# CHECK-NEXT: }
-
-.section .text, "ax"
-.quad 0
-
-.section .data, "aw"
-.quad 0
diff --git a/test/ELF/linkerscript/memory-at.test b/test/ELF/linkerscript/memory-at.test
new file mode 100644
index 0000000..0f06a66
--- /dev/null
+++ b/test/ELF/linkerscript/memory-at.test
@@ -0,0 +1,45 @@
+# REQUIRES: x86
+# RUN: echo '.section .text,"ax"; .quad 0' > %t.s
+# RUN: echo '.section .data,"aw"; .quad 0' >> %t.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t.s -o %t
+# RUN: ld.lld %t --script %s -o %t2
+# RUN: llvm-readobj -program-headers %t2 | FileCheck %s
+
+MEMORY {
+ FLASH (rx) : ORIGIN = 0x1000, LENGTH = 0x100
+ RAM (rwx) : ORIGIN = 0x2000, LENGTH = 0x100
+}
+
+SECTIONS {
+ .text : { *(.text*) } > FLASH
+ __etext = .;
+ .data : AT (__etext) { *(.data*) } > RAM
+}
+
+# CHECK: ProgramHeaders [
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x1000
+# CHECK-NEXT: VirtualAddress: 0x1000
+# CHECK-NEXT: PhysicalAddress: 0x1000
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_X
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment:
+# CHECK-NEXT: }
+# CHECK-NEXT: ProgramHeader {
+# CHECK-NEXT: Type: PT_LOAD
+# CHECK-NEXT: Offset: 0x2000
+# CHECK-NEXT: VirtualAddress: 0x2000
+# CHECK-NEXT: PhysicalAddress: 0x1008
+# CHECK-NEXT: FileSize: 8
+# CHECK-NEXT: MemSize: 8
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: PF_R
+# CHECK-NEXT: PF_W
+# CHECK-NEXT: ]
+# CHECK-NEXT: Alignment:
+# CHECK-NEXT: }
diff --git a/test/ELF/linkerscript/memory-region-alignment.test b/test/ELF/linkerscript/memory-region-alignment.test
new file mode 100644
index 0000000..ba9d4e3
--- /dev/null
+++ b/test/ELF/linkerscript/memory-region-alignment.test
@@ -0,0 +1,58 @@
+# REQUIRES: x86
+# RUN: echo '.section .foo,"a"; .quad 0; .section .zed,"M",@progbits,1; .byte 0' > %t.s
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t.s -o %t.o
+
+MEMORY {
+ ram (rwx): org = 0x1, len = 96K
+}
+
+SECTIONS {
+ .foo : ALIGN(8) {
+ *(.foo)
+ } > ram
+
+ .zed : {
+ *(.zed)
+ } > ram
+}
+
+# RUN: ld.lld %t.o -o %t --script %s
+# RUN: llvm-readobj -sections %t | FileCheck %s
+
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x8
+# CHECK-NEXT: Offset: 0x1008
+# CHECK-NEXT: Size: 8
+
+# CHECK: Name: .text
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_EXECINSTR
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x10
+# CHECK-NEXT: Offset: 0x1010
+# CHECK-NEXT: Size: 0
+
+# CHECK: Name: .zed
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x10
+# CHECK-NEXT: Offset: 0x1010
+# CHECK-NEXT: Size: 1
+
+# CHECK: Name: .comment
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: SHF_STRINGS
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x0
+# CHECK-NEXT: Offset: 0x1011
+# CHECK-NEXT: Size: 8
diff --git a/test/ELF/linkerscript/no-pt-load.s b/test/ELF/linkerscript/no-pt-load.s
deleted file mode 100644
index c704025..0000000
--- a/test/ELF/linkerscript/no-pt-load.s
+++ /dev/null
@@ -1,5 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "PHDRS {foo PT_DYNAMIC ;} " \
-# RUN: "SECTIONS { .text : { *(.text) } : foo }" > %t.script
-# RUN: ld.lld -o %t1 --script %t.script %t.o
diff --git a/test/ELF/linkerscript/no-pt-load.test b/test/ELF/linkerscript/no-pt-load.test
new file mode 100644
index 0000000..16f7f44
--- /dev/null
+++ b/test/ELF/linkerscript/no-pt-load.test
@@ -0,0 +1,11 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux /dev/null -o %t.o
+# RUN: ld.lld -o %t1 --script %s %t.o
+
+## Check we do not crash.
+
+PHDRS { foo PT_DYNAMIC; }
+
+SECTIONS {
+ .text : { *(.text) } : foo
+}
diff --git a/test/ELF/linkerscript/non-absolute2.s b/test/ELF/linkerscript/non-absolute2.test
similarity index 64%
rename from test/ELF/linkerscript/non-absolute2.s
rename to test/ELF/linkerscript/non-absolute2.test
index 97c34d3..fa9e0e4 100644
--- a/test/ELF/linkerscript/non-absolute2.s
+++ b/test/ELF/linkerscript/non-absolute2.test
@@ -1,9 +1,13 @@
# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
-# RUN: echo "SECTIONS { A = . + 0x1; . += 0x1000; }" > %t.script
-# RUN: ld.lld -shared %t1.o --script %t.script -o %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux /dev/null -o %t1.o
+# RUN: ld.lld -shared %t1.o --script %s -o %t
# RUN: llvm-objdump -section-headers -t %t | FileCheck %s
+SECTIONS {
+ A = . + 0x1;
+ . += 0x1000;
+}
+
# CHECK: Sections:
# CHECK-NEXT: Idx Name Size Address
# CHECK-NEXT: 0 00000000 0000000000000000
diff --git a/test/ELF/linkerscript/openbsd-bootdata.s b/test/ELF/linkerscript/openbsd-bootdata.s
deleted file mode 100644
index 3e90574..0000000
--- a/test/ELF/linkerscript/openbsd-bootdata.s
+++ /dev/null
@@ -1,7 +0,0 @@
-# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o
-# RUN: echo "PHDRS { boot PT_OPENBSD_BOOTDATA; }" > %t.script
-# RUN: ld.lld --script %t.script %t.o -o %t
-# RUN: llvm-readobj --program-headers -s %t | FileCheck %s
-
-# CHECK: ProgramHeader {
-# CHECK: Type: PT_OPENBSD_BOOTDATA (0x65A41BE6)
diff --git a/test/ELF/linkerscript/openbsd-bootdata.test b/test/ELF/linkerscript/openbsd-bootdata.test
new file mode 100644
index 0000000..ad3a569
--- /dev/null
+++ b/test/ELF/linkerscript/openbsd-bootdata.test
@@ -0,0 +1,8 @@
+# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux /dev/null -o %t.o
+# RUN: ld.lld --script %s %t.o -o %t
+# RUN: llvm-readobj --program-headers -s %t | FileCheck %s
+
+PHDRS { boot PT_OPENBSD_BOOTDATA; }
+
+# CHECK: ProgramHeader {
+# CHECK: Type: PT_OPENBSD_BOOTDATA (0x65A41BE6)
diff --git a/test/ELF/linkerscript/openbsd-wxneeded.s b/test/ELF/linkerscript/openbsd-wxneeded.test
similarity index 65%
rename from test/ELF/linkerscript/openbsd-wxneeded.s
rename to test/ELF/linkerscript/openbsd-wxneeded.test
index d371da9..3b82a94 100644
--- a/test/ELF/linkerscript/openbsd-wxneeded.s
+++ b/test/ELF/linkerscript/openbsd-wxneeded.test
@@ -1,8 +1,9 @@
-# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o
-# RUN: echo "PHDRS { text PT_LOAD FILEHDR PHDRS; wxneeded PT_OPENBSD_WXNEEDED; }" > %t.script
-# RUN: ld.lld -z wxneeded --script %t.script %t.o -o %t
+# RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux /dev/null -o %t.o
+# RUN: ld.lld -z wxneeded --script %s %t.o -o %t
# RUN: llvm-readobj --program-headers %t | FileCheck %s
+PHDRS { text PT_LOAD FILEHDR PHDRS; wxneeded PT_OPENBSD_WXNEEDED; }
+
# CHECK: ProgramHeader {
# CHECK: Type: PT_OPENBSD_WXNEEDED (0x65A3DBE7)
# CHECK-NEXT: Offset: 0x0
diff --git a/test/ELF/linkerscript/operators.test b/test/ELF/linkerscript/operators.test
index fa6e7ee..7996044 100644
--- a/test/ELF/linkerscript/operators.test
+++ b/test/ELF/linkerscript/operators.test
@@ -26,6 +26,8 @@
unary = -1 + 3;
lshift = 1 << 5;
rshift = 0xff >> 3;
+ precedence1 = 1 | 0xff & 1 << 1 + 1 * 2;
+ precedence2 = (1 | (0xff & (1 << (1 + (1 * 2)))));
maxpagesize = CONSTANT (MAXPAGESIZE);
commonpagesize = CONSTANT (COMMONPAGESIZE);
. = 0xfff0;
@@ -34,6 +36,8 @@
_end = .;
minus_rel = _end - 0x10;
minus_abs = _end - _start;
+ max = MAX(11, 22);
+ min = MIN(11, 22);
}
# CHECK: 00000000000006 *ABS* 00000000 plus
@@ -56,12 +60,16 @@
# CHECK: 00000000000002 *ABS* 00000000 unary
# CHECK: 00000000000020 *ABS* 00000000 lshift
# CHECK: 0000000000001f *ABS* 00000000 rshift
+# CHECK: 00000000000009 *ABS* 00000000 precedence1
+# CHECK: 00000000000009 *ABS* 00000000 precedence2
# CHECK: 00000000001000 *ABS* 00000000 maxpagesize
# CHECK: 00000000001000 *ABS* 00000000 commonpagesize
# CHECK: 0000000000ffff *ABS* 00000000 datasegmentalign
# CHECK: 0000000000fff0 *ABS* 00000000 datasegmentalign2
# CHECK: 0000000000ffe0 .text 00000000 minus_rel
# CHECK: 0000000000fff0 *ABS* 00000000 minus_abs
+# CHECK: 00000000000016 *ABS* 00000000 max
+# CHECK: 0000000000000b *ABS* 00000000 min
## Mailformed number error.
# RUN: echo "SECTIONS { . = 0x12Q41; }" > %t.script
diff --git a/test/ELF/linkerscript/orphan-first-cmd.s b/test/ELF/linkerscript/orphan-first-cmd.s
deleted file mode 100644
index 263cb30..0000000
--- a/test/ELF/linkerscript/orphan-first-cmd.s
+++ /dev/null
@@ -1,20 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "SECTIONS { \
-# RUN: foo = 123; \
-# RUN: . = 0x1000; \
-# RUN: . = 0x2000; \
-# RUN: .bar : { *(.bar) } \
-# RUN: }" > %t.script
-# RUN: ld.lld -o %t -T %t.script %t.o -shared
-# RUN: llvm-readobj -s %t | FileCheck %s
-
-# CHECK: Name: .text
-# CHECK-NEXT: Type: SHT_PROGBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: SHF_EXECINSTR
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x1000
-
-.section .bar, "aw"
diff --git a/test/ELF/linkerscript/orphan-first-cmd.test b/test/ELF/linkerscript/orphan-first-cmd.test
new file mode 100644
index 0000000..6ef473b
--- /dev/null
+++ b/test/ELF/linkerscript/orphan-first-cmd.test
@@ -0,0 +1,20 @@
+# REQUIRES: x86
+# RUN: echo '.section .bar, "aw"' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o
+# RUN: ld.lld -o %t -T %s %t.o -shared
+# RUN: llvm-readobj -s %t | FileCheck %s
+
+SECTIONS {
+ foo = 123;
+ . = 0x1000;
+ . = 0x2000;
+ .bar : { *(.bar) }
+}
+
+# CHECK: Name: .text
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_EXECINSTR
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x1000
diff --git a/test/ELF/linkerscript/outputarch.s b/test/ELF/linkerscript/outputarch.s
deleted file mode 100644
index dd3bf93..0000000
--- a/test/ELF/linkerscript/outputarch.s
+++ /dev/null
@@ -1,4 +0,0 @@
-# REQUIRES: x86
-# RUN: echo "OUTPUT_ARCH(All data written here is ignored)" > %t.script
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-freebsd %s -o %t1
-# RUN: ld.lld -shared -o %t2 %t1 %t.script
diff --git a/test/ELF/linkerscript/outputarch.test b/test/ELF/linkerscript/outputarch.test
new file mode 100644
index 0000000..4819a98
--- /dev/null
+++ b/test/ELF/linkerscript/outputarch.test
@@ -0,0 +1,5 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-freebsd /dev/null -o %t1
+# RUN: ld.lld -shared -o %t2 %t1 %s
+
+OUTPUT_ARCH(All data written here is ignored)
diff --git a/test/ELF/linkerscript/overlapping-sections.s b/test/ELF/linkerscript/overlapping-sections.s
index fcc9957..0b7b741 100644
--- a/test/ELF/linkerscript/overlapping-sections.s
+++ b/test/ELF/linkerscript/overlapping-sections.s
@@ -1,61 +1,5 @@
-# TODO: maybe this should be converted to an x86 test to get more buildbot coverage
-# REQUIRES: mips
-# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o
-
-# RUN: echo "SECTIONS { \
-# RUN: .sec1 0x8000 : { sec1_start = .; *(.first_sec) sec1_end = .;} \
-# RUN: .sec2 0x8800 : { sec2_start = .; *(.second_sec) sec2_end = .;} \
-# RUN: }" > %t.script
-# RUN: ld.lld -o %t.so --script %t.script %t.o -shared
-# RUN: llvm-readobj -sections -program-headers %t.so | FileCheck %s -check-prefix GOOD
-
-# GOOD: Name: .sec1
-# GOOD-NEXT: Type: SHT_PROGBITS (0x1)
-# GOOD-NEXT: Flags [ (0x3)
-# GOOD-NEXT: SHF_ALLOC (0x2)
-# GOOD-NEXT: SHF_WRITE (0x1)
-# GOOD-NEXT: ]
-# GOOD-NEXT: Address: 0x8000
-# GOOD-NEXT: Offset: 0x18000
-# GOOD-NEXT: Size: 256
-
-# GOOD: Name: .sec2
-# GOOD-NEXT: Type: SHT_PROGBITS (0x1)
-# GOOD-NEXT: Flags [ (0x3)
-# GOOD-NEXT: SHF_ALLOC (0x2)
-# GOOD-NEXT: SHF_WRITE (0x1)
-# GOOD-NEXT: ]
-# GOOD-NEXT: Address: 0x8800
-# GOOD-NEXT: Offset: 0x18800
-# GOOD-NEXT: Size: 256
-
-# GOOD: ProgramHeaders [
-# GOOD-NEXT: ProgramHeader {
-# GOOD-NEXT: Type: PT_LOAD (0x1)
-# GOOD-NEXT: Offset: 0x10000
-# GOOD-NEXT: VirtualAddress: 0x0
-# GOOD-NEXT: PhysicalAddress: 0x0
-# GOOD-NEXT: FileSize: 481
-# GOOD-NEXT: MemSize: 481
-# GOOD-NEXT: Flags [ (0x5)
-# GOOD-NEXT: PF_R (0x4)
-# GOOD-NEXT: PF_X (0x1)
-# GOOD-NEXT: ]
-# GOOD-NEXT: Alignment: 65536
-# GOOD-NEXT: }
-# GOOD-NEXT: ProgramHeader {
-# GOOD-NEXT: Type: PT_LOAD (0x1)
-# GOOD-NEXT: Offset: 0x18000
-# GOOD-NEXT: VirtualAddress: 0x8000
-# GOOD-NEXT: PhysicalAddress: 0x8000
-# GOOD-NEXT: FileSize: 2320
-# GOOD-NEXT: MemSize: 2320
-# GOOD-NEXT: Flags [ (0x6)
-# GOOD-NEXT: PF_R (0x4)
-# GOOD-NEXT: PF_W (0x2)
-# GOOD-NEXT: ]
-# GOOD-NEXT: Alignment: 65536
-# GOOD-NEXT: }
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
# RUN: echo "SECTIONS { \
# RUN: .sec1 0x8000 : AT(0x8000) { sec1_start = .; *(.first_sec) sec1_end = .;} \
@@ -63,8 +7,8 @@
# RUN: }" > %t-lma.script
# RUN: not ld.lld -o %t.so --script %t-lma.script %t.o -shared 2>&1 | FileCheck %s -check-prefix LMA-OVERLAP-ERR
# LMA-OVERLAP-ERR: error: section .sec1 load address range overlaps with .sec2
-# LMA-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000 -> 0x80FF]
-# LMA-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8080 -> 0x817F]
+# LMA-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000, 0x80FF]
+# LMA-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8080, 0x817F]
# Check that we create the expected binary with --noinhibit-exec or --no-check-sections:
# RUN: ld.lld -o %t.so --script %t-lma.script %t.o -shared --noinhibit-exec
@@ -75,17 +19,16 @@
# overlaps with where .sec1 is loaded:
# RUN: llvm-readobj -sections -program-headers -elf-output-style=GNU %t.so | FileCheck %s -check-prefix BAD-LMA
# BAD-LMA-LABEL: Section Headers:
-# BAD-LMA: .sec1 PROGBITS 0000000000008000 018000 000100 00 WA 0 0 1
-# BAD-LMA: .sec2 PROGBITS 0000000000008800 018800 000100 00 WA 0 0 1
+# BAD-LMA: .sec1 PROGBITS 0000000000008000 002000 000100 00 WA 0 0 1
+# BAD-LMA: .sec2 PROGBITS 0000000000008800 002800 000100 00 WA 0 0 1
# BAD-LMA-LABEL: Program Headers:
# BAD-LMA-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
-# BAD-LMA-NEXT: LOAD 0x010000 0x0000000000000000 0x0000000000000000 0x0001e1 0x0001e1 R E 0x10000
-# BAD-LMA-NEXT: LOAD 0x018000 0x0000000000008000 0x0000000000008000 0x000100 0x000100 RW 0x10000
-# BAD-LMA-NEXT: LOAD 0x018800 0x0000000000008800 0x0000000000008080 0x000110 0x000110 RW 0x10000
+# BAD-LMA-NEXT: LOAD 0x001000 0x0000000000000000 0x0000000000000000 0x0000fd 0x0000fd R E 0x1000
+# BAD-LMA-NEXT: LOAD 0x002000 0x0000000000008000 0x0000000000008000 0x000100 0x000100 RW 0x1000
+# BAD-LMA-NEXT: LOAD 0x002800 0x0000000000008800 0x0000000000008080 0x000170 0x000170 RW 0x1000
# BAD-LMA-LABEL: Section to Segment mapping:
# BAD-LMA: 01 .sec1
-# BAD-LMA: 02 .sec2 .data .got
-
+# BAD-LMA: 02 .sec2 .dynamic
# Now try a script where the virtual memory addresses overlap but ensure that the
# load addresses don't:
@@ -95,23 +38,23 @@
# RUN: }" > %t-vaddr.script
# RUN: not ld.lld -o %t.so --script %t-vaddr.script %t.o -shared 2>&1 | FileCheck %s -check-prefix VADDR-OVERLAP-ERR
# VADDR-OVERLAP-ERR: error: section .sec1 virtual address range overlaps with .sec2
-# VADDR-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000 -> 0x80FF]
-# VADDR-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8020 -> 0x811F]
+# VADDR-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000, 0x80FF]
+# VADDR-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8020, 0x811F]
# Check that the expected binary was created with --noinhibit-exec:
# RUN: ld.lld -o %t.so --script %t-vaddr.script %t.o -shared --noinhibit-exec
# RUN: llvm-readobj -sections -program-headers -elf-output-style=GNU %t.so | FileCheck %s -check-prefix BAD-VADDR
# BAD-VADDR-LABEL: Section Headers:
-# BAD-VADDR: .sec1 PROGBITS 0000000000008000 018000 000100 00 WA 0 0 1
-# BAD-VADDR: .sec2 PROGBITS 0000000000008020 028020 000100 00 WA 0 0 1
+# BAD-VADDR: .sec1 PROGBITS 0000000000008000 002000 000100 00 WA 0 0 1
+# BAD-VADDR: .sec2 PROGBITS 0000000000008020 003020 000100 00 WA 0 0 1
# BAD-VADDR-LABEL: Program Headers:
# BAD-VADDR-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
-# BAD-VADDR-NEXT: LOAD 0x010000 0x0000000000000000 0x0000000000000000 0x0001e1 0x0001e1 R E 0x10000
-# BAD-VADDR-NEXT: LOAD 0x018000 0x0000000000008000 0x0000000000008000 0x000100 0x000100 RW 0x10000
-# BAD-VADDR-NEXT: LOAD 0x028020 0x0000000000008020 0x0000000000008800 0x000110 0x000110 RW 0x10000
+# BAD-VADDR-NEXT: LOAD 0x001000 0x0000000000000000 0x0000000000000000 0x0000fd 0x0000fd R E 0x1000
+# BAD-VADDR-NEXT: LOAD 0x002000 0x0000000000008000 0x0000000000008000 0x000100 0x000100 RW 0x1000
+# BAD-VADDR-NEXT: LOAD 0x003020 0x0000000000008020 0x0000000000008800 0x000170 0x000170 RW 0x1000
# BAD-VADDR-LABEL: Section to Segment mapping:
# BAD-VADDR: 01 .sec1
-# BAD-VADDR: 02 .sec2 .data .got
+# BAD-VADDR: 02 .sec2 .dynamic
# Finally check the case where both LMA and vaddr overlap
@@ -123,17 +66,17 @@
# RUN: not ld.lld -o %t.so --script %t-both-overlap.script %t.o -shared 2>&1 | FileCheck %s -check-prefix BOTH-OVERLAP-ERR
# BOTH-OVERLAP-ERR: error: section .sec1 file range overlaps with .sec2
-# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x18000 -> 0x180FF]
-# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x18040 -> 0x1813F]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x2000, 0x20FF]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x2040, 0x213F]
# BOTH-OVERLAP-ERR: error: section .sec1 virtual address range overlaps with .sec2
-# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000 -> 0x80FF]
-# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8040 -> 0x813F]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000, 0x80FF]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8040, 0x813F]
# BOTH-OVERLAP-ERR: error: section .sec1 load address range overlaps with .sec2
-# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000 -> 0x80FF]
-# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8040 -> 0x813F]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec1 range is [0x8000, 0x80FF]
+# BOTH-OVERLAP-ERR-NEXT: >>> .sec2 range is [0x8040, 0x813F]
# RUN: ld.lld -o %t.so --script %t-both-overlap.script %t.o -shared --noinhibit-exec
-# Note: I case everything overlaps we create a binary with overlapping file
+# Note: In case everything overlaps we create a binary with overlapping file
# offsets. ld.bfd seems to place .sec1 to file offset 18000 and .sec2
# at 18100 so that only virtual addr and LMA overlap
# However, in order to create such a broken binary the user has to ignore a
@@ -141,24 +84,23 @@
# RUN: llvm-objdump -s %t.so | FileCheck %s -check-prefix BROKEN-OUTPUT-FILE
# BROKEN-OUTPUT-FILE-LABEL: Contents of section .sec1:
-# BROKEN-OUTPUT-FILE-NEXT: 8000 01010101 01010101 01010101 01010101 ................
-# BROKEN-OUTPUT-FILE-NEXT: 8010 01010101 01010101 01010101 01010101 ................
-# BROKEN-OUTPUT-FILE-NEXT: 8020 01010101 01010101 01010101 01010101 ................
-# BROKEN-OUTPUT-FILE-NEXT: 8030 01010101 01010101 01010101 01010101 ................
+# BROKEN-OUTPUT-FILE-NEXT: 8000 01010101 01010101 01010101 01010101
+# BROKEN-OUTPUT-FILE-NEXT: 8010 01010101 01010101 01010101 01010101
+# BROKEN-OUTPUT-FILE-NEXT: 8020 01010101 01010101 01010101 01010101
+# BROKEN-OUTPUT-FILE-NEXT: 8030 01010101 01010101 01010101 01010101
# Starting here the contents of .sec2 overwrites .sec1:
-# BROKEN-OUTPUT-FILE-NEXT: 8040 02020202 02020202 02020202 02020202 ................
+# BROKEN-OUTPUT-FILE-NEXT: 8040 02020202 02020202 02020202 02020202
# RUN: llvm-readobj -sections -program-headers -elf-output-style=GNU %t.so | FileCheck %s -check-prefix BAD-BOTH
# BAD-BOTH-LABEL: Section Headers:
-# BAD-BOTH: .sec1 PROGBITS 0000000000008000 018000 000100 00 WA 0 0 1
-# BAD-BOTH: .sec2 PROGBITS 0000000000008040 018040 000100 00 WA 0 0 1
+# BAD-BOTH: .sec1 PROGBITS 0000000000008000 002000 000100 00 WA 0 0 1
+# BAD-BOTH: .sec2 PROGBITS 0000000000008040 002040 000100 00 WA 0 0 1
# BAD-BOTH-LABEL: Program Headers:
# BAD-BOTH-NEXT: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
-# BAD-BOTH-NEXT: LOAD 0x010000 0x0000000000000000 0x0000000000000000 0x0001e1 0x0001e1 R E 0x10000
-# BAD-BOTH-NEXT: LOAD 0x018000 0x0000000000008000 0x0000000000008000 0x000150 0x000150 RW 0x10000
+# BAD-BOTH-NEXT: LOAD 0x001000 0x0000000000000000 0x0000000000000000 0x0000fd 0x0000fd R E 0x1000
+# BAD-BOTH-NEXT: LOAD 0x002000 0x0000000000008000 0x0000000000008000 0x0001b0 0x0001b0 RW 0x1000
# BAD-BOTH-LABEL: Section to Segment mapping:
-# BAD-BOTH: 01 .sec1 .sec2 .data .got
-
+# BAD-BOTH: 01 .sec1 .sec2 .dynamic
.section .first_sec,"aw",@progbits
.rept 0x100
diff --git a/test/ELF/linkerscript/page-size-align.s b/test/ELF/linkerscript/page-size-align.s
deleted file mode 100644
index 771bb13..0000000
--- a/test/ELF/linkerscript/page-size-align.s
+++ /dev/null
@@ -1,22 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-
-# RUN: echo "SECTIONS { \
-# RUN: . = SIZEOF_HEADERS; \
-# RUN: .text : { *(.text) } \
-# RUN: . = ALIGN(CONSTANT(MAXPAGESIZE)); \
-# RUN: . = . + 0x3000; \
-# RUN: .dynamic : { *(.dynamic) } \
-# RUN: }" > %t.script
-
-# RUN: ld.lld -T %t.script -z max-page-size=0x4000 %t.o -o %t.so -shared
-# RUN: llvm-readobj -s %t.so | FileCheck %s
-
-# CHECK: Name: .dynamic
-# CHECK-NEXT: Type: SHT_DYNAMIC
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: SHF_WRITE
-# CHECK-NEXT: ]
-# CHECK-NEXT: Address: 0x7000
-# CHECK-NEXT: Offset: 0x3000
diff --git a/test/ELF/linkerscript/page-size-align.test b/test/ELF/linkerscript/page-size-align.test
new file mode 100644
index 0000000..f694131
--- /dev/null
+++ b/test/ELF/linkerscript/page-size-align.test
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux /dev/null -o %t.o
+# RUN: ld.lld -T %s -z max-page-size=0x4000 %t.o -o %t.so -shared
+# RUN: llvm-readobj -s %t.so | FileCheck %s
+
+SECTIONS {
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+ . = ALIGN(CONSTANT(MAXPAGESIZE));
+ . = . + 0x3000;
+ .dynamic : { *(.dynamic) }
+}
+
+# CHECK: Name: .dynamic
+# CHECK-NEXT: Type: SHT_DYNAMIC
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address: 0x7000
+# CHECK-NEXT: Offset: 0x3000
diff --git a/test/ELF/linkerscript/parse-section-in-addr.s b/test/ELF/linkerscript/parse-section-in-addr.s
deleted file mode 100644
index 7a79f64..0000000
--- a/test/ELF/linkerscript/parse-section-in-addr.s
+++ /dev/null
@@ -1,10 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-
-# RUN: echo "SECTIONS { \
-# RUN: .foo-bar : AT(ADDR(.foo-bar)) { *(.text) } \
-# RUN: }" > %t.script
-# RUN: ld.lld -o %t.so --script %t.script %t.o -shared
-# RUN: llvm-readelf -S %t.so | FileCheck %s
-
-# CHECK: .foo-bar
diff --git a/test/ELF/linkerscript/parse-section-in-addr.test b/test/ELF/linkerscript/parse-section-in-addr.test
new file mode 100644
index 0000000..6f42a6f
--- /dev/null
+++ b/test/ELF/linkerscript/parse-section-in-addr.test
@@ -0,0 +1,10 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux /dev/null -o %t.o
+# RUN: ld.lld -o %t.so --script %s %t.o -shared
+# RUN: llvm-readelf -S %t.so | FileCheck %s
+
+SECTIONS {
+ .foo-bar : AT(ADDR(.foo-bar)) { *(.text) }
+}
+
+# CHECK: .foo-bar
diff --git a/test/ELF/linkerscript/rosegment.s b/test/ELF/linkerscript/rosegment.test
similarity index 71%
rename from test/ELF/linkerscript/rosegment.s
rename to test/ELF/linkerscript/rosegment.test
index 3201b8b..41479e6 100644
--- a/test/ELF/linkerscript/rosegment.s
+++ b/test/ELF/linkerscript/rosegment.test
@@ -1,12 +1,14 @@
# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t
# Test that with linker scripts we don't create a RO PT_LOAD.
-# RUN: echo "SECTIONS {}" > %t.script
-# RUN: ld.lld -o %t1 --script %t.script %t -shared
+# RUN: ld.lld -o %t1 --script %s %t -shared
# RUN: llvm-readobj -l %t1 | FileCheck %s
+SECTIONS {
+}
+
# CHECK-NOT: Type: PT_LOAD
# CHECK: Type: PT_LOAD
diff --git a/test/ELF/linkerscript/sections-va-overflow.test b/test/ELF/linkerscript/sections-va-overflow.test
new file mode 100644
index 0000000..7ede6ec
--- /dev/null
+++ b/test/ELF/linkerscript/sections-va-overflow.test
@@ -0,0 +1,22 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/sections-va-overflow.s -o %t.o
+# RUN: not ld.lld -o /dev/null --script %s %t.o 2>&1 | FileCheck %s -check-prefix=ERR
+
+PHDRS {
+ ph_headers PT_PHDR PHDRS;
+ ph_text PT_LOAD FILEHDR PHDRS FLAGS (0x1 | 0x4);
+}
+
+SECTIONS {
+ . = 0xffffffff20000000;
+ .text : { *(.text*) } : ph_text
+ .test 0x1000 : { BYTE(0) }
+ .bss : { *(.bss*) }
+}
+
+## Section .test has VA 0x1000 and placed in the same segment as section .text
+## with VA 0xffffffff20000000. That might be technically correct, but most probably
+## is a result of a broken script file and causes file offset calculation overflow.
+## It seems we do not have to support it, so we don't and we report an error in this case.
+# ERR: error: unable to place section .text at file offset [0xFFFFFFFF20000000, 0xFFFFFFFE40000000]; check your linker script for overflows
+# ERR-NOT: unable to place section .bss
diff --git a/test/ELF/linkerscript/sort-constructors.s b/test/ELF/linkerscript/sort-constructors.s
deleted file mode 100644
index a0c23af..0000000
--- a/test/ELF/linkerscript/sort-constructors.s
+++ /dev/null
@@ -1,5 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
-# RUN: echo "SECTIONS { .aaa : { SORT(CONSTRUCTORS) } }" > %t1.script
-# RUN: ld.lld -shared -o %t1 --script %t1.script %t1.o
-# RUN: llvm-readobj %t1 > /dev/null
diff --git a/test/ELF/linkerscript/sort-constructors.test b/test/ELF/linkerscript/sort-constructors.test
new file mode 100644
index 0000000..698208a
--- /dev/null
+++ b/test/ELF/linkerscript/sort-constructors.test
@@ -0,0 +1,8 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t1.o
+# RUN: ld.lld -shared -o %t1 --script %s %t1.o
+# RUN: llvm-readobj %t1 > /dev/null
+
+SECTIONS {
+ .aaa : { SORT(CONSTRUCTORS) }
+}
diff --git a/test/ELF/linkerscript/start-end.s b/test/ELF/linkerscript/start-end.s
deleted file mode 100644
index b68606a..0000000
--- a/test/ELF/linkerscript/start-end.s
+++ /dev/null
@@ -1,16 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
-# RUN: echo "SECTIONS { \
-# RUN: .init_array : { \
-# RUN: __init_array_start = .; \
-# RUN: *(.init_array) \
-# RUN: __init_array_end = .; } }" > %t.script
-# RUN: ld.lld %t.o -script %t.script -o %t 2>&1
-
-.globl _start
-.text
-_start:
- nop
-
-.section .init_array, "aw"
- .quad 0
diff --git a/test/ELF/linkerscript/start-end.test b/test/ELF/linkerscript/start-end.test
new file mode 100644
index 0000000..ab7504d
--- /dev/null
+++ b/test/ELF/linkerscript/start-end.test
@@ -0,0 +1,12 @@
+# REQUIRES: x86
+# RUN: echo '.section .init_array, "aw"; .quad 0' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o
+# RUN: ld.lld %t.o -script %s -o %t 2>&1
+
+SECTIONS {
+ .init_array : {
+ __init_array_start = .;
+ *(.init_array)
+ __init_array_end = .;
+ }
+}
diff --git a/test/ELF/linkerscript/symbol-only-flags.s b/test/ELF/linkerscript/symbol-only-flags.s
deleted file mode 100644
index 300d8d8..0000000
--- a/test/ELF/linkerscript/symbol-only-flags.s
+++ /dev/null
@@ -1,20 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o
-# RUN: echo "SECTIONS { . = SIZEOF_HEADERS; \
-# RUN: .tbss : { *(.tbss) } \
-# RUN: .foo : { bar = .; } }" > %t.script
-# RUN: ld.lld -o %t --script %t.script %t.o
-# RUN: llvm-readobj -s %t | FileCheck %s
-
-## Check .foo does not get SHF_TLS flag.
-# CHECK: Section {
-# CHECK: Index:
-# CHECK: Name: .foo
-# CHECK-NEXT: Type: SHT_NOBITS
-# CHECK-NEXT: Flags [
-# CHECK-NEXT: SHF_ALLOC
-# CHECK-NEXT: SHF_WRITE
-# CHECK-NEXT: ]
-
-.section .tbss,"awT",@nobits
-.quad 0
diff --git a/test/ELF/linkerscript/symbol-only-flags.test b/test/ELF/linkerscript/symbol-only-flags.test
new file mode 100644
index 0000000..cea2539
--- /dev/null
+++ b/test/ELF/linkerscript/symbol-only-flags.test
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: echo '.section .tbss,"awT",@nobits; .quad 0' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t.o
+# RUN: ld.lld -o %t --script %s %t.o
+# RUN: llvm-readobj -s %t | FileCheck %s
+
+SECTIONS {
+ . = SIZEOF_HEADERS;
+ .tbss : { *(.tbss) }
+ .foo : { bar = .; }
+}
+
+## Check .foo does not get SHF_TLS flag.
+# CHECK: Section {
+# CHECK: Index:
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_NOBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_WRITE
+# CHECK-NEXT: ]
diff --git a/test/ELF/linkerscript/symbol-only.s b/test/ELF/linkerscript/symbol-only.s
deleted file mode 100644
index 76d54f0..0000000
--- a/test/ELF/linkerscript/symbol-only.s
+++ /dev/null
@@ -1,21 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-
-# RUN: echo "SECTIONS { \
-# RUN: . = SIZEOF_HEADERS; \
-# RUN: abc : { foo = .; } \
-# RUN: . = ALIGN(0x1000); \
-# RUN: bar : { *(bar) } \
-# RUN: }" > %t.script
-# RUN: ld.lld -o %t1 --script %t.script %t -shared
-# RUN: llvm-objdump -section-headers -t %t1 | FileCheck %s
-# CHECK: Sections:
-# CHECK-NEXT: Idx Name Size Address
-# CHECK-NEXT: 0 00000000 0000000000000000
-# CHECK: abc 00000000 [[ADDR:[0-9a-f]*]] BSS
-# CHECK-NEXT: bar 00000000 0000000000001000 DATA
-
-# CHECK: SYMBOL TABLE:
-# CHECK: [[ADDR]] abc 00000000 foo
-
-.section bar, "a"
diff --git a/test/ELF/linkerscript/symbol-only.test b/test/ELF/linkerscript/symbol-only.test
new file mode 100644
index 0000000..e2123fb
--- /dev/null
+++ b/test/ELF/linkerscript/symbol-only.test
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: echo '.section bar, "a"' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t
+# RUN: ld.lld -o %t1 --script %s %t -shared
+# RUN: llvm-objdump -section-headers -t %t1 | FileCheck %s
+
+SECTIONS {
+ . = SIZEOF_HEADERS;
+ abc : { foo = .; }
+ . = ALIGN(0x1000);
+ bar : { *(bar) }
+}
+
+# CHECK: Sections:
+# CHECK-NEXT: Idx Name Size Address
+# CHECK-NEXT: 0 00000000 0000000000000000
+# CHECK: abc 00000000 [[ADDR:[0-9a-f]*]] BSS
+# CHECK-NEXT: bar 00000000 0000000000001000 DATA
+
+# CHECK: SYMBOL TABLE:
+# CHECK: [[ADDR]] abc 00000000 foo
diff --git a/test/ELF/linkerscript/symbols-non-alloc.s b/test/ELF/linkerscript/symbols-non-alloc.s
deleted file mode 100644
index e51a39e..0000000
--- a/test/ELF/linkerscript/symbols-non-alloc.s
+++ /dev/null
@@ -1,19 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t
-
-# RUN: echo "SECTIONS { . = SIZEOF_HEADERS; \
-# RUN: .text : { *(.text) } \
-# RUN: .nonalloc : { *(.nonalloc) } \
-# RUN: Sym = .; \
-# RUN: }" > %t.script
-# RUN: ld.lld -o %t2 --script %t.script %t
-# RUN: llvm-objdump -section-headers -t %t2 | FileCheck %s
-
-# CHECK: Sections:
-# CHECK: .nonalloc 00000008 0000000000000000
-
-# CHECK: SYMBOL TABLE:
-# CHECK: 0000000000000008 .nonalloc 00000000 Sym
-
-.section .nonalloc,""
- .quad 0
diff --git a/test/ELF/linkerscript/symbols-non-alloc.test b/test/ELF/linkerscript/symbols-non-alloc.test
new file mode 100644
index 0000000..6d7580a
--- /dev/null
+++ b/test/ELF/linkerscript/symbols-non-alloc.test
@@ -0,0 +1,18 @@
+# REQUIRES: x86
+# RUN: echo '.section .nonalloc,""; .quad 0' \
+# RUN: | llvm-mc -filetype=obj -triple=x86_64-unknown-linux - -o %t
+# RUN: ld.lld -o %t2 --script %s %t
+# RUN: llvm-objdump -section-headers -t %t2 | FileCheck %s
+
+# CHECK: Sections:
+# CHECK: .nonalloc 00000008 0000000000000000
+
+# CHECK: SYMBOL TABLE:
+# CHECK: 0000000000000008 .nonalloc 00000000 Sym
+
+SECTIONS {
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+ .nonalloc : { *(.nonalloc) }
+ Sym = .;
+}
diff --git a/test/ELF/linkerscript/synthetic-relsec-layout.s b/test/ELF/linkerscript/synthetic-relsec-layout.s
new file mode 100644
index 0000000..efaa946
--- /dev/null
+++ b/test/ELF/linkerscript/synthetic-relsec-layout.s
@@ -0,0 +1,16 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: echo "SECTIONS { .foo : { *(.rela.dyn) } }" > %t.script
+# RUN: ld.lld -T %t.script %t.o -o %t.so -shared
+# RUN: llvm-readobj -r %t.so | FileCheck %s
+
+# Check we are able to do custom layout for synthetic sections.
+# (here we check we can place synthetic .rela.dyn into .foo).
+
+# CHECK: Relocations [
+# CHECK: Section ({{.*}}) .foo {
+# CHECK: R_X86_64_64 .foo 0x0
+# CHECK: }
+
+.data
+.quad .foo
diff --git a/test/ELF/linkerscript/unused-synthetic2.s b/test/ELF/linkerscript/unused-synthetic2.test
similarity index 67%
rename from test/ELF/linkerscript/unused-synthetic2.s
rename to test/ELF/linkerscript/unused-synthetic2.test
index cdde97d..755d1af 100644
--- a/test/ELF/linkerscript/unused-synthetic2.s
+++ b/test/ELF/linkerscript/unused-synthetic2.test
@@ -1,9 +1,12 @@
# REQUIRES: arm
-# RUN: llvm-mc -filetype=obj -triple=armv7-unknown-linux-gnueabi %s -o %t.o
-# RUN: echo "SECTIONS { .trap : { *(.ARM.exidx) *(.dummy) } }" > %t.script
+# RUN: llvm-mc -filetype=obj -triple=armv7-unknown-linux-gnueabi /dev/null -o %t.o
## We incorrectly removed unused synthetic sections and crashed before.
## Check we do not crash and do not produce .trap output section.
-# RUN: ld.lld -shared -o %t.so --script %t.script %t.o
+# RUN: ld.lld -shared -o %t.so --script %s %t.o
# RUN: llvm-objdump -section-headers %t.so | FileCheck %s
# CHECK-NOT: .trap
+
+SECTIONS {
+ .trap : { *(.ARM.exidx) *(.dummy) }
+}
diff --git a/test/ELF/lto-plugin-ignore.s b/test/ELF/lto-plugin-ignore.s
index 1393df9..65230f1 100644
--- a/test/ELF/lto-plugin-ignore.s
+++ b/test/ELF/lto-plugin-ignore.s
@@ -5,7 +5,6 @@
# RUN: -plugin-opt=-pass-through=-lgcc -plugin-opt=-function-sections \
# RUN: -plugin-opt=-data-sections -plugin-opt=thinlto -o /dev/null
-# RUN: not ld.lld %t -plugin-opt=-data-sectionxxx \
-# RUN: -plugin-opt=-function-sectionxxx 2>&1 | FileCheck %s
-# CHECK: Unknown command line argument '-data-sectionxxx'
-# CHECK: Unknown command line argument '-function-sectionxxx'
+# RUN: not ld.lld %t -plugin-opt=-abc -plugin-opt=-xyz 2>&1 | FileCheck %s
+# CHECK: error: --plugin-opt: ld.lld{{.*}}: Unknown command line argument '-abc'
+# CHECK: error: --plugin-opt: ld.lld{{.*}}: Unknown command line argument '-xyz'
diff --git a/test/ELF/lto/version-script2.ll b/test/ELF/lto/version-script2.ll
new file mode 100644
index 0000000..ab8f75a
--- /dev/null
+++ b/test/ELF/lto/version-script2.ll
@@ -0,0 +1,15 @@
+; REQUIRES: x86
+; RUN: llvm-as %s -o %t.o
+; RUN: echo "VER1 {};" > %t.script
+; RUN: ld.lld %t.o -o %t.so -shared --version-script %t.script
+; RUN: llvm-readobj -dyn-symbols %t.so | FileCheck %s
+
+; test that we have the correct version.
+; CHECK: Name: foo@@VER1 (
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+module asm ".global foo"
+module asm "foo:"
+module asm ".symver foo,foo@@@VER1"
diff --git a/test/ELF/map-file.s b/test/ELF/map-file.s
index 989ae7d..23b9381 100644
--- a/test/ELF/map-file.s
+++ b/test/ELF/map-file.s
@@ -15,6 +15,8 @@
.global _start
_start:
+.cfi_startproc
+.cfi_endproc
.quad sharedFoo
.quad sharedBar
.byte 0xe8
@@ -26,8 +28,8 @@
.global _Z1fi
_Z1fi:
.cfi_startproc
-.cfi_endproc
nop
+.cfi_endproc
.weak bar
bar:
.long bar - .
@@ -51,8 +53,10 @@
// CHECK-NEXT: 00000000002002d0 0000000000000030 8 <internal>:(.rela.dyn)
// CHECK-NEXT: 0000000000200300 0000000000000030 8 .rela.plt
// CHECK-NEXT: 0000000000200300 0000000000000030 8 <internal>:(.rela.plt)
-// CHECK-NEXT: 0000000000200330 0000000000000030 8 .eh_frame
-// CHECK-NEXT: 0000000000200330 0000000000000030 8 <internal>:(.eh_frame)
+// CHECK-NEXT: 0000000000200330 0000000000000060 8 .eh_frame
+// CHECK-NEXT: 0000000000200330 000000000000002c 0 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.eh_frame+0x0)
+// CHECK-NEXT: 0000000000200360 0000000000000014 0 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.eh_frame+0x2c)
+// CHECK-NEXT: 0000000000200378 0000000000000018 0 {{.*}}{{/|\\}}map-file.s.tmp2.o:(.eh_frame+0x18)
// CHECK-NEXT: 0000000000201000 000000000000002d 4 .text
// CHECK-NEXT: 0000000000201000 0000000000000028 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.text)
// CHECK-NEXT: 0000000000201000 0000000000000000 0 _start
diff --git a/test/ELF/merge-reloc-O0.s b/test/ELF/merge-reloc-O0.s
new file mode 100644
index 0000000..daf5c34
--- /dev/null
+++ b/test/ELF/merge-reloc-O0.s
@@ -0,0 +1,48 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: ld.lld %t.o -r -o %t2.o -O0
+# RUN: llvm-readobj -s -section-data %t2.o | FileCheck %s
+
+# We combine just the sections with the same name and sh_entsize.
+
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 16
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment: 8
+# CHECK-NEXT: EntrySize: 8
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 41000000 00000000 42000000 00000000
+# CHECK-NEXT: )
+
+# CHECK: Name: .foo
+# CHECK-NEXT: Type: SHT_PROGBITS
+# CHECK-NEXT: Flags [
+# CHECK-NEXT: SHF_ALLOC
+# CHECK-NEXT: SHF_MERGE
+# CHECK-NEXT: ]
+# CHECK-NEXT: Address:
+# CHECK-NEXT: Offset:
+# CHECK-NEXT: Size: 8
+# CHECK-NEXT: Link:
+# CHECK-NEXT: Info:
+# CHECK-NEXT: AddressAlignment: 4
+# CHECK-NEXT: EntrySize: 4
+# CHECK-NEXT: SectionData (
+# CHECK-NEXT: 0000: 41000000 42000000
+# CHECK-NEXT: )
+
+ .section .foo, "aM",@progbits,8,unique,0
+ .quad 0x41
+ .section .foo, "aM",@progbits,8,unique,1
+ .quad 0x42
+ .section .foo, "aM",@progbits,4,unique,2
+ .long 0x41
+ .long 0x42
diff --git a/test/ELF/multiple-cu.s b/test/ELF/multiple-cu.s
new file mode 100644
index 0000000..df2b94d
--- /dev/null
+++ b/test/ELF/multiple-cu.s
@@ -0,0 +1,38 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/multiple-cu.s -o %t2.o
+# RUN: ld.lld -r -o %t.o %t1.o %t2.o
+# RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s
+
+# CHECK: error: undefined symbol: foo
+# CHECK-NEXT: referenced by test1.c:2
+
+# CHECK: error: undefined symbol: bar
+# CHECK-NEXT: referenced by test2.c:2
+
+ .globl _start
+_start:
+ .file 1 "test1.c"
+ .loc 1 2 0
+ jmp foo
+
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+
+ .section .debug_info,"",@progbits
+ .long .Lend0 - .Lbegin0 # Length of Unit
+.Lbegin0:
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit
+ .long .debug_line # DW_AT_stmt_list
+.Lend0:
+ .section .debug_line,"",@progbits
diff --git a/test/ELF/ppc64_entry_point.s b/test/ELF/ppc64_entry_point.s
new file mode 100644
index 0000000..b3bd5ec
--- /dev/null
+++ b/test/ELF/ppc64_entry_point.s
@@ -0,0 +1,36 @@
+# REQUIRES: ppc
+# RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t
+# RUN: ld.lld %t -o %t2
+# RUN: llvm-objdump -D %t2 | FileCheck %s
+
+.text
+.abiversion 2
+.globl _start
+.p2align 4
+.type _start,@function
+
+_start:
+.Lfunc_begin0:
+.Lfunc_gep0:
+ lis 4, .Lfunc_gep0@ha
+ addi 4, 4, .Lfunc_gep0@l
+ # now r4 should contain the address of _start
+
+ lis 5, .TOC.-.Lfunc_gep0@ha
+ addi 5, 5, .TOC.-.Lfunc_gep0@l
+ # now r5 should contain the offset s.t. r4 + r5 = TOC base
+
+ # exit 55
+ li 0, 1
+ li 3, 55
+ sc
+.Lfunc_end0:
+ .size _start, .Lfunc_end0-.Lfunc_begin0
+
+// CHECK: 10010000: 01 10 80 3c lis 4, 4097
+// CHECK-NEXT: 10010004: 00 00 84 38 addi 4, 4, 0
+// CHECK-NEXT: 10010008: 02 00 a0 3c lis 5, 2
+// CHECK-NEXT: 1001000c: 00 80 a5 38 addi 5, 5, -32768
+// CHECK: Disassembly of section .got:
+// CHECK-NEXT: .got:
+// CHECK-NEXT: 10020000: 00 80 02 10
diff --git a/test/ELF/relocatable-comdat-multiple.s b/test/ELF/relocatable-comdat-multiple.s
index 4c3e7ce..935fbdc 100644
--- a/test/ELF/relocatable-comdat-multiple.s
+++ b/test/ELF/relocatable-comdat-multiple.s
@@ -8,6 +8,8 @@
# CHECK-NEXT: Group {
# CHECK-NEXT: Name: .group
# CHECK-NEXT: Index: 2
+# CHECK-NEXT: Link: 8
+# CHECK-NEXT: Info: 1
# CHECK-NEXT: Type: COMDAT
# CHECK-NEXT: Signature: aaa
# CHECK-NEXT: Section(s) in group [
@@ -18,6 +20,8 @@
# CHECK-NEXT: Group {
# CHECK-NEXT: Name: .group
# CHECK-NEXT: Index: 5
+# CHECK-NEXT: Link: 8
+# CHECK-NEXT: Info: 2
# CHECK-NEXT: Type: COMDAT
# CHECK-NEXT: Signature: bbb
# CHECK-NEXT: Section(s) in group [
diff --git a/test/ELF/relocatable-comdat.s b/test/ELF/relocatable-comdat.s
index 24504d2..11aa30c 100644
--- a/test/ELF/relocatable-comdat.s
+++ b/test/ELF/relocatable-comdat.s
@@ -30,6 +30,8 @@
# CHECK-NEXT: Group {
# CHECK-NEXT: Name: .group
# CHECK-NEXT: Index: 2
+# CHECK-NEXT: Link: 5
+# CHECK-NEXT: Info: 1
# CHECK-NEXT: Type: COMDAT
# CHECK-NEXT: Signature: abc
# CHECK-NEXT: Section(s) in group [
diff --git a/test/ELF/relocatable-comdat2.s b/test/ELF/relocatable-comdat2.s
index 2643e64..27844fe 100644
--- a/test/ELF/relocatable-comdat2.s
+++ b/test/ELF/relocatable-comdat2.s
@@ -13,6 +13,8 @@
# CHECK-NEXT: Group {
# CHECK-NEXT: Name: .group
# CHECK-NEXT: Index: 2
+# CHECK-NEXT: Link: 7
+# CHECK-NEXT: Info: 1
# CHECK-NEXT: Type: COMDAT
# CHECK-NEXT: Signature: bar
# CHECK-NEXT: Section(s) in group [
@@ -22,6 +24,8 @@
# CHECK-NEXT: Group {
# CHECK-NEXT: Name: .group
# CHECK-NEXT: Index: 4
+# CHECK-NEXT: Link: 7
+# CHECK-NEXT: Info: 2
# CHECK-NEXT: Type: COMDAT
# CHECK-NEXT: Signature: zed
# CHECK-NEXT: Section(s) in group [
diff --git a/test/ELF/relocation-size-err.s b/test/ELF/relocation-size-err.s
index 7bef45b..763ad87 100644
--- a/test/ELF/relocation-size-err.s
+++ b/test/ELF/relocation-size-err.s
@@ -2,7 +2,7 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
// RUN: not ld.lld %t.o -o %t.so -shared 2>&1 | FileCheck %s
-// CHECK: error: can't create dynamic relocation R_X86_64_SIZE64 against symbol: foo in readonly segment; recompile object files with -fPIC
+// CHECK: error: can't create dynamic relocation R_X86_64_SIZE64 against symbol: foo in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output
.global foo
foo:
diff --git a/test/ELF/section-metadata-err.s b/test/ELF/section-metadata-err.s
index 1bcbedf..ba102be 100644
--- a/test/ELF/section-metadata-err.s
+++ b/test/ELF/section-metadata-err.s
@@ -3,7 +3,7 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s
-# CHECK: error: a section with SHF_LINK_ORDER should not refer a non-regular section: {{.*}}section-metadata-err.s.tmp.o:(.foo)
+# CHECK: error: a section .bar with SHF_LINK_ORDER should not refer a non-regular section: {{.*}}section-metadata-err.s.tmp.o:(.foo)
.global _start
_start:
@@ -12,4 +12,4 @@
.section .foo,"aM",@progbits,8
.quad 0
-.section bar,"ao",@progbits,.foo
+.section .bar,"ao",@progbits,.foo
diff --git a/test/ELF/section-metadata-err2.s b/test/ELF/section-metadata-err2.s
new file mode 100644
index 0000000..b2b611f
--- /dev/null
+++ b/test/ELF/section-metadata-err2.s
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s
+
+## Check we do not crash and report proper errors.
+# CHECK: error: a section .bar with SHF_LINK_ORDER should not refer a non-regular section: {{.*}}section-metadata-err2.s.tmp.o:(.foo)
+# CHECK: error: a section .bar with SHF_LINK_ORDER should not refer a non-regular section: {{.*}}section-metadata-err2.s.tmp.o:(.foo)
+
+.section .foo,"aM",@progbits,8
+.quad 0
+
+.section .bar,"ao",@progbits,.foo,unique,1
+.quad 0
+
+.section .bar,"ao",@progbits,.foo,unique,2
+.quad 1
diff --git a/test/ELF/section-metadata-err3.s b/test/ELF/section-metadata-err3.s
new file mode 100644
index 0000000..36666bf
--- /dev/null
+++ b/test/ELF/section-metadata-err3.s
@@ -0,0 +1,17 @@
+# REQUIRES: x86
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
+# RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s
+
+# CHECK: error: incompatible section flags for .bar
+# CHECK-NEXT: >>> {{.*}}section-metadata-err3.s.tmp.o:(.bar): 0x2
+# CHECK-NEXT: >>> output section .bar: 0x82
+
+.section .foo,"a",@progbits
+.quad 0
+
+.section .bar,"ao",@progbits,.foo,unique,1
+.quad 0
+
+.section .bar,"a",@progbits,unique,2
+.quad 1
diff --git a/test/ELF/silent-ignore.test b/test/ELF/silent-ignore.test
index 6655754..adfc244 100644
--- a/test/ELF/silent-ignore.test
+++ b/test/ELF/silent-ignore.test
@@ -1,6 +1,5 @@
RUN: ld.lld --version \
RUN: -allow-shlib-undefined \
-RUN: -cref \
RUN: -g \
RUN: -no-add-needed \
RUN: -no-allow-shlib-undefined \
diff --git a/test/ELF/startstop-shared.s b/test/ELF/startstop-shared.s
index 80ccf3d..4d3192a 100644
--- a/test/ELF/startstop-shared.s
+++ b/test/ELF/startstop-shared.s
@@ -11,18 +11,30 @@
.quad __start_bar
.section bar,"a"
-// Test that we are able to hide the symbol.
-// CHECK: R_X86_64_RELATIVE - 0x[[ADDR:.*]]
+// CHECK: Relocations [
+// CHECK-NEXT: Section {{.*}} .rela.dyn {
+// CHECK-NEXT: R_X86_64_RELATIVE
+// CHECK-NEXT: R_X86_64_RELATIVE
+// CHECK-NEXT: }
+// CHECK-NEXT: ]
-// By default the symbol is visible and we need a dynamic reloc.
-// CHECK: R_X86_64_64 __start_foo 0x0
+// Test that we are able to hide the symbol.
+// By default the symbol is protected.
// CHECK: Name: __start_bar
-// CHECK-NEXT: Value: 0x[[ADDR]]
+// CHECK-NEXT: Value:
// CHECK-NEXT: Size:
// CHECK-NEXT: Binding: Local
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_HIDDEN
+// CHECK-NEXT: ]
// CHECK: Name: __start_foo
// CHECK-NEXT: Value:
// CHECK-NEXT: Size:
// CHECK-NEXT: Binding: Global
+// CHECK-NEXT: Type: None
+// CHECK-NEXT: Other [
+// CHECK-NEXT: STV_PROTECTED
+// CHECK-NEXT: ]
diff --git a/test/ELF/startstop.s b/test/ELF/startstop.s
index 1e75042..470298e 100644
--- a/test/ELF/startstop.s
+++ b/test/ELF/startstop.s
@@ -19,13 +19,12 @@
// DISASM: 1013: 90 nop
// DISASM: 1014: 90 nop
-
// SYMBOL: Relocations [
// SYMBOL-NEXT: Section ({{.*}}) .rela.dyn {
-// SYMBOL-NEXT: 0x2010 R_X86_64_64 __stop_zed1 0x0
-// SYMBOL-NEXT: 0x2018 R_X86_64_64 __stop_zed1 0x1
-// SYMBOL-NEXT: 0x2000 R_X86_64_64 __stop_zed2 0x0
-// SYMBOL-NEXT: 0x2008 R_X86_64_64 __stop_zed2 0x1
+// SYMBOL-NEXT: R_X86_64_RELATIVE
+// SYMBOL-NEXT: R_X86_64_RELATIVE
+// SYMBOL-NEXT: R_X86_64_RELATIVE
+// SYMBOL-NEXT: R_X86_64_RELATIVE
// SYMBOL-NEXT: }
// SYMBOL-NEXT: ]
@@ -45,20 +44,20 @@
// SYMBOL: Symbol {
// SYMBOL: Name: __stop_foo
// SYMBOL: Value: 0x1012
-// STMBOL: STV_HIDDEN
+// SYMBOL: STV_HIDDEN
// SYMBOL: Section: foo
// SYMBOL: }
// SYMBOL: Symbol {
// SYMBOL: Name: __stop_zed1
// SYMBOL: Value: 0x2010
-// STMBOL: Other: 0
+// SYMBOL: STV_PROTECTED
// SYMBOL: Section: zed1
// SYMBOL: }
// SYMBOL: Symbol {
// SYMBOL: Name: __stop_zed2
// SYMBOL: Value: 0x2020
-// STMBOL: Other: 0
+// SYMBOL: STV_PROTECTED
// SYMBOL: Section: zed2
// SYMBOL: }
diff --git a/test/ELF/symbol-ordering-file-warnings.s b/test/ELF/symbol-ordering-file-warnings.s
index 2918c96..aabac53 100644
--- a/test/ELF/symbol-ordering-file-warnings.s
+++ b/test/ELF/symbol-ordering-file-warnings.s
@@ -6,79 +6,81 @@
# Check that a warning is emitted for entries in the file that are not present in any used input.
# RUN: echo "missing" > %t-order-missing.txt
-# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-missing.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,MISSING
+# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-missing.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,MISSING
# Check that the warning can be disabled.
-# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-missing.txt --unresolved-symbols=ignore-all --no-warn-symbol-ordering 2>&1 | \
+# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-missing.txt \
+# RUN: --unresolved-symbols=ignore-all --no-warn-symbol-ordering 2>&1 | \
# RUN: FileCheck %s --check-prefixes=WARN --allow-empty
# Check that the warning can be re-enabled
-# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-missing.txt --unresolved-symbols=ignore-all --no-warn-symbol-ordering --warn-symbol-ordering 2>&1 | \
+# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-missing.txt \
+# RUN: --unresolved-symbols=ignore-all --no-warn-symbol-ordering --warn-symbol-ordering 2>&1 | \
# RUN: FileCheck %s --check-prefixes=WARN,MISSING
# Check that a warning is emitted for undefined symbols.
# RUN: echo "undefined" > %t-order-undef.txt
-# RUN: ld.lld %t1.o %t3.o -o %t --symbol-ordering-file %t-order-undef.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,UNDEFINED
+# RUN: ld.lld %t1.o %t3.o -o %t --symbol-ordering-file %t-order-undef.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,UNDEFINED
# Check that a warning is emitted for imported shared symbols.
# RUN: echo "shared" > %t-order-shared.txt
-# RUN: ld.lld %t1.o %t.so -o %t --symbol-ordering-file %t-order-shared.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,SHARED
+# RUN: ld.lld %t1.o %t.so -o %t --symbol-ordering-file %t-order-shared.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,SHARED
# Check that a warning is emitted for absolute symbols.
# RUN: echo "absolute" > %t-order-absolute.txt
-# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-absolute.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,ABSOLUTE
+# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-absolute.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,ABSOLUTE
# Check that a warning is emitted for symbols discarded due to --gc-sections.
# RUN: echo "gc" > %t-order-gc.txt
-# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-gc.txt --gc-sections --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,GC
+# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-gc.txt --gc-sections \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,GC
# Check that a warning is emitted for the symbol removed due to --icf.
# RUN: echo "icf1" > %t-order-icf.txt
# RUN: echo "icf2" >> %t-order-icf.txt
-# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-icf.txt --icf=all --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,ICF
+# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-icf.txt --icf=all \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,ICF
# Check that a warning is emitted for symbols discarded due to a linker script /DISCARD/ section.
# RUN: echo "discard" > %t-order-discard.txt
# RUN: echo "SECTIONS { /DISCARD/ : { *(.text.discard) } }" > %t.script
-# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-discard.txt -T %t.script --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,DISCARD
+# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-discard.txt -T %t.script \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,DISCARD
# Check that LLD does not warn for discarded COMDAT symbols, if they are present in the kept instance.
# RUN: echo "comdat" > %t-order-comdat.txt
-# RUN: ld.lld %t1.o %t2.o -o %t --symbol-ordering-file %t-order-comdat.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN --allow-empty
+# RUN: ld.lld %t1.o %t2.o -o %t --symbol-ordering-file %t-order-comdat.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN --allow-empty
# Check that if a COMDAT was unused and discarded via --gc-sections, warn for each instance.
-# RUN: ld.lld %t1.o %t2.o -o %t --symbol-ordering-file %t-order-comdat.txt --gc-sections --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,COMDAT
+# RUN: ld.lld %t1.o %t2.o -o %t --symbol-ordering-file %t-order-comdat.txt --gc-sections \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,COMDAT
# Check that if a weak symbol is not kept, because of an equivalent global symbol, no warning is emitted.
# RUN: echo "glob_or_wk" > %t-order-weak.txt
-# RUN: ld.lld %t1.o %t2.o -o %t --symbol-ordering-file %t-order-weak.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN --allow-empty
+# RUN: ld.lld %t1.o %t2.o -o %t --symbol-ordering-file %t-order-weak.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN --allow-empty
# Check that symbols only in unused archive members result in a warning.
# RUN: rm -f %t.a
# RUN: llvm-ar rc %t.a %t3.o
-# RUN: ld.lld %t1.o %t.a -o %t --symbol-ordering-file %t-order-missing.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,MISSING --allow-empty
+# RUN: ld.lld %t1.o %t.a -o %t --symbol-ordering-file %t-order-missing.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,MISSING --allow-empty
# Check that a warning for each same-named symbol with an issue.
# RUN: echo "multi" > %t-order-same-name.txt
-# RUN: ld.lld %t1.o %t2.o %t3.o -o %t --symbol-ordering-file %t-order-same-name.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,MULTI
+# RUN: ld.lld %t1.o %t2.o %t3.o -o %t --symbol-ordering-file %t-order-same-name.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,MULTI
# Check that a warning is emitted if the same symbol is mentioned multiple times in the ordering file.
# RUN: echo "_start" > %t-order-multiple-same.txt
# RUN: echo "_start" >> %t-order-multiple-same.txt
-# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-multiple-same.txt --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,SAMESYM
+# RUN: ld.lld %t1.o -o %t --symbol-ordering-file %t-order-multiple-same.txt \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,SAMESYM
# Check that all warnings can be emitted from the same input.
# RUN: echo "missing_sym" > %t-order-multi.txt
@@ -89,8 +91,8 @@
# RUN: echo "gc" >> %t-order-multi.txt
# RUN: echo "discard" >> %t-order-multi.txt
# RUN: echo "_start" >> %t-order-multi.txt
-# RUN: ld.lld %t1.o %t3.o %t.so -o %t --symbol-ordering-file %t-order-multi.txt --gc-sections -T %t.script --unresolved-symbols=ignore-all 2>&1 | \
-# RUN: FileCheck %s --check-prefixes=WARN,SAMESYM,ABSOLUTE,SHARED,UNDEFINED,GC,DISCARD,MISSING2
+# RUN: ld.lld %t1.o %t3.o %t.so -o %t --symbol-ordering-file %t-order-multi.txt --gc-sections -T %t.script \
+# RUN: --unresolved-symbols=ignore-all 2>&1 | FileCheck %s --check-prefixes=WARN,SAMESYM,ABSOLUTE,SHARED,UNDEFINED,GC,DISCARD,MISSING2
# WARN-NOT: warning:
# SAMESYM: warning: {{.*}}.txt: duplicate ordered symbol: _start
diff --git a/test/ELF/tls-got.s b/test/ELF/tls-got.s
index b1686cd..a0261ee 100644
--- a/test/ELF/tls-got.s
+++ b/test/ELF/tls-got.s
@@ -25,13 +25,13 @@
// CHECK: Relocations [
// CHECK-NEXT: Section (4) .rela.dyn {
// CHECK-NEXT: 0x2020B8 R_X86_64_TPOFF64 tls0 0x0
-// CHECK-NEXT: [[ADDR]] R_X86_64_TPOFF64 tls1 0x0
+// CHECK-NEXT: 0x2020B0 R_X86_64_TPOFF64 tls1 0x0
// CHECK-NEXT: }
// CHECK-NEXT: ]
-//0x201000 + 4249 + 7 = 0x2020B0
-//0x20100A + 4247 + 7 = 0x2020B8
-//0x201014 + 4237 + 7 = 0x2020B8
+//0x201000 + 4265 + 7 = 0x2020B0
+//0x20100A + 4263 + 7 = 0x2020B8
+//0x201014 + 4253 + 7 = 0x2020B8
//DISASM: Disassembly of section .text:
//DISASM-NEXT: main:
//DISASM-NEXT: 201000: 48 8b 05 a9 10 00 00 movq 4265(%rip), %rax
diff --git a/test/ELF/undef-broken-debug.test b/test/ELF/undef-broken-debug.test
index 1238ebe..4a61bce 100644
--- a/test/ELF/undef-broken-debug.test
+++ b/test/ELF/undef-broken-debug.test
@@ -40,6 +40,27 @@
- Offset: 0x0000000000000029
Symbol: bar
Type: R_X86_64_64
+ - Name: .debug_info
+ Type: SHT_PROGBITS
+ AddressAlign: 0x0000000000000001
+ Content: 0C000000040000000000080100000000
+ - Name: .rela.debug_info
+ Type: SHT_RELA
+ Link: .symtab
+ AddressAlign: 0x0000000000000008
+ Info: .debug_info
+ Relocations:
+ - Offset: 0x0000000000000006
+ Symbol: .debug_abbrev
+ Type: R_X86_64_32
+ - Offset: 0x000000000000000C
+ Symbol: .debug_line
+ Type: R_X86_64_32
+ - Name: .debug_abbrev
+ Type: SHT_PROGBITS
+ AddressAlign: 0x0000000000000001
+ Content: '0111001017000000'
+
Symbols:
Global:
- Name: _start
diff --git a/test/ELF/undef.s b/test/ELF/undef.s
index 49f8410..aabcbbc 100644
--- a/test/ELF/undef.s
+++ b/test/ELF/undef.s
@@ -2,9 +2,10 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef.s -o %t2.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef-debug.s -o %t3.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef-bad-debug.s -o %t4.o
# RUN: llvm-ar rc %t2.a %t2.o
-# RUN: not ld.lld %t.o %t2.a %t3.o -o %t.exe 2>&1 | FileCheck %s
-# RUN: not ld.lld -pie %t.o %t2.a %t3.o -o %t.exe 2>&1 | FileCheck %s
+# RUN: not ld.lld %t.o %t2.a %t3.o %t4.o -o %t.exe 2>&1 | FileCheck %s
+# RUN: not ld.lld -pie %t.o %t2.a %t3.o %t4.o -o %t.exe 2>&1 | FileCheck %s
# CHECK: error: undefined symbol: foo
# CHECK: >>> referenced by undef.s
@@ -33,6 +34,9 @@
# CHECK: >>> referenced by undef-debug.s:11 (dir{{/|\\}}undef-debug.s:11)
# CHECK: >>> {{.*}}.o:(.text.2+0x0)
+# CHECK: error: undefined symbol: zed6
+# CHECK: >>> referenced by {{.*}}tmp4.o:(.text+0x0)
+
# RUN: not ld.lld %t.o %t2.a -o %t.exe -no-demangle 2>&1 | \
# RUN: FileCheck -check-prefix=NO-DEMANGLE %s
# NO-DEMANGLE: error: undefined symbol: _Z3fooi
diff --git a/test/ELF/x86-64-reloc-debug-overflow.s b/test/ELF/x86-64-reloc-debug-overflow.s
new file mode 100644
index 0000000..364c3bf
--- /dev/null
+++ b/test/ELF/x86-64-reloc-debug-overflow.s
@@ -0,0 +1,9 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/x86-64-reloc-error.s -o %tabs
+# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
+# RUN: not ld.lld -shared %tabs %t -o %t2 2>&1 | FileCheck %s
+
+# CHECK: (.debug_info+0x0): relocation R_X86_64_32 out of range: 281474976710656 is not in [0, 4294967295]; consider recompiling with -fdebug-types-section to reduce size of debug sections
+
+.section .debug_info,"",@progbits
+ .long .debug_info + 0x1000000000000
diff --git a/test/ELF/x86-64-retpoline-linkerscript.s b/test/ELF/x86-64-retpoline-linkerscript.s
new file mode 100644
index 0000000..d173cf5
--- /dev/null
+++ b/test/ELF/x86-64-retpoline-linkerscript.s
@@ -0,0 +1,67 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+
+// RUN: echo "SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .plt : { *(.plt) } \
+// RUN: .got.plt : { *(.got.plt) } \
+// RUN: .dynstr : { *(.dynstr) } \
+// RUN: }" > %t.script
+// RUN: ld.lld -shared %t1.o %t2.so -o %t.exe -z retpolineplt --script %t.script
+// RUN: llvm-objdump -d -s %t.exe | FileCheck %s
+
+// CHECK: Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10: ff 35 4a 01 00 00 pushq 330(%rip)
+// CHECK-NEXT: 16: 4c 8b 1d 4b 01 00 00 movq 331(%rip), %r11
+// CHECK-NEXT: 1d: e8 0e 00 00 00 callq 14 <.plt+0x20>
+// CHECK-NEXT: 22: f3 90 pause
+// CHECK-NEXT: 24: 0f ae e8 lfence
+// CHECK-NEXT: 27: eb f9 jmp -7 <.plt+0x12>
+// CHECK-NEXT: 29: cc int3
+// CHECK-NEXT: 2a: cc int3
+// CHECK-NEXT: 2b: cc int3
+// CHECK-NEXT: 2c: cc int3
+// CHECK-NEXT: 2d: cc int3
+// CHECK-NEXT: 2e: cc int3
+// CHECK-NEXT: 2f: cc int3
+// CHECK-NEXT: 30: 4c 89 1c 24 movq %r11, (%rsp)
+// CHECK-NEXT: 34: c3 retq
+// CHECK-NEXT: 35: cc int3
+// CHECK-NEXT: 36: cc int3
+// CHECK-NEXT: 37: cc int3
+// CHECK-NEXT: 38: cc int3
+// CHECK-NEXT: 39: cc int3
+// CHECK-NEXT: 3a: cc int3
+// CHECK-NEXT: 3b: cc int3
+// CHECK-NEXT: 3c: cc int3
+// CHECK-NEXT: 3d: cc int3
+// CHECK-NEXT: 3e: cc int3
+// CHECK-NEXT: 3f: cc int3
+// CHECK-NEXT: 40: 4c 8b 1d 29 01 00 00 movq 297(%rip), %r11
+// CHECK-NEXT: 47: e8 e4 ff ff ff callq -28 <.plt+0x20>
+// CHECK-NEXT: 4c: e9 d1 ff ff ff jmp -47 <.plt+0x12>
+// CHECK-NEXT: 51: 68 00 00 00 00 pushq $0
+// CHECK-NEXT: 56: e9 b5 ff ff ff jmp -75 <.plt>
+// CHECK-NEXT: 5b: cc int3
+// CHECK-NEXT: 5c: cc int3
+// CHECK-NEXT: 5d: cc int3
+// CHECK-NEXT: 5e: cc int3
+// CHECK-NEXT: 5f: cc int3
+// CHECK-NEXT: 60: 4c 8b 1d 11 01 00 00 movq 273(%rip), %r11
+// CHECK-NEXT: 67: e8 c4 ff ff ff callq -60 <.plt+0x20>
+// CHECK-NEXT: 6c: e9 b1 ff ff ff jmp -79 <.plt+0x12>
+// CHECK-NEXT: 71: 68 01 00 00 00 pushq $1
+// CHECK-NEXT: 76: e9 95 ff ff ff jmp -107 <.plt>
+// CHECK-NEXT: 7b: cc int3
+// CHECK-NEXT: 7c: cc int3
+// CHECK-NEXT: 7d: cc int3
+// CHECK-NEXT: 7e: cc int3
+// CHECK-NEXT: 7f: cc int3
+
+.global _start
+_start:
+ jmp bar@PLT
+ jmp zed@PLT
diff --git a/test/ELF/x86-64-retpoline-znow-linkerscript.s b/test/ELF/x86-64-retpoline-znow-linkerscript.s
new file mode 100644
index 0000000..27737b8
--- /dev/null
+++ b/test/ELF/x86-64-retpoline-znow-linkerscript.s
@@ -0,0 +1,54 @@
+// REQUIRES: x86
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o
+// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/shared.s -o %t2.o
+// RUN: ld.lld -shared %t2.o -o %t2.so
+
+// RUN: echo "SECTIONS { \
+// RUN: .text : { *(.text) } \
+// RUN: .plt : { *(.plt) } \
+// RUN: .got.plt : { *(.got.plt) } \
+// RUN: .dynstr : { *(.dynstr) } \
+// RUN: }" > %t.script
+// RUN: ld.lld -shared %t1.o %t2.so -o %t.exe -z retpolineplt -z now --script %t.script
+// RUN: llvm-objdump -d -s %t.exe | FileCheck %s
+
+// CHECK: Disassembly of section .plt:
+// CHECK-NEXT: .plt:
+// CHECK-NEXT: 10: e8 0b 00 00 00 callq 11 <.plt+0x10>
+// CHECK-NEXT: 15: f3 90 pause
+// CHECK-NEXT: 17: 0f ae e8 lfence
+// CHECK-NEXT: 1a: eb f9 jmp -7 <.plt+0x5>
+// CHECK-NEXT: 1c: cc int3
+// CHECK-NEXT: 1d: cc int3
+// CHECK-NEXT: 1e: cc int3
+// CHECK-NEXT: 1f: cc int3
+// CHECK-NEXT: 20: 4c 89 1c 24 movq %r11, (%rsp)
+// CHECK-NEXT: 24: c3 retq
+// CHECK-NEXT: 25: cc int3
+// CHECK-NEXT: 26: cc int3
+// CHECK-NEXT: 27: cc int3
+// CHECK-NEXT: 28: cc int3
+// CHECK-NEXT: 29: cc int3
+// CHECK-NEXT: 2a: cc int3
+// CHECK-NEXT: 2b: cc int3
+// CHECK-NEXT: 2c: cc int3
+// CHECK-NEXT: 2d: cc int3
+// CHECK-NEXT: 2e: cc int3
+// CHECK-NEXT: 2f: cc int3
+// CHECK-NEXT: 30: 4c 8b 1d 09 01 00 00 movq 265(%rip), %r11
+// CHECK-NEXT: 37: e9 d4 ff ff ff jmp -44 <.plt>
+// CHECK-NEXT: 3c: cc int3
+// CHECK-NEXT: 3d: cc int3
+// CHECK-NEXT: 3e: cc int3
+// CHECK-NEXT: 3f: cc int3
+// CHECK-NEXT: 40: 4c 8b 1d 01 01 00 00 movq 257(%rip), %r11
+// CHECK-NEXT: 47: e9 c4 ff ff ff jmp -60 <.plt>
+// CHECK-NEXT: 4c: cc int3
+// CHECK-NEXT: 4d: cc int3
+// CHECK-NEXT: 4e: cc int3
+// CHECK-NEXT: 4f: cc int3
+
+.global _start
+_start:
+ jmp bar@PLT
+ jmp zed@PLT
diff --git a/test/MinGW/driver.test b/test/MinGW/driver.test
index b0b5f56..1a95d23 100644
--- a/test/MinGW/driver.test
+++ b/test/MinGW/driver.test
@@ -126,3 +126,7 @@
ICF: -opt:icf
RUN: ld.lld -### -m i386pep --start-group foo.o --end-group
+
+RUN: ld.lld -### foo.o -m i386pe -shared --kill-at | FileCheck -check-prefix=KILL-AT %s
+RUN: ld.lld -### foo.o -m i386pe -shared -kill-at | FileCheck -check-prefix=KILL-AT %s
+KILL-AT: -kill-at
diff --git a/test/lit.cfg.py b/test/lit.cfg.py
index 05ebc23..8fd1a60 100644
--- a/test/lit.cfg.py
+++ b/test/lit.cfg.py
@@ -71,7 +71,7 @@
'X86': 'x86'})
])
-# Set a fake constant version so that we get consitent output.
+# Set a fake constant version so that we get consistent output.
config.environment['LLD_VERSION'] = 'LLD 1.0'
config.environment['LLD_IN_TEST'] = '1'
@@ -85,6 +85,9 @@
if (config.llvm_libxml2_enabled == '1'):
config.available_features.add('libxml2')
+if config.have_dia_sdk:
+ config.available_features.add("diasdk")
+
tar_executable = lit.util.which('tar', config.environment['PATH'])
if tar_executable:
tar_version = subprocess.Popen(
diff --git a/test/lit.site.cfg.py.in b/test/lit.site.cfg.py.in
index 50593f7..764ab83 100644
--- a/test/lit.site.cfg.py.in
+++ b/test/lit.site.cfg.py.in
@@ -1,5 +1,8 @@
@LIT_SITE_CFG_IN_HEADER@
+import lit.util
+
+config.have_dia_sdk = lit.util.pythonize_bool("@LLVM_ENABLE_DIA_SDK@")
config.llvm_src_root = "@LLVM_SOURCE_DIR@"
config.llvm_obj_root = "@LLVM_BINARY_DIR@"
config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
diff --git a/test/wasm/alias.ll b/test/wasm/alias.ll
index e5b5776..c12ef2d 100644
--- a/test/wasm/alias.ll
+++ b/test/wasm/alias.ll
@@ -66,10 +66,10 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: start_alias
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
@@ -82,7 +82,7 @@
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
; CHECK-NEXT: ...
diff --git a/test/wasm/call-indirect.ll b/test/wasm/call-indirect.ll
index c4fa355..9a6d64f 100644
--- a/test/wasm/call-indirect.ll
+++ b/test/wasm/call-indirect.ll
@@ -57,7 +57,7 @@
; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - I32
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 3, 1, 3, 4, 3 ]
+; CHECK-NEXT: FunctionTypes: [ 3, 0, 3, 1, 3, 4 ]
; CHECK-NEXT: - Type: TABLE
; CHECK-NEXT: Tables:
; CHECK-NEXT: - ElemType: ANYFUNC
@@ -107,50 +107,50 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 3
+; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Name: bar
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: call_bar_indirect
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: foo
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: indirect_func
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: call_ptr
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 4
+; CHECK-NEXT: Index: 5
; CHECK-NEXT: - Type: ELEM
; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1
-; CHECK-NEXT: Functions: [ 0, 2 ]
+; CHECK-NEXT: Functions: [ 1, 3 ]
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 42010B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 42010B
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Locals:
; CHECK-NEXT: - Type: I32
; CHECK-NEXT: Count: 1
; CHECK-NEXT: Body: 4100280284888080002100410028028088808000118080808000001A2000118180808000001A0B
-; CHECK-NEXT: - Index: 2
-; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41020B
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 410028028888808000118180808000001A0B
+; CHECK-NEXT: Body: 41020B
; CHECK-NEXT: - Index: 4
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 42012000118280808000001A0B
+; CHECK-NEXT: Body: 410028028888808000118180808000001A0B
; CHECK-NEXT: - Index: 5
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: Body: 42012000118280808000001A0B
; CHECK-NEXT: - Type: DATA
; CHECK-NEXT: Segments:
; CHECK-NEXT: - SectionOffset: 7
@@ -163,10 +163,15 @@
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: bar
+; CHECK-NEXT: Name: __wasm_call_ctors
; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: call_bar_indirect
+; CHECK-NEXT: Name: bar
; CHECK-NEXT: - Index: 2
-; CHECK-NEXT: Name: foo
+; CHECK-NEXT: Name: call_bar_indirect
; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: foo
+; CHECK-NEXT: - Index: 4
; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 5
+; CHECK-NEXT: Name: call_ptr
+; CHECK-NEXT: ...
diff --git a/test/wasm/comdats.ll b/test/wasm/comdats.ll
index 65e74db..d737132 100644
--- a/test/wasm/comdats.ll
+++ b/test/wasm/comdats.ll
@@ -53,42 +53,42 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: inlineFn
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: constantData
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: callInline1
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: callInline2
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 3
+; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Type: ELEM
; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1
-; CHECK-NEXT: Functions: [ 1 ]
+; CHECK-NEXT: Functions: [ 2 ]
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1081808080001A0B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4180888080000B
+; CHECK-NEXT: Body: 1082808080001A0B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4181808080000B
+; CHECK-NEXT: Body: 4180888080000B
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 4181808080000B
; CHECK-NEXT: - Index: 4
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: Body: 4181808080000B
; CHECK-NEXT: - Type: DATA
; CHECK-NEXT: Segments:
; CHECK-NEXT: - SectionOffset: 7
diff --git a/test/wasm/cxx-mangling.ll b/test/wasm/cxx-mangling.ll
new file mode 100644
index 0000000..8b73ca9
--- /dev/null
+++ b/test/wasm/cxx-mangling.ll
@@ -0,0 +1,66 @@
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: wasm-ld --demangle --check-signatures -o %t_demangle.wasm %t.o
+; RUN: obj2yaml %t_demangle.wasm | FileCheck %s
+; RUN: wasm-ld --no-demangle --check-signatures -o %t_nodemangle.wasm %t.o
+; RUN: obj2yaml %t_nodemangle.wasm | FileCheck %s
+
+target triple = "wasm32-unknown-unknown-wasm"
+
+; Check that the EXPORT name is still mangled, but that the "name" custom
+; section contains the unmangled name.
+
+define void @_Z3fooi(i32 %arg) {
+ ret void
+}
+
+declare extern_weak void @_Z3bari(i32 %arg)
+
+define void @_start() {
+ call void @_Z3fooi(i32 1)
+ call void @_Z3bari(i32 1)
+ ret void
+}
+
+; CHECK: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: __heap_base
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: __data_end
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: _start
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 3
+; CHECK-NEXT: - Name: _Z3fooi
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 000B
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 410110828080800041011081808080000B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: 'undefined function bar(int)'
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: 'foo(int)'
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: ...
diff --git a/test/wasm/data-layout.ll b/test/wasm/data-layout.ll
index e1e64b3..ece5f20 100644
--- a/test/wasm/data-layout.ll
+++ b/test/wasm/data-layout.ll
@@ -9,10 +9,17 @@
@hello_str = external global i8*
@external_ref = global i8** @hello_str, align 8
+%struct.s = type { i32, i32 }
+@local_struct = hidden global %struct.s zeroinitializer, align 4
+@local_struct_internal_ptr = hidden local_unnamed_addr global i32* getelementptr inbounds (%struct.s, %struct.s* @local_struct, i32 0, i32 1), align 4
+
; RUN: wasm-ld -no-gc-sections --check-signatures --allow-undefined -o %t.wasm %t.o %t.hello.o
; RUN: obj2yaml %t.wasm | FileCheck %s
-; CHECK: - Type: GLOBAL
+; CHECK: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
; CHECK-NEXT: Globals:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Type: I32
@@ -34,13 +41,32 @@
; CHECK-NEXT: Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1024
-; CHECK-NEXT: Content: 0100000000000000000000000000000003000000000000001C040000
-; CHECK-NEXT: - SectionOffset: 41
+; CHECK-NEXT: Content: '0100000000000000000000000000000003000000000000002804000024040000'
+; CHECK-NEXT: - SectionOffset: 45
; CHECK-NEXT: MemoryIndex: 0
; CHECK-NEXT: Offset:
; CHECK-NEXT: Opcode: I32_CONST
-; CHECK-NEXT: Value: 1052
+; CHECK-NEXT: Value: 1056
+; CHECK-NEXT: Content: '0000000000000000'
+; CHECK-NEXT: - SectionOffset: 59
+; CHECK-NEXT: MemoryIndex: 0
+; CHECK-NEXT: Offset:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1064
; CHECK-NEXT: Content: 68656C6C6F0A00
+; CHECK-NEXT: - Type: CUSTOM
+
+
+; RUN: wasm-ld -no-gc-sections --check-signatures --allow-undefined \
+; RUN: --initial-memory=131072 --max-memory=131072 -o %t_max.wasm %t.o \
+; RUN: %t.hello.o
+; RUN: obj2yaml %t_max.wasm | FileCheck %s -check-prefix=CHECK-MAX
+
+; CHECK-MAX: - Type: MEMORY
+; CHECK-MAX-NEXT: Memories:
+; CHECK-MAX-NEXT: - Flags: [ HAS_MAX ]
+; CHECK-MAX-NEXT: Initial: 0x00000002
+; CHECK-MAX-NEXT: Maximum: 0x00000002
; RUN: wasm-ld --check-signatures --relocatable -o %t_reloc.wasm %t.o %t.hello.o
@@ -49,8 +75,12 @@
; RELOC: - Type: DATA
; RELOC-NEXT: Relocations:
; RELOC-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32
-; RELOC-NEXT: Index: 4
+; RELOC-NEXT: Index: 6
; RELOC-NEXT: Offset: 0x00000018
+; RELOC-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32
+; RELOC-NEXT: Index: 3
+; RELOC-NEXT: Offset: 0x0000002E
+; RELOC-NEXT: Addend: 4
; RELOC-NEXT: Segments:
; RELOC-NEXT: - SectionOffset: 6
; RELOC-NEXT: MemoryIndex: 0
@@ -69,12 +99,24 @@
; RELOC-NEXT: Offset:
; RELOC-NEXT: Opcode: I32_CONST
; RELOC-NEXT: Value: 24
-; RELOC-NEXT: Content: 1C000000
+; RELOC-NEXT: Content: '28000000'
; RELOC-NEXT: - SectionOffset: 33
; RELOC-NEXT: MemoryIndex: 0
+; RELOC-NEXT: Offset:
+; RELOC-NEXT: Opcode: I32_CONST
+; RELOC-NEXT: Value: 28
+; RELOC-NEXT: Content: '0000000000000000'
+; RELOC-NEXT: - SectionOffset: 46
+; RELOC-NEXT: MemoryIndex: 0
+; RELOC-NEXT: Offset:
+; RELOC-NEXT: Opcode: I32_CONST
+; RELOC-NEXT: Value: 36
+; RELOC-NEXT: Content: '20000000'
+; RELOC-NEXT: - SectionOffset: 55
+; RELOC-NEXT: MemoryIndex: 0
; RELOC-NEXT: Offset:
; RELOC-NEXT: Opcode: I32_CONST
-; RELOC-NEXT: Value: 28
+; RELOC-NEXT: Value: 40
; RELOC-NEXT: Content: 68656C6C6F0A00
; RELOC: - Type: CUSTOM
@@ -98,9 +140,9 @@
; RELOC-NEXT: Flags: [ ]
; RELOC-NEXT: Segment: 2
; RELOC-NEXT: Size: 4
-; RELOC: - Index: 4
+; RELOC: - Index: 6
; RELOC-NEXT: Kind: DATA
; RELOC-NEXT: Name: hello_str
; RELOC-NEXT: Flags: [ ]
-; RELOC-NEXT: Segment: 3
+; RELOC-NEXT: Segment: 5
; RELOC-NEXT: Size: 7
diff --git a/test/wasm/demangle.ll b/test/wasm/demangle.ll
new file mode 100644
index 0000000..07f9927
--- /dev/null
+++ b/test/wasm/demangle.ll
@@ -0,0 +1,17 @@
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: not wasm-ld --check-signatures --undefined _Z3fooi \
+; RUN: -o %t.wasm %t.o 2>&1 | FileCheck %s
+
+; CHECK: error: undefined symbol: foo(int)
+
+; RUN: not wasm-ld --check-signatures --no-demangle --undefined _Z3fooi \
+; RUN: -o %t.wasm %t.o 2>&1 | FileCheck -check-prefix=CHECK-NODEMANGLE %s
+
+; CHECK-NODEMANGLE: error: undefined symbol: _Z3fooi
+
+target triple = "wasm32-unknown-unknown-wasm"
+
+define hidden void @_start() local_unnamed_addr {
+entry:
+ ret void
+}
diff --git a/test/wasm/driver.ll b/test/wasm/driver.ll
index 10a8eca..7222cb5 100644
--- a/test/wasm/driver.ll
+++ b/test/wasm/driver.ll
@@ -16,3 +16,7 @@
; RUN: not wasm-ld 2>&1 | FileCheck -check-prefix=BOTH %s
; BOTH: error: no input files
; BOTH-NOT: error: no output file specified
+
+; RUN: not wasm-ld --export-table --import-table %t.o 2>&1 \
+; RUN: | FileCheck -check-prefix=TABLE %s
+; TABLE: error: --import-table and --export-table may not be used together
diff --git a/test/wasm/entry.ll b/test/wasm/entry.ll
index 058a782..083bf97 100644
--- a/test/wasm/entry.ll
+++ b/test/wasm/entry.ll
@@ -25,7 +25,7 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: entry
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Type:
; The __wasm_call_ctors is somewhat special. Make sure we can use it
diff --git a/test/wasm/export-table.test b/test/wasm/export-table.test
new file mode 100644
index 0000000..0923af6
--- /dev/null
+++ b/test/wasm/export-table.test
@@ -0,0 +1,19 @@
+# RUN: llc -filetype=obj %p/Inputs/start.ll -o %t.start.o
+# RUN: wasm-ld --check-signatures --export-table -o %t.wasm %t.start.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+# Verify the --export-table flag creates a table export
+
+# CHECK: - Type: TABLE
+# CHECK-NEXT: Tables:
+# CHECK-NEXT: - ElemType: ANYFUNC
+# CHECK-NEXT: Limits:
+# CHECK-NEXT: Flags: [ HAS_MAX ]
+# CHECK-NEXT: Initial: 0x00000001
+# CHECK-NEXT: Maximum: 0x00000001
+# CHECK-NEXT: - Type:
+# CHECK: - Type: EXPORT
+# CHECK-NEXT: Exports:
+# CHECK: - Name: __indirect_function_table
+# CHECK-NEXT: Kind: TABLE
+# CHECK-NEXT: Index: 0
diff --git a/test/wasm/export.ll b/test/wasm/export.ll
index 05f2b4f..f2a4fff 100644
--- a/test/wasm/export.ll
+++ b/test/wasm/export.ll
@@ -30,8 +30,8 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: hidden_function
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Type: CODE
diff --git a/test/wasm/function-imports-first.ll b/test/wasm/function-imports-first.ll
index 876d4d1..be71d7c 100644
--- a/test/wasm/function-imports-first.ll
+++ b/test/wasm/function-imports-first.ll
@@ -24,25 +24,25 @@
; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - F32
; CHECK: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 1, 0 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 1 ]
; CHECK: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 43000000001081808080001A0B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: Body: 43000000001082808080001A0B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: Body: 41000B
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: ret32
-; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: ret32
; CHECK-NEXT: ...
diff --git a/test/wasm/function-imports.ll b/test/wasm/function-imports.ll
index a435cdf..742cec2 100644
--- a/test/wasm/function-imports.ll
+++ b/test/wasm/function-imports.ll
@@ -18,14 +18,14 @@
; CHECK: - Type: TYPE
; CHECK-NEXT: Signatures:
; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
; CHECK-NEXT: ReturnType: I32
; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - F32
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: ReturnType: NORESULT
-; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 1, 1 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 0 ]
; CHECK: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK: - Index: 0
@@ -34,9 +34,9 @@
; CHECK: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: ret32
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: ret32
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: _start
; CHECK-NEXT: ...
diff --git a/test/wasm/gc-sections.ll b/test/wasm/gc-sections.ll
index d3b3731..dec455e 100644
--- a/test/wasm/gc-sections.ll
+++ b/test/wasm/gc-sections.ll
@@ -31,10 +31,10 @@
; CHECK: - Type: TYPE
; CHECK-NEXT: Signatures:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ReturnType: NORESULT
; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ReturnType: I32
; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
@@ -48,13 +48,13 @@
; CHECK-NEXT: Content: '02000000'
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: name
-; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: used_function
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: used_function
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: _start
; CHECK-NEXT: ...
; RUN: wasm-ld -print-gc-sections --no-gc-sections -o %t1.no-gc.wasm %t.o
@@ -63,13 +63,13 @@
; NO-GC: - Type: TYPE
; NO-GC-NEXT: Signatures:
; NO-GC-NEXT: - Index: 0
+; NO-GC-NEXT: ReturnType: NORESULT
+; NO-GC-NEXT: ParamTypes:
+; NO-GC-NEXT: - Index: 1
; NO-GC-NEXT: ReturnType: I64
; NO-GC-NEXT: ParamTypes:
-; NO-GC-NEXT: - Index: 1
-; NO-GC-NEXT: ReturnType: I32
-; NO-GC-NEXT: ParamTypes:
; NO-GC-NEXT: - Index: 2
-; NO-GC-NEXT: ReturnType: NORESULT
+; NO-GC-NEXT: ReturnType: I32
; NO-GC-NEXT: ParamTypes:
; NO-GC-NEXT: - Type: FUNCTION
@@ -83,16 +83,16 @@
; NO-GC-NEXT: Content: '010000000000000002000000'
; NO-GC-NEXT: - Type: CUSTOM
; NO-GC-NEXT: Name: name
-; NO-GC-NEXT: FunctionNames:
+; NO-GC-NEXT: FunctionNames:
; NO-GC-NEXT: - Index: 0
-; NO-GC-NEXT: Name: unused_function
-; NO-GC-NEXT: - Index: 1
-; NO-GC-NEXT: Name: used_function
-; NO-GC-NEXT: - Index: 2
-; NO-GC-NEXT: Name: _start
-; NO-GC-NEXT: - Index: 3
; NO-GC-NEXT: Name: __wasm_call_ctors
+; NO-GC-NEXT: - Index: 1
+; NO-GC-NEXT: Name: unused_function
+; NO-GC-NEXT: - Index: 2
+; NO-GC-NEXT: Name: used_function
+; NO-GC-NEXT: - Index: 3
+; NO-GC-NEXT: Name: _start
; NO-GC-NEXT: ...
; RUN: not wasm-ld --gc-sections --relocatable -o %t1.no-gc.wasm %t.o 2>&1 | FileCheck %s -check-prefix=CHECK-ERROR
-; CHECK-ERROR: wasm-ld: error: -r and --gc-sections may not be used together
+; CHECK-ERROR: error: -r and --gc-sections may not be used together
diff --git a/test/wasm/import-memory.test b/test/wasm/import-memory.test
index ab38c7a..955d2df 100644
--- a/test/wasm/import-memory.test
+++ b/test/wasm/import-memory.test
@@ -11,3 +11,23 @@
# CHECK-NEXT: Kind: MEMORY
# CHECK-NEXT: Memory:
# CHECK-NEXT: Initial: 0x00000002
+# CHECK-NEXT: - Type:
+
+
+
+# RUN: wasm-ld --check-signatures --import-memory --initial-memory=262144 \
+# RUN: --max-memory=327680 -o %t.max.wasm %t.start.o
+# RUN: obj2yaml %t.max.wasm | FileCheck -check-prefix=CHECK-MAX %s
+
+# Verify the --initial-memory and --max-memory arguments work with imports
+
+# CHECK-MAX: - Type: IMPORT
+# CHECK-MAX-NEXT: Imports:
+# CHECK-MAX-NEXT: - Module: env
+# CHECK-MAX-NEXT: Field: memory
+# CHECK-MAX-NEXT: Kind: MEMORY
+# CHECK-MAX-NEXT: Memory:
+# CHECK-MAX-NEXT: Flags: [ HAS_MAX ]
+# CHECK-MAX-NEXT: Initial: 0x00000004
+# CHECK-MAX-NEXT: Maximum: 0x00000005
+# CHECK-MAX-NEXT: - Type:
diff --git a/test/wasm/import-table.test b/test/wasm/import-table.test
new file mode 100644
index 0000000..98e0749
--- /dev/null
+++ b/test/wasm/import-table.test
@@ -0,0 +1,18 @@
+# RUN: llc -filetype=obj %p/Inputs/start.ll -o %t.start.o
+# RUN: wasm-ld --check-signatures --import-table -o %t.wasm %t.start.o
+# RUN: obj2yaml %t.wasm | FileCheck %s
+
+# Verify the --import-table flag creates a table import
+
+# CHECK: - Type: IMPORT
+# CHECK-NEXT: Imports:
+# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: __indirect_function_table
+# CHECK-NEXT: Kind: TABLE
+# CHECK-NEXT: Table:
+# CHECK-NEXT: ElemType: ANYFUNC
+# CHECK-NEXT: Limits:
+# CHECK-NEXT: Flags: [ HAS_MAX ]
+# CHECK-NEXT: Initial: 0x00000001
+# CHECK-NEXT: Maximum: 0x00000001
+
diff --git a/test/wasm/init-fini.ll b/test/wasm/init-fini.ll
index 7594377..1ca2008 100644
--- a/test/wasm/init-fini.ll
+++ b/test/wasm/init-fini.ll
@@ -23,6 +23,9 @@
ret void
}
+declare hidden void @externCtor()
+declare hidden void @externDtor()
+
define i32 @__cxa_atexit(i32 %func, i32 %arg, i32 %dso_handle) {
ret i32 0
}
@@ -32,70 +35,96 @@
ret void
}
-@llvm.global_ctors = appending global [3 x { i32, void ()*, i8* }] [
+@llvm.global_ctors = appending global [4 x { i32, void ()*, i8* }] [
{ i32, void ()*, i8* } { i32 1001, void ()* @func1, i8* null },
{ i32, void ()*, i8* } { i32 101, void ()* @func1, i8* null },
- { i32, void ()*, i8* } { i32 101, void ()* @func2, i8* null }
+ { i32, void ()*, i8* } { i32 101, void ()* @func2, i8* null },
+ { i32, void ()*, i8* } { i32 4000, void ()* @externCtor, i8* null }
]
-@llvm.global_dtors = appending global [3 x { i32, void ()*, i8* }] [
+@llvm.global_dtors = appending global [4 x { i32, void ()*, i8* }] [
{ i32, void ()*, i8* } { i32 1001, void ()* @func3, i8* null },
{ i32, void ()*, i8* } { i32 101, void ()* @func3, i8* null },
- { i32, void ()*, i8* } { i32 101, void ()* @func4, i8* null }
+ { i32, void ()*, i8* } { i32 101, void ()* @func4, i8* null },
+ { i32, void ()*, i8* } { i32 4000, void ()* @externDtor, i8* null }
]
-; RUN: wasm-ld --check-signatures %t.o %t.global-ctor-dtor.o -o %t.wasm
+; RUN: wasm-ld --check-signatures --allow-undefined %t.o %t.global-ctor-dtor.o -o %t.wasm
; RUN: obj2yaml %t.wasm | FileCheck %s
+; CHECK: - Type: IMPORT
+; CHECK-NEXT: Imports:
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: externDtor
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: SigIndex: 0
+; CHECK-NEXT: - Module: env
+; CHECK-NEXT: Field: externCtor
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: SigIndex: 0
; CHECK: - Type: ELEM
; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1
-; CHECK-NEXT: Functions: [ 6, 8, 12, 14, 16 ]
-
-; CHECK: Body: 100010011007100A100D100A100F10001009100A10110B
+; CHECK-NEXT: Functions: [ 9, 11, 13, 17, 19, 21 ]
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 10031004100A100F1012100F10141003100C100F10161001100E0B
+; CHECK: - Index: 22
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 024041868080800041004180888080001087808080000D000F0B00000B
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: func1
+; CHECK-NEXT: Name: externDtor
; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: func2
+; CHECK-NEXT: Name: externCtor
; CHECK-NEXT: - Index: 2
-; CHECK-NEXT: Name: func3
-; CHECK-NEXT: - Index: 3
-; CHECK-NEXT: Name: func4
-; CHECK-NEXT: - Index: 4
-; CHECK-NEXT: Name: __cxa_atexit
-; CHECK-NEXT: - Index: 5
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 6
-; CHECK-NEXT: Name: .Lcall_dtors.101
-; CHECK-NEXT: - Index: 7
-; CHECK-NEXT: Name: .Lregister_call_dtors.101
-; CHECK-NEXT: - Index: 8
-; CHECK-NEXT: Name: .Lcall_dtors.1001
-; CHECK-NEXT: - Index: 9
-; CHECK-NEXT: Name: .Lregister_call_dtors.1001
-; CHECK-NEXT: - Index: 10
-; CHECK-NEXT: Name: myctor
-; CHECK-NEXT: - Index: 11
-; CHECK-NEXT: Name: mydtor
-; CHECK-NEXT: - Index: 12
-; CHECK-NEXT: Name: .Lcall_dtors.101
-; CHECK-NEXT: - Index: 13
-; CHECK-NEXT: Name: .Lregister_call_dtors.101
-; CHECK-NEXT: - Index: 14
-; CHECK-NEXT: Name: .Lcall_dtors.202
-; CHECK-NEXT: - Index: 15
-; CHECK-NEXT: Name: .Lregister_call_dtors.202
-; CHECK-NEXT: - Index: 16
-; CHECK-NEXT: Name: .Lcall_dtors.2002
-; CHECK-NEXT: - Index: 17
-; CHECK-NEXT: Name: .Lregister_call_dtors.2002
-; CHECK-NEXT: - Index: 18
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: func1
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Name: func2
+; CHECK-NEXT: - Index: 5
+; CHECK-NEXT: Name: func3
+; CHECK-NEXT: - Index: 6
+; CHECK-NEXT: Name: func4
+; CHECK-NEXT: - Index: 7
+; CHECK-NEXT: Name: __cxa_atexit
+; CHECK-NEXT: - Index: 8
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 9
+; CHECK-NEXT: Name: .Lcall_dtors.101
+; CHECK-NEXT: - Index: 10
+; CHECK-NEXT: Name: .Lregister_call_dtors.101
+; CHECK-NEXT: - Index: 11
+; CHECK-NEXT: Name: .Lcall_dtors.1001
+; CHECK-NEXT: - Index: 12
+; CHECK-NEXT: Name: .Lregister_call_dtors.1001
+; CHECK-NEXT: - Index: 13
+; CHECK-NEXT: Name: .Lcall_dtors.4000
+; CHECK-NEXT: - Index: 14
+; CHECK-NEXT: Name: .Lregister_call_dtors.4000
+; CHECK-NEXT: - Index: 15
+; CHECK-NEXT: Name: myctor
+; CHECK-NEXT: - Index: 16
+; CHECK-NEXT: Name: mydtor
+; CHECK-NEXT: - Index: 17
+; CHECK-NEXT: Name: .Lcall_dtors.101
+; CHECK-NEXT: - Index: 18
+; CHECK-NEXT: Name: .Lregister_call_dtors.101
+; CHECK-NEXT: - Index: 19
+; CHECK-NEXT: Name: .Lcall_dtors.202
+; CHECK-NEXT: - Index: 20
+; CHECK-NEXT: Name: .Lregister_call_dtors.202
+; CHECK-NEXT: - Index: 21
+; CHECK-NEXT: Name: .Lcall_dtors.2002
+; CHECK-NEXT: - Index: 22
+; CHECK-NEXT: Name: .Lregister_call_dtors.2002
; CHECK-NEXT: ...
@@ -108,42 +137,42 @@
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: func1
; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
-; RELOC-NEXT: Function: 0
+; RELOC-NEXT: Function: 2
; RELOC-NEXT: - Index: 1
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: func2
; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
-; RELOC-NEXT: Function: 1
+; RELOC-NEXT: Function: 3
; RELOC-NEXT: - Index: 2
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: func3
; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
-; RELOC-NEXT: Function: 2
+; RELOC-NEXT: Function: 4
; RELOC-NEXT: - Index: 3
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: func4
; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
-; RELOC-NEXT: Function: 3
+; RELOC-NEXT: Function: 5
; RELOC-NEXT: - Index: 4
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: __cxa_atexit
; RELOC-NEXT: Flags: [ ]
-; RELOC-NEXT: Function: 4
+; RELOC-NEXT: Function: 6
; RELOC-NEXT: - Index: 5
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: _start
; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
-; RELOC-NEXT: Function: 5
+; RELOC-NEXT: Function: 7
; RELOC-NEXT: - Index: 6
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: .Lcall_dtors.101
; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
-; RELOC-NEXT: Function: 6
+; RELOC-NEXT: Function: 8
; RELOC-NEXT: - Index: 7
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: .Lregister_call_dtors.101
; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
-; RELOC-NEXT: Function: 7
+; RELOC-NEXT: Function: 9
; RELOC-NEXT: - Index: 8
; RELOC-NEXT: Kind: DATA
; RELOC-NEXT: Name: __dso_handle
@@ -152,57 +181,77 @@
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: .Lcall_dtors.1001
; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
-; RELOC-NEXT: Function: 8
+; RELOC-NEXT: Function: 10
; RELOC-NEXT: - Index: 10
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: .Lregister_call_dtors.1001
; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
-; RELOC-NEXT: Function: 9
+; RELOC-NEXT: Function: 11
; RELOC-NEXT: - Index: 11
; RELOC-NEXT: Kind: FUNCTION
+; RELOC-NEXT: Name: .Lcall_dtors.4000
+; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
+; RELOC-NEXT: Function: 12
+; RELOC-NEXT: - Index: 12
+; RELOC-NEXT: Kind: FUNCTION
+; RELOC-NEXT: Name: externDtor
+; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN, UNDEFINED ]
+; RELOC-NEXT: Function: 0
+; RELOC-NEXT: - Index: 13
+; RELOC-NEXT: Kind: FUNCTION
+; RELOC-NEXT: Name: .Lregister_call_dtors.4000
+; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
+; RELOC-NEXT: Function: 13
+; RELOC-NEXT: - Index: 14
+; RELOC-NEXT: Kind: FUNCTION
+; RELOC-NEXT: Name: externCtor
+; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN, UNDEFINED ]
+; RELOC-NEXT: Function: 1
+; RELOC-NEXT: - Index: 15
+; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: myctor
; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
-; RELOC-NEXT: Function: 10
-; RELOC-NEXT: - Index: 12
+; RELOC-NEXT: Function: 14
+; RELOC-NEXT: - Index: 16
; RELOC-NEXT: Kind: FUNCTION
; RELOC-NEXT: Name: mydtor
; RELOC-NEXT: Flags: [ VISIBILITY_HIDDEN ]
-; RELOC-NEXT: Function: 11
-; RELOC-NEXT: - Index: 13
+; RELOC-NEXT: Function: 15
+; RELOC-NEXT: - Index: 17
; RELOC-NEXT: Kind: GLOBAL
; RELOC-NEXT: Name: __stack_pointer
; RELOC-NEXT: Flags: [ UNDEFINED ]
; RELOC-NEXT: Global: 0
-; RELOC-NEXT: - Index: 14
-; RELOC-NEXT: Kind: FUNCTION
-; RELOC-NEXT: Name: .Lcall_dtors.101
-; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
-; RELOC-NEXT: Function: 12
-; RELOC-NEXT: - Index: 15
-; RELOC-NEXT: Kind: FUNCTION
-; RELOC-NEXT: Name: .Lregister_call_dtors.101
-; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
-; RELOC-NEXT: Function: 13
-; RELOC-NEXT: - Index: 16
-; RELOC-NEXT: Kind: FUNCTION
-; RELOC-NEXT: Name: .Lcall_dtors.202
-; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
-; RELOC-NEXT: Function: 14
-; RELOC-NEXT: - Index: 17
-; RELOC-NEXT: Kind: FUNCTION
-; RELOC-NEXT: Name: .Lregister_call_dtors.202
-; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
-; RELOC-NEXT: Function: 15
; RELOC-NEXT: - Index: 18
; RELOC-NEXT: Kind: FUNCTION
-; RELOC-NEXT: Name: .Lcall_dtors.2002
+; RELOC-NEXT: Name: .Lcall_dtors.101
; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
; RELOC-NEXT: Function: 16
; RELOC-NEXT: - Index: 19
; RELOC-NEXT: Kind: FUNCTION
-; RELOC-NEXT: Name: .Lregister_call_dtors.2002
+; RELOC-NEXT: Name: .Lregister_call_dtors.101
; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
; RELOC-NEXT: Function: 17
+; RELOC-NEXT: - Index: 20
+; RELOC-NEXT: Kind: FUNCTION
+; RELOC-NEXT: Name: .Lcall_dtors.202
+; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
+; RELOC-NEXT: Function: 18
+; RELOC-NEXT: - Index: 21
+; RELOC-NEXT: Kind: FUNCTION
+; RELOC-NEXT: Name: .Lregister_call_dtors.202
+; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
+; RELOC-NEXT: Function: 19
+; RELOC-NEXT: - Index: 22
+; RELOC-NEXT: Kind: FUNCTION
+; RELOC-NEXT: Name: .Lcall_dtors.2002
+; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
+; RELOC-NEXT: Function: 20
+; RELOC-NEXT: - Index: 23
+; RELOC-NEXT: Kind: FUNCTION
+; RELOC-NEXT: Name: .Lregister_call_dtors.2002
+; RELOC-NEXT: Flags: [ BINDING_LOCAL ]
+; RELOC-NEXT: Function: 21
; RELOC-NEXT: InitFunctions:
; RELOC-NEXT: - Priority: 101
; RELOC-NEXT: Symbol: 0
@@ -211,58 +260,70 @@
; RELOC-NEXT: - Priority: 101
; RELOC-NEXT: Symbol: 7
; RELOC-NEXT: - Priority: 101
-; RELOC-NEXT: Symbol: 11
+; RELOC-NEXT: Symbol: 15
; RELOC-NEXT: - Priority: 101
+; RELOC-NEXT: Symbol: 19
+; RELOC-NEXT: - Priority: 202
; RELOC-NEXT: Symbol: 15
; RELOC-NEXT: - Priority: 202
-; RELOC-NEXT: Symbol: 11
-; RELOC-NEXT: - Priority: 202
-; RELOC-NEXT: Symbol: 17
+; RELOC-NEXT: Symbol: 21
; RELOC-NEXT: - Priority: 1001
; RELOC-NEXT: Symbol: 0
; RELOC-NEXT: - Priority: 1001
; RELOC-NEXT: Symbol: 10
; RELOC-NEXT: - Priority: 2002
-; RELOC-NEXT: Symbol: 11
+; RELOC-NEXT: Symbol: 15
; RELOC-NEXT: - Priority: 2002
-; RELOC-NEXT: Symbol: 19
+; RELOC-NEXT: Symbol: 23
+; RELOC-NEXT: - Priority: 4000
+; RELOC-NEXT: Symbol: 14
+; RELOC-NEXT: - Priority: 4000
+; RELOC-NEXT: Symbol: 13
; RELOC-NEXT: - Type: CUSTOM
; RELOC-NEXT: Name: name
; RELOC-NEXT: FunctionNames:
; RELOC-NEXT: - Index: 0
-; RELOC-NEXT: Name: func1
+; RELOC-NEXT: Name: externDtor
; RELOC-NEXT: - Index: 1
-; RELOC-NEXT: Name: func2
+; RELOC-NEXT: Name: externCtor
; RELOC-NEXT: - Index: 2
-; RELOC-NEXT: Name: func3
+; RELOC-NEXT: Name: func1
; RELOC-NEXT: - Index: 3
-; RELOC-NEXT: Name: func4
+; RELOC-NEXT: Name: func2
; RELOC-NEXT: - Index: 4
-; RELOC-NEXT: Name: __cxa_atexit
+; RELOC-NEXT: Name: func3
; RELOC-NEXT: - Index: 5
-; RELOC-NEXT: Name: _start
+; RELOC-NEXT: Name: func4
; RELOC-NEXT: - Index: 6
-; RELOC-NEXT: Name: .Lcall_dtors.101
+; RELOC-NEXT: Name: __cxa_atexit
; RELOC-NEXT: - Index: 7
-; RELOC-NEXT: Name: .Lregister_call_dtors.101
+; RELOC-NEXT: Name: _start
; RELOC-NEXT: - Index: 8
-; RELOC-NEXT: Name: .Lcall_dtors.1001
-; RELOC-NEXT: - Index: 9
-; RELOC-NEXT: Name: .Lregister_call_dtors.1001
-; RELOC-NEXT: - Index: 10
-; RELOC-NEXT: Name: myctor
-; RELOC-NEXT: - Index: 11
-; RELOC-NEXT: Name: mydtor
-; RELOC-NEXT: - Index: 12
; RELOC-NEXT: Name: .Lcall_dtors.101
-; RELOC-NEXT: - Index: 13
+; RELOC-NEXT: - Index: 9
; RELOC-NEXT: Name: .Lregister_call_dtors.101
+; RELOC-NEXT: - Index: 10
+; RELOC-NEXT: Name: .Lcall_dtors.1001
+; RELOC-NEXT: - Index: 11
+; RELOC-NEXT: Name: .Lregister_call_dtors.1001
+; RELOC-NEXT: - Index: 12
+; RELOC-NEXT: Name: .Lcall_dtors.4000
+; RELOC-NEXT: - Index: 13
+; RELOC-NEXT: Name: .Lregister_call_dtors.4000
; RELOC-NEXT: - Index: 14
-; RELOC-NEXT: Name: .Lcall_dtors.202
+; RELOC-NEXT: Name: myctor
; RELOC-NEXT: - Index: 15
-; RELOC-NEXT: Name: .Lregister_call_dtors.202
+; RELOC-NEXT: Name: mydtor
; RELOC-NEXT: - Index: 16
-; RELOC-NEXT: Name: .Lcall_dtors.2002
+; RELOC-NEXT: Name: .Lcall_dtors.101
; RELOC-NEXT: - Index: 17
+; RELOC-NEXT: Name: .Lregister_call_dtors.101
+; RELOC-NEXT: - Index: 18
+; RELOC-NEXT: Name: .Lcall_dtors.202
+; RELOC-NEXT: - Index: 19
+; RELOC-NEXT: Name: .Lregister_call_dtors.202
+; RELOC-NEXT: - Index: 20
+; RELOC-NEXT: Name: .Lcall_dtors.2002
+; RELOC-NEXT: - Index: 21
; RELOC-NEXT: Name: .Lregister_call_dtors.2002
; RELOC-NEXT: ...
diff --git a/test/wasm/load-undefined.test b/test/wasm/load-undefined.test
index 144ef28..7e78bf0 100644
--- a/test/wasm/load-undefined.test
+++ b/test/wasm/load-undefined.test
@@ -21,13 +21,13 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: ret32
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: ret64
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Type:
diff --git a/test/wasm/local-symbols.ll b/test/wasm/local-symbols.ll
index 149ba03..fcb3c42 100644
--- a/test/wasm/local-symbols.ll
+++ b/test/wasm/local-symbols.ll
@@ -26,13 +26,13 @@
; CHECK-NEXT: - Type: TYPE
; CHECK-NEXT: Signatures:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: ReturnType: I32
-; CHECK-NEXT: ParamTypes:
-; CHECK-NEXT: - Index: 1
; CHECK-NEXT: ReturnType: NORESULT
; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 1, 1 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 0 ]
; CHECK-NEXT: - Type: TABLE
; CHECK-NEXT: Tables:
; CHECK-NEXT: - ElemType: ANYFUNC
@@ -82,7 +82,7 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: foo
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 3
@@ -90,13 +90,13 @@
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4100280284888080000B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1080808080001A0B
+; CHECK-NEXT: Body: 4100280284888080000B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: Body: 1081808080001A0B
; CHECK-NEXT: - Type: DATA
; CHECK-NEXT: Segments:
; CHECK-NEXT: - SectionOffset: 7
@@ -109,9 +109,9 @@
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: baz
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: baz
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: _start
; CHECK-NEXT: ...
diff --git a/test/wasm/locals-duplicate.test b/test/wasm/locals-duplicate.test
index 0eee7b6..017bc09 100644
--- a/test/wasm/locals-duplicate.test
+++ b/test/wasm/locals-duplicate.test
@@ -10,14 +10,14 @@
; CHECK-NEXT: - Type: TYPE
; CHECK-NEXT: Signatures:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: ReturnType: I32
-; CHECK-NEXT: ParamTypes:
-; CHECK-NEXT: - Index: 1
; CHECK-NEXT: ReturnType: NORESULT
; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-; CHECK-NEXT: 0, 0, 1 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+; CHECK-NEXT: 1, 1, 1 ]
; CHECK-NEXT: - Type: TABLE
; CHECK-NEXT: Tables:
; CHECK-NEXT: - ElemType: ANYFUNC
@@ -73,63 +73,63 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: colliding_func2
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: get_global1A
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 3
+; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Name: get_global2A
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 4
+; CHECK-NEXT: Index: 5
; CHECK-NEXT: - Name: colliding_global2
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: get_global3A
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 5
+; CHECK-NEXT: Index: 6
; CHECK-NEXT: - Name: get_func1A
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 6
+; CHECK-NEXT: Index: 7
; CHECK-NEXT: - Name: get_func2A
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 7
+; CHECK-NEXT: Index: 8
; CHECK-NEXT: - Name: get_func3A
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 8
+; CHECK-NEXT: Index: 9
; CHECK-NEXT: - Name: colliding_func1
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 9
+; CHECK-NEXT: Index: 10
; CHECK-NEXT: - Name: get_global1B
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 12
+; CHECK-NEXT: Index: 13
; CHECK-NEXT: - Name: colliding_global1
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Name: get_global2B
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 13
+; CHECK-NEXT: Index: 14
; CHECK-NEXT: - Name: get_global3B
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 14
+; CHECK-NEXT: Index: 15
; CHECK-NEXT: - Name: get_func1B
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 15
+; CHECK-NEXT: Index: 16
; CHECK-NEXT: - Name: get_func2B
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 16
+; CHECK-NEXT: Index: 17
; CHECK-NEXT: - Name: get_func3B
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 17
+; CHECK-NEXT: Index: 18
; CHECK-NEXT: - Type: ELEM
; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1
-; CHECK-NEXT: Functions: [ 0, 1, 2, 9, 10, 11 ]
+; CHECK-NEXT: Functions: [ 1, 2, 3, 10, 11, 12 ]
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41020B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 41020B
@@ -138,25 +138,25 @@
; CHECK-NEXT: Body: 41020B
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4180888080000B
+; CHECK-NEXT: Body: 41020B
; CHECK-NEXT: - Index: 4
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4184888080000B
+; CHECK-NEXT: Body: 4180888080000B
; CHECK-NEXT: - Index: 5
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4188888080000B
+; CHECK-NEXT: Body: 4184888080000B
; CHECK-NEXT: - Index: 6
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4181808080000B
+; CHECK-NEXT: Body: 4188888080000B
; CHECK-NEXT: - Index: 7
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4182808080000B
+; CHECK-NEXT: Body: 4181808080000B
; CHECK-NEXT: - Index: 8
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4183808080000B
+; CHECK-NEXT: Body: 4182808080000B
; CHECK-NEXT: - Index: 9
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41020B
+; CHECK-NEXT: Body: 4183808080000B
; CHECK-NEXT: - Index: 10
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 41020B
@@ -165,25 +165,25 @@
; CHECK-NEXT: Body: 41020B
; CHECK-NEXT: - Index: 12
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 418C888080000B
+; CHECK-NEXT: Body: 41020B
; CHECK-NEXT: - Index: 13
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4190888080000B
+; CHECK-NEXT: Body: 418C888080000B
; CHECK-NEXT: - Index: 14
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4194888080000B
+; CHECK-NEXT: Body: 4190888080000B
; CHECK-NEXT: - Index: 15
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4184808080000B
+; CHECK-NEXT: Body: 4194888080000B
; CHECK-NEXT: - Index: 16
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4185808080000B
+; CHECK-NEXT: Body: 4184808080000B
; CHECK-NEXT: - Index: 17
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4186808080000B
+; CHECK-NEXT: Body: 4185808080000B
; CHECK-NEXT: - Index: 18
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: Body: 4186808080000B
; CHECK-NEXT: - Type: DATA
; CHECK-NEXT: Segments:
; CHECK-NEXT: - SectionOffset: 7
@@ -196,43 +196,43 @@
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: colliding_func1
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: colliding_func2
-; CHECK-NEXT: - Index: 2
-; CHECK-NEXT: Name: colliding_func3
-; CHECK-NEXT: - Index: 3
-; CHECK-NEXT: Name: get_global1A
-; CHECK-NEXT: - Index: 4
-; CHECK-NEXT: Name: get_global2A
-; CHECK-NEXT: - Index: 5
-; CHECK-NEXT: Name: get_global3A
-; CHECK-NEXT: - Index: 6
-; CHECK-NEXT: Name: get_func1A
-; CHECK-NEXT: - Index: 7
-; CHECK-NEXT: Name: get_func2A
-; CHECK-NEXT: - Index: 8
-; CHECK-NEXT: Name: get_func3A
-; CHECK-NEXT: - Index: 9
-; CHECK-NEXT: Name: colliding_func1
-; CHECK-NEXT: - Index: 10
-; CHECK-NEXT: Name: colliding_func2
-; CHECK-NEXT: - Index: 11
-; CHECK-NEXT: Name: colliding_func3
-; CHECK-NEXT: - Index: 12
-; CHECK-NEXT: Name: get_global1B
-; CHECK-NEXT: - Index: 13
-; CHECK-NEXT: Name: get_global2B
-; CHECK-NEXT: - Index: 14
-; CHECK-NEXT: Name: get_global3B
-; CHECK-NEXT: - Index: 15
-; CHECK-NEXT: Name: get_func1B
-; CHECK-NEXT: - Index: 16
-; CHECK-NEXT: Name: get_func2B
-; CHECK-NEXT: - Index: 17
-; CHECK-NEXT: Name: get_func3B
-; CHECK-NEXT: - Index: 18
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: colliding_func1
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: colliding_func2
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: colliding_func3
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Name: get_global1A
+; CHECK-NEXT: - Index: 5
+; CHECK-NEXT: Name: get_global2A
+; CHECK-NEXT: - Index: 6
+; CHECK-NEXT: Name: get_global3A
+; CHECK-NEXT: - Index: 7
+; CHECK-NEXT: Name: get_func1A
+; CHECK-NEXT: - Index: 8
+; CHECK-NEXT: Name: get_func2A
+; CHECK-NEXT: - Index: 9
+; CHECK-NEXT: Name: get_func3A
+; CHECK-NEXT: - Index: 10
+; CHECK-NEXT: Name: colliding_func1
+; CHECK-NEXT: - Index: 11
+; CHECK-NEXT: Name: colliding_func2
+; CHECK-NEXT: - Index: 12
+; CHECK-NEXT: Name: colliding_func3
+; CHECK-NEXT: - Index: 13
+; CHECK-NEXT: Name: get_global1B
+; CHECK-NEXT: - Index: 14
+; CHECK-NEXT: Name: get_global2B
+; CHECK-NEXT: - Index: 15
+; CHECK-NEXT: Name: get_global3B
+; CHECK-NEXT: - Index: 16
+; CHECK-NEXT: Name: get_func1B
+; CHECK-NEXT: - Index: 17
+; CHECK-NEXT: Name: get_func2B
+; CHECK-NEXT: - Index: 18
+; CHECK-NEXT: Name: get_func3B
; CHECK-NEXT: ...
diff --git a/test/wasm/undefined-weak-call.ll b/test/wasm/undefined-weak-call.ll
new file mode 100644
index 0000000..cd26e05
--- /dev/null
+++ b/test/wasm/undefined-weak-call.ll
@@ -0,0 +1,117 @@
+; RUN: llc -filetype=obj %s -o %t.o
+; RUN: wasm-ld --check-signatures --no-entry %t.o -o %t.wasm
+; RUN: obj2yaml %t.wasm | FileCheck %s
+
+; Check that calling an undefined weak function generates an appropriate stub
+; that will fail at runtime with "unreachable".
+
+target triple = "wasm32-unknown-unknown-wasm"
+
+declare extern_weak void @weakFunc1()
+declare extern_weak void @weakFunc2() ; same signature
+declare extern_weak void @weakFunc3(i32 %arg) ; different
+declare extern_weak void @weakFunc4() ; should be GC'd as not called
+
+define i32 @callWeakFuncs() {
+ call void @weakFunc1()
+ call void @weakFunc2()
+ call void @weakFunc3(i32 2)
+ %addr1 = ptrtoint void ()* @weakFunc1 to i32
+ %addr4 = ptrtoint void ()* @weakFunc4 to i32
+ %sum = add i32 %addr1, %addr4
+ ret i32 %sum
+}
+
+; CHECK: --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT: Version: 0x00000001
+; CHECK-NEXT: Sections:
+; CHECK-NEXT: - Type: TYPE
+; CHECK-NEXT: Signatures:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: NORESULT
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - I32
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Type: FUNCTION
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 0, 1, 2 ]
+; CHECK-NEXT: - Type: TABLE
+; CHECK-NEXT: Tables:
+; CHECK-NEXT: - ElemType: ANYFUNC
+; CHECK-NEXT: Limits:
+; CHECK-NEXT: Flags: [ HAS_MAX ]
+; CHECK-NEXT: Initial: 0x00000001
+; CHECK-NEXT: Maximum: 0x00000001
+; CHECK-NEXT: - Type: MEMORY
+; CHECK-NEXT: Memories:
+; CHECK-NEXT: - Initial: 0x00000002
+; CHECK-NEXT: - Type: GLOBAL
+; CHECK-NEXT: Globals:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: true
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 66560
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Type: I32
+; CHECK-NEXT: Mutable: false
+; CHECK-NEXT: InitExpr:
+; CHECK-NEXT: Opcode: I32_CONST
+; CHECK-NEXT: Value: 1024
+; CHECK-NEXT: - Type: EXPORT
+; CHECK-NEXT: Exports:
+; CHECK-NEXT: - Name: memory
+; CHECK-NEXT: Kind: MEMORY
+; CHECK-NEXT: Index: 0
+; CHECK-NEXT: - Name: __heap_base
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 1
+; CHECK-NEXT: - Name: __data_end
+; CHECK-NEXT: Kind: GLOBAL
+; CHECK-NEXT: Index: 2
+; CHECK-NEXT: - Name: callWeakFuncs
+; CHECK-NEXT: Kind: FUNCTION
+; CHECK-NEXT: Index: 4
+; CHECK-NEXT: - Type: CODE
+; CHECK-NEXT: Functions:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 000B
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 000B
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 000B
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Locals:
+; CHECK-NEXT: Body: 10818080800010828080800041021083808080004180808080004180808080006A0B
+; CHECK-NEXT: - Type: CUSTOM
+; CHECK-NEXT: Name: name
+; CHECK-NEXT: FunctionNames:
+; CHECK-NEXT: - Index: 0
+; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: undefined function weakFunc1
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: undefined function weakFunc2
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: undefined function weakFunc3
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Name: callWeakFuncs
+; CHECK-NEXT: ...
diff --git a/test/wasm/undefined.ll b/test/wasm/undefined.ll
index 69b7a82..a74dd6d 100644
--- a/test/wasm/undefined.ll
+++ b/test/wasm/undefined.ll
@@ -6,10 +6,13 @@
; CHECK: error: {{.*}}.o: undefined symbol: foo
; CHECK: error: undefined symbol: baz
-; But succeeds if we pass a file containing 'foo' as --allow-undefined-file.
+; Succeeds if we pass a file containing 'foo' as --allow-undefined-file.
; RUN: echo 'foo' > %t.txt
; RUN: wasm-ld --check-signatures --allow-undefined-file=%t.txt -o %t.wasm %t.o
+; Succeeds even if a missing symbol is added via --export
+; RUN: wasm-ld --check-signatures --allow-undefined --export=xxx -o %t.wasm %t.o
+
target triple = "wasm32-unknown-unknown-wasm"
; Takes the address of the external foo() resulting in undefined external
diff --git a/test/wasm/visibility-hidden.ll b/test/wasm/visibility-hidden.ll
index 38db22f..530649b 100644
--- a/test/wasm/visibility-hidden.ll
+++ b/test/wasm/visibility-hidden.ll
@@ -44,11 +44,11 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: objectDefault
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: archiveDefault
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 4
+; CHECK-NEXT: Index: 5
; CHECK-NEXT: - Type:
diff --git a/test/wasm/weak-alias-overide.ll b/test/wasm/weak-alias-overide.ll
index b4f8361..2252298 100644
--- a/test/wasm/weak-alias-overide.ll
+++ b/test/wasm/weak-alias-overide.ll
@@ -26,13 +26,13 @@
; CHECK-NEXT: - Type: TYPE
; CHECK-NEXT: Signatures:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: ReturnType: I32
-; CHECK-NEXT: ParamTypes:
-; CHECK-NEXT: - Index: 1
; CHECK-NEXT: ReturnType: NORESULT
; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 1, 0, 0, 0, 0, 0, 1 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 0, 1, 1, 1, 1, 1 ]
; CHECK-NEXT: - Type: TABLE
; CHECK-NEXT: Tables:
; CHECK-NEXT: - ElemType: ANYFUNC
@@ -76,78 +76,78 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: alias_fn
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: direct_fn
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: call_direct
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 3
+; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Name: call_alias
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 4
+; CHECK-NEXT: Index: 5
; CHECK-NEXT: - Name: call_alias_ptr
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 5
+; CHECK-NEXT: Index: 6
; CHECK-NEXT: - Name: call_direct_ptr
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 6
+; CHECK-NEXT: Index: 7
; CHECK-NEXT: - Type: ELEM
; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1
-; CHECK-NEXT: Functions: [ 0, 2 ]
+; CHECK-NEXT: Functions: [ 1, 3 ]
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41010B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1080808080001A0B
+; CHECK-NEXT: Body: 41010B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: Body: 1081808080001A0B
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1082808080000B
+; CHECK-NEXT: Body: 41000B
; CHECK-NEXT: - Index: 4
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1080808080000B
+; CHECK-NEXT: Body: 1083808080000B
; CHECK-NEXT: - Index: 5
; CHECK-NEXT: Locals:
-; CHECK-NEXT: - Type: I32
-; CHECK-NEXT: Count: 2
-; CHECK-NEXT: Body: 23808080800041106B220024808080800020004181808080003602081080808080002101200041106A24808080800020010B
+; CHECK-NEXT: Body: 1081808080000B
; CHECK-NEXT: - Index: 6
; CHECK-NEXT: Locals:
; CHECK-NEXT: - Type: I32
; CHECK-NEXT: Count: 2
-; CHECK-NEXT: Body: 23808080800041106B220024808080800020004182808080003602081082808080002101200041106A24808080800020010B
+; CHECK-NEXT: Body: 23808080800041106B220024808080800020004181808080003602081081808080002101200041106A24808080800020010B
; CHECK-NEXT: - Index: 7
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Count: 2
+; CHECK-NEXT: Body: 23808080800041106B220024808080800020004182808080003602081083808080002101200041106A24808080800020010B
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: alias_fn
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 2
-; CHECK-NEXT: Name: direct_fn
-; CHECK-NEXT: - Index: 3
-; CHECK-NEXT: Name: call_direct
-; CHECK-NEXT: - Index: 4
-; CHECK-NEXT: Name: call_alias
-; CHECK-NEXT: - Index: 5
-; CHECK-NEXT: Name: call_alias_ptr
-; CHECK-NEXT: - Index: 6
-; CHECK-NEXT: Name: call_direct_ptr
-; CHECK-NEXT: - Index: 7
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: alias_fn
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: direct_fn
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Name: call_direct
+; CHECK-NEXT: - Index: 5
+; CHECK-NEXT: Name: call_alias
+; CHECK-NEXT: - Index: 6
+; CHECK-NEXT: Name: call_alias_ptr
+; CHECK-NEXT: - Index: 7
+; CHECK-NEXT: Name: call_direct_ptr
; CHECK-NEXT: ...
diff --git a/test/wasm/weak-alias.ll b/test/wasm/weak-alias.ll
index a71c2ea..1df16e2 100644
--- a/test/wasm/weak-alias.ll
+++ b/test/wasm/weak-alias.ll
@@ -29,7 +29,7 @@
; CHECK-NEXT: ReturnType: I32
; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 1, 1, 1, 1, 1, 0 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 1, 1, 1, 1, 1 ]
; CHECK-NEXT: - Type: TABLE
; CHECK-NEXT: Tables:
; CHECK-NEXT: - ElemType: ANYFUNC
@@ -73,75 +73,75 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: alias_fn
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: direct_fn
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: call_direct
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: call_alias
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 3
+; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Name: call_alias_ptr
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 4
+; CHECK-NEXT: Index: 5
; CHECK-NEXT: - Name: call_direct_ptr
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 5
+; CHECK-NEXT: Index: 6
; CHECK-NEXT: - Type: ELEM
; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1
-; CHECK-NEXT: Functions: [ 1 ]
+; CHECK-NEXT: Functions: [ 2 ]
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1081808080001A0B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41000B
+; CHECK-NEXT: Body: 1082808080001A0B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1081808080000B
+; CHECK-NEXT: Body: 41000B
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1081808080000B
+; CHECK-NEXT: Body: 1082808080000B
; CHECK-NEXT: - Index: 4
; CHECK-NEXT: Locals:
-; CHECK-NEXT: - Type: I32
-; CHECK-NEXT: Count: 2
-; CHECK-NEXT: Body: 23808080800041106B220024808080800020004181808080003602081081808080002101200041106A24808080800020010B
+; CHECK-NEXT: Body: 1082808080000B
; CHECK-NEXT: - Index: 5
; CHECK-NEXT: Locals:
; CHECK-NEXT: - Type: I32
; CHECK-NEXT: Count: 2
-; CHECK-NEXT: Body: 23808080800041106B220024808080800020004181808080003602081081808080002101200041106A24808080800020010B
+; CHECK-NEXT: Body: 23808080800041106B220024808080800020004181808080003602081082808080002101200041106A24808080800020010B
; CHECK-NEXT: - Index: 6
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: - Type: I32
+; CHECK-NEXT: Count: 2
+; CHECK-NEXT: Body: 23808080800041106B220024808080800020004181808080003602081082808080002101200041106A24808080800020010B
; CHECK-NEXT: - Type: CUSTOM
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: direct_fn
-; CHECK-NEXT: - Index: 2
-; CHECK-NEXT: Name: call_direct
-; CHECK-NEXT: - Index: 3
-; CHECK-NEXT: Name: call_alias
-; CHECK-NEXT: - Index: 4
-; CHECK-NEXT: Name: call_alias_ptr
-; CHECK-NEXT: - Index: 5
-; CHECK-NEXT: Name: call_direct_ptr
-; CHECK-NEXT: - Index: 6
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: direct_fn
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: call_direct
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Name: call_alias
+; CHECK-NEXT: - Index: 5
+; CHECK-NEXT: Name: call_alias_ptr
+; CHECK-NEXT: - Index: 6
+; CHECK-NEXT: Name: call_direct_ptr
; CHECK-NEXT: ...
; RUN: wasm-ld --check-signatures --relocatable %t.o %t2.o -o %t.reloc.o
diff --git a/test/wasm/weak-symbols.ll b/test/wasm/weak-symbols.ll
index 9724e81..7ecb900 100644
--- a/test/wasm/weak-symbols.ll
+++ b/test/wasm/weak-symbols.ll
@@ -29,7 +29,7 @@
; CHECK-NEXT: ReturnType: I32
; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 1, 1, 1, 1, 0 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 0, 1, 1, 1, 1 ]
; CHECK-NEXT: - Type: TABLE
; CHECK-NEXT: Tables:
; CHECK-NEXT: - ElemType: ANYFUNC
@@ -79,45 +79,45 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: weakFn
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: exportWeak1
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: weakGlobal
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: exportWeak2
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 4
+; CHECK-NEXT: Index: 5
; CHECK-NEXT: - Type: ELEM
; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1
-; CHECK-NEXT: Functions: [ 1 ]
+; CHECK-NEXT: Functions: [ 2 ]
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1081808080001A0B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41010B
+; CHECK-NEXT: Body: 1082808080001A0B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4181808080000B
+; CHECK-NEXT: Body: 41010B
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 41020B
+; CHECK-NEXT: Body: 4181808080000B
; CHECK-NEXT: - Index: 4
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4181808080000B
+; CHECK-NEXT: Body: 41020B
; CHECK-NEXT: - Index: 5
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: Body: 4181808080000B
; CHECK-NEXT: - Type: DATA
; CHECK-NEXT: Segments:
; CHECK-NEXT: - SectionOffset: 7
@@ -130,15 +130,15 @@
; CHECK-NEXT: Name: name
; CHECK-NEXT: FunctionNames:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: Name: _start
-; CHECK-NEXT: - Index: 1
-; CHECK-NEXT: Name: weakFn
-; CHECK-NEXT: - Index: 2
-; CHECK-NEXT: Name: exportWeak1
-; CHECK-NEXT: - Index: 3
-; CHECK-NEXT: Name: weakFn
-; CHECK-NEXT: - Index: 4
-; CHECK-NEXT: Name: exportWeak2
-; CHECK-NEXT: - Index: 5
; CHECK-NEXT: Name: __wasm_call_ctors
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: Name: _start
+; CHECK-NEXT: - Index: 2
+; CHECK-NEXT: Name: weakFn
+; CHECK-NEXT: - Index: 3
+; CHECK-NEXT: Name: exportWeak1
+; CHECK-NEXT: - Index: 4
+; CHECK-NEXT: Name: weakFn
+; CHECK-NEXT: - Index: 5
+; CHECK-NEXT: Name: exportWeak2
; CHECK-NEXT: ...
diff --git a/test/wasm/weak-undefined.ll b/test/wasm/weak-undefined.ll
index 6de571f..ce68e54 100644
--- a/test/wasm/weak-undefined.ll
+++ b/test/wasm/weak-undefined.ll
@@ -33,13 +33,13 @@
; CHECK-NEXT: - Type: TYPE
; CHECK-NEXT: Signatures:
; CHECK-NEXT: - Index: 0
-; CHECK-NEXT: ReturnType: I32
-; CHECK-NEXT: ParamTypes:
-; CHECK-NEXT: - Index: 1
; CHECK-NEXT: ReturnType: NORESULT
; CHECK-NEXT: ParamTypes:
+; CHECK-NEXT: - Index: 1
+; CHECK-NEXT: ReturnType: I32
+; CHECK-NEXT: ParamTypes:
; CHECK-NEXT: - Type: FUNCTION
-; CHECK-NEXT: FunctionTypes: [ 0, 0, 1, 1 ]
+; CHECK-NEXT: FunctionTypes: [ 0, 1, 1, 0 ]
; CHECK-NEXT: - Type: TABLE
; CHECK-NEXT: Tables:
; CHECK-NEXT: - ElemType: ANYFUNC
@@ -83,25 +83,25 @@
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: _start
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 2
+; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: get_address_of_foo
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 0
+; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: get_address_of_global_var
; CHECK-NEXT: Kind: FUNCTION
-; CHECK-NEXT: Index: 1
+; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Type: CODE
; CHECK-NEXT: Functions:
; CHECK-NEXT: - Index: 0
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 4180808080000B
+; CHECK-NEXT: Body: 0B
; CHECK-NEXT: - Index: 1
; CHECK-NEXT: Locals:
; CHECK-NEXT: Body: 4180808080000B
; CHECK-NEXT: - Index: 2
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 1081808080001A0B
+; CHECK-NEXT: Body: 4180808080000B
; CHECK-NEXT: - Index: 3
; CHECK-NEXT: Locals:
-; CHECK-NEXT: Body: 0B
+; CHECK-NEXT: Body: 1082808080001A0B
; CHECK-NEXT: ...
diff --git a/wasm/Config.h b/wasm/Config.h
index b6db524..adccbfe 100644
--- a/wasm/Config.h
+++ b/wasm/Config.h
@@ -21,8 +21,10 @@
bool AllowUndefined;
bool CheckSignatures;
bool Demangle;
+ bool ExportTable;
bool GcSections;
bool ImportMemory;
+ bool ImportTable;
bool PrintGcSections;
bool Relocatable;
bool StripAll;
diff --git a/wasm/Driver.cpp b/wasm/Driver.cpp
index e68a7ce..97a8159 100644
--- a/wasm/Driver.cpp
+++ b/wasm/Driver.cpp
@@ -9,6 +9,7 @@
#include "lld/Common/Driver.h"
#include "Config.h"
+#include "InputChunks.h"
#include "InputGlobal.h"
#include "MarkLive.h"
#include "SymbolTable.h"
@@ -16,6 +17,7 @@
#include "lld/Common/Args.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
+#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/Twine.h"
@@ -214,6 +216,46 @@
return Arg->getValue();
}
+static const uint8_t UnreachableFn[] = {
+ 0x03 /* ULEB length */, 0x00 /* ULEB num locals */,
+ 0x00 /* opcode unreachable */, 0x0b /* opcode end */
+};
+
+// For weak undefined functions, there may be "call" instructions that reference
+// the symbol. In this case, we need to synthesise a dummy/stub function that
+// will abort at runtime, so that relocations can still provided an operand to
+// the call instruction that passes Wasm validation.
+static void handleWeakUndefines() {
+ for (Symbol *Sym : Symtab->getSymbols()) {
+ if (!Sym->isUndefined() || !Sym->isWeak())
+ continue;
+ auto *FuncSym = dyn_cast<FunctionSymbol>(Sym);
+ if (!FuncSym)
+ continue;
+
+ // It is possible for undefined functions not to have a signature (eg. if
+ // added via "--undefined"), but weak undefined ones do have a signature.
+ assert(FuncSym->getFunctionType());
+ const WasmSignature &Sig = *FuncSym->getFunctionType();
+
+ // Add a synthetic dummy for weak undefined functions. These dummies will
+ // be GC'd if not used as the target of any "call" instructions.
+ Optional<std::string> SymName = demangleItanium(Sym->getName());
+ StringRef StubName =
+ Saver.save("undefined function " +
+ (SymName ? StringRef(*SymName) : Sym->getName()));
+ SyntheticFunction *Func = make<SyntheticFunction>(Sig, StubName);
+ Func->setBody(UnreachableFn);
+ // Ensure it compares equal to the null pointer, and so that table relocs
+ // don't pull in the stub body (only call-operand relocs should do that).
+ Func->setTableIndex(0);
+ Symtab->SyntheticFunctions.emplace_back(Func);
+ // Hide our dummy to prevent export.
+ uint32_t Flags = WASM_SYMBOL_VISIBILITY_HIDDEN;
+ replaceSymbol<DefinedFunction>(Sym, Sym->getName(), Flags, nullptr, Func);
+ }
+}
+
void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
WasmOptTable Parser;
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
@@ -242,8 +284,11 @@
Config->AllowUndefined = Args.hasArg(OPT_allow_undefined);
Config->CheckSignatures =
Args.hasFlag(OPT_check_signatures, OPT_no_check_signatures, false);
+ Config->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true);
Config->Entry = getEntry(Args, Args.hasArg(OPT_relocatable) ? "" : "_start");
+ Config->ExportTable = Args.hasArg(OPT_export_table);
Config->ImportMemory = Args.hasArg(OPT_import_memory);
+ Config->ImportTable = Args.hasArg(OPT_import_table);
Config->OutputFile = Args.getLastArgValue(OPT_o);
Config->Relocatable = Args.hasArg(OPT_relocatable);
Config->GcSections =
@@ -273,6 +318,9 @@
if (Config->OutputFile.empty())
error("no output file specified");
+ if (Config->ImportTable && Config->ExportTable)
+ error("--import-table and --export-table may not be used together");
+
if (Config->Relocatable) {
if (!Config->Entry.empty())
error("entry point specified for relocatable output file");
@@ -295,9 +343,11 @@
StackPointer->Live = true;
static WasmSignature NullSignature = {{}, WASM_TYPE_NORESULT};
+
// Add synthetic symbols before any others
WasmSym::CallCtors = Symtab->addSyntheticFunction(
- "__wasm_call_ctors", &NullSignature, WASM_SYMBOL_VISIBILITY_HIDDEN);
+ "__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
+ make<SyntheticFunction>(NullSignature, "__wasm_call_ctors"));
WasmSym::StackPointer = Symtab->addSyntheticGlobal(
"__stack_pointer", WASM_SYMBOL_VISIBILITY_HIDDEN, StackPointer);
WasmSym::HeapBase = Symtab->addSyntheticDataSymbol("__heap_base", 0);
@@ -310,7 +360,7 @@
&NullSignature);
// Handle the `--undefined <sym>` options.
- for (auto* Arg : Args.filtered(OPT_undefined))
+ for (auto *Arg : Args.filtered(OPT_undefined))
Symtab->addUndefinedFunction(Arg->getValue(), 0, nullptr, nullptr);
}
@@ -323,6 +373,10 @@
for (InputFile *F : Files)
Symtab->addFile(F);
+ // Add synthetic dummies for weak undefined functions.
+ if (!Config->Relocatable)
+ handleWeakUndefines();
+
// Make sure we have resolved all symbols.
if (!Config->Relocatable && !Config->AllowUndefined) {
Symtab->reportRemainingUndefines();
@@ -331,7 +385,7 @@
// -u/--undefined since these undefined symbols have only names and no
// function signature, which means they cannot be written to the final
// output.
- for (auto* Arg : Args.filtered(OPT_undefined)) {
+ for (auto *Arg : Args.filtered(OPT_undefined)) {
Symbol *Sym = Symtab->find(Arg->getValue());
if (!Sym->isDefined())
error("function forced with --undefined not found: " + Sym->getName());
@@ -346,7 +400,7 @@
Symbol *Sym = Symtab->find(Name);
if (Sym && Sym->isDefined())
Sym->setHidden(false);
- else
+ else if (!Config->AllowUndefined)
error("symbol exported via --export not found: " + Name);
}
diff --git a/wasm/InputChunks.cpp b/wasm/InputChunks.cpp
index 546fb51..0cac19b 100644
--- a/wasm/InputChunks.cpp
+++ b/wasm/InputChunks.cpp
@@ -23,10 +23,26 @@
using namespace lld;
using namespace lld::wasm;
+StringRef ReloctTypeToString(uint8_t RelocType) {
+ switch (RelocType) {
+#define WASM_RELOC(NAME, REL) case REL: return #NAME;
+#include "llvm/BinaryFormat/WasmRelocs.def"
+#undef WASM_RELOC
+ }
+ llvm_unreachable("unknown reloc type");
+}
+
std::string lld::toString(const InputChunk *C) {
return (toString(C->File) + ":(" + C->getName() + ")").str();
}
+StringRef InputChunk::getComdatName() const {
+ uint32_t Index = getComdat();
+ if (Index == UINT32_MAX)
+ return StringRef();
+ return File->getWasmObj()->linkingData().Comdats[Index];
+}
+
void InputChunk::copyRelocations(const WasmSection &Section) {
if (Section.Relocations.empty())
return;
@@ -46,14 +62,16 @@
if (Relocations.empty())
return;
- DEBUG(dbgs() << "applyRelocations: count=" << Relocations.size() << "\n");
+ DEBUG(dbgs() << "applying relocations: " << getName()
+ << " count=" << Relocations.size() << "\n");
int32_t Off = OutputOffset - getInputSectionOffset();
for (const WasmRelocation &Rel : Relocations) {
uint8_t *Loc = Buf + Rel.Offset + Off;
- uint64_t Value = File->calcNewValue(Rel);
-
- DEBUG(dbgs() << "write reloc: type=" << Rel.Type << " index=" << Rel.Index
+ uint32_t Value = File->calcNewValue(Rel);
+ uint32_t ExistingValue;
+ DEBUG(dbgs() << "apply reloc: type=" << ReloctTypeToString(Rel.Type)
+ << " addend=" << Rel.Addend << " index=" << Rel.Index
<< " value=" << Value << " offset=" << Rel.Offset << "\n");
switch (Rel.Type) {
@@ -61,19 +79,28 @@
case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
+ ExistingValue = decodeULEB128(Loc);
encodeULEB128(Value, Loc, 5);
break;
case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ ExistingValue = static_cast<uint32_t>(decodeSLEB128(Loc));
encodeSLEB128(static_cast<int32_t>(Value), Loc, 5);
break;
case R_WEBASSEMBLY_TABLE_INDEX_I32:
case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ ExistingValue = static_cast<uint32_t>(read32le(Loc));
write32le(Loc, Value);
break;
default:
llvm_unreachable("unknown relocation type");
}
+
+ uint32_t ExpectedValue = File->calcExpectedValue(Rel);
+ if (ExpectedValue != ExistingValue)
+ error("unexpected existing value for " + ReloctTypeToString(Rel.Type) +
+ ": existing=" + Twine(ExistingValue) +
+ " expected=" + Twine(ExpectedValue));
}
}
@@ -103,14 +130,16 @@
}
}
-void InputFunction::setOutputIndex(uint32_t Index) {
- DEBUG(dbgs() << "InputFunction::setOutputIndex: " << getName() << " -> " << Index << "\n");
- assert(!hasOutputIndex());
- OutputIndex = Index;
+void InputFunction::setFunctionIndex(uint32_t Index) {
+ DEBUG(dbgs() << "InputFunction::setFunctionIndex: " << getName() << " -> "
+ << Index << "\n");
+ assert(!hasFunctionIndex());
+ FunctionIndex = Index;
}
void InputFunction::setTableIndex(uint32_t Index) {
- DEBUG(dbgs() << "InputFunction::setTableIndex: " << getName() << " -> " << Index << "\n");
+ DEBUG(dbgs() << "InputFunction::setTableIndex: " << getName() << " -> "
+ << Index << "\n");
assert(!hasTableIndex());
TableIndex = Index;
}
diff --git a/wasm/InputChunks.h b/wasm/InputChunks.h
index bff574b..3d6ea23 100644
--- a/wasm/InputChunks.h
+++ b/wasm/InputChunks.h
@@ -24,14 +24,13 @@
#include "Config.h"
#include "InputFiles.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Strings.h"
#include "llvm/Object/Wasm.h"
+using llvm::object::WasmSection;
using llvm::object::WasmSegment;
using llvm::wasm::WasmFunction;
using llvm::wasm::WasmRelocation;
using llvm::wasm::WasmSignature;
-using llvm::object::WasmSection;
namespace llvm {
class raw_ostream;
@@ -45,7 +44,7 @@
class InputChunk {
public:
- enum Kind { DataSegment, Function };
+ enum Kind { DataSegment, Function, SyntheticFunction };
Kind kind() const { return SectionKind; }
@@ -57,8 +56,9 @@
ArrayRef<WasmRelocation> getRelocations() const { return Relocations; }
- virtual StringRef getComdat() const = 0;
virtual StringRef getName() const = 0;
+ virtual uint32_t getComdat() const = 0;
+ StringRef getComdatName() const;
size_t NumRelocations() const { return Relocations.size(); }
void writeRelocations(llvm::raw_ostream &OS) const;
@@ -99,7 +99,7 @@
uint32_t getAlignment() const { return Segment.Data.Alignment; }
StringRef getName() const override { return Segment.Data.Name; }
- StringRef getComdat() const override { return Segment.Data.Comdat; }
+ uint32_t getComdat() const override { return Segment.Data.Comdat; }
const OutputSegment *OutputSeg = nullptr;
int32_t OutputSegmentOffset = 0;
@@ -117,19 +117,19 @@
// combined to create the final output CODE section.
class InputFunction : public InputChunk {
public:
- InputFunction(const WasmSignature &S, const WasmFunction *Func,
- ObjFile *F)
+ InputFunction(const WasmSignature &S, const WasmFunction *Func, ObjFile *F)
: InputChunk(F, InputChunk::Function), Signature(S), Function(Func) {}
static bool classof(const InputChunk *C) {
- return C->kind() == InputChunk::Function;
+ return C->kind() == InputChunk::Function ||
+ C->kind() == InputChunk::SyntheticFunction;
}
StringRef getName() const override { return Function->Name; }
- StringRef getComdat() const override { return Function->Comdat; }
- uint32_t getOutputIndex() const { return OutputIndex.getValue(); }
- bool hasOutputIndex() const { return OutputIndex.hasValue(); }
- void setOutputIndex(uint32_t Index);
+ uint32_t getComdat() const override { return Function->Comdat; }
+ uint32_t getFunctionIndex() const { return FunctionIndex.getValue(); }
+ bool hasFunctionIndex() const { return FunctionIndex.hasValue(); }
+ void setFunctionIndex(uint32_t Index);
uint32_t getTableIndex() const { return TableIndex.getValue(); }
bool hasTableIndex() const { return TableIndex.hasValue(); }
void setTableIndex(uint32_t Index);
@@ -146,22 +146,31 @@
}
const WasmFunction *Function;
- llvm::Optional<uint32_t> OutputIndex;
+ llvm::Optional<uint32_t> FunctionIndex;
llvm::Optional<uint32_t> TableIndex;
};
class SyntheticFunction : public InputFunction {
public:
- SyntheticFunction(const WasmSignature &S, std::string Body, StringRef Name)
- : InputFunction(S, nullptr, nullptr), Name(Name), Body(std::move(Body)) {}
+ SyntheticFunction(const WasmSignature &S, StringRef Name)
+ : InputFunction(S, nullptr, nullptr), Name(Name) {
+ SectionKind = InputChunk::SyntheticFunction;
+ }
+
+ static bool classof(const InputChunk *C) {
+ return C->kind() == InputChunk::SyntheticFunction;
+ }
StringRef getName() const override { return Name; }
+ uint32_t getComdat() const override { return UINT32_MAX; }
+
+ void setBody(ArrayRef<uint8_t> Body_) { Body = Body_; }
protected:
- ArrayRef<uint8_t> data() const override { return toArrayRef(Body); }
+ ArrayRef<uint8_t> data() const override { return Body; }
StringRef Name;
- std::string Body;
+ ArrayRef<uint8_t> Body;
};
} // namespace wasm
diff --git a/wasm/InputFiles.cpp b/wasm/InputFiles.cpp
index 2c65c7b..e743686 100644
--- a/wasm/InputFiles.cpp
+++ b/wasm/InputFiles.cpp
@@ -49,10 +49,9 @@
"\n Global Imports : " + Twine(WasmObj->getNumImportedGlobals()));
}
-// Relocations contain an index into the function, global or table index
-// space of the input file. This function takes a relocation and returns the
-// relocated index (i.e. translates from the input index space to the output
-// index space).
+// Relocations contain either symbol or type indices. This function takes a
+// relocation and returns relocated index (i.e. translates from the input
+// sybmol/type space to the output symbol/type space).
uint32_t ObjFile::calcNewIndex(const WasmRelocation &Reloc) const {
if (Reloc.Type == R_WEBASSEMBLY_TYPE_INDEX_LEB) {
assert(TypeIsUsed[Reloc.Index]);
@@ -61,30 +60,56 @@
return Symbols[Reloc.Index]->getOutputSymbolIndex();
}
+// Calculate the value we expect to find at the relocation location.
+// This is used as a sanity check before applying a relocation to a given
+// location. It is useful for catching bugs in the compiler and linker.
+uint32_t ObjFile::calcExpectedValue(const WasmRelocation &Reloc) const {
+ switch (Reloc.Type) {
+ case R_WEBASSEMBLY_TABLE_INDEX_I32:
+ case R_WEBASSEMBLY_TABLE_INDEX_SLEB: {
+ const WasmSymbol& Sym = WasmObj->syms()[Reloc.Index];
+ return TableEntries[Sym.Info.ElementIndex];
+ }
+ case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
+ case R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ case R_WEBASSEMBLY_MEMORY_ADDR_LEB: {
+ const WasmSymbol& Sym = WasmObj->syms()[Reloc.Index];
+ if (Sym.isUndefined())
+ return 0;
+ const WasmSegment& Segment = WasmObj->dataSegments()[Sym.Info.DataRef.Segment];
+ return Segment.Data.Offset.Value.Int32 + Sym.Info.DataRef.Offset +
+ Reloc.Addend;
+ }
+ case R_WEBASSEMBLY_TYPE_INDEX_LEB:
+ return Reloc.Index;
+ case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
+ case R_WEBASSEMBLY_GLOBAL_INDEX_LEB: {
+ const WasmSymbol& Sym = WasmObj->syms()[Reloc.Index];
+ return Sym.Info.ElementIndex;
+ }
+ default:
+ llvm_unreachable("unknown relocation type");
+ }
+}
+
// Translate from the relocation's index into the final linked output value.
uint32_t ObjFile::calcNewValue(const WasmRelocation &Reloc) const {
switch (Reloc.Type) {
case R_WEBASSEMBLY_TABLE_INDEX_I32:
- case R_WEBASSEMBLY_TABLE_INDEX_SLEB: {
- // The null case is possible, if you take the address of a weak function
- // that's simply not supplied.
- FunctionSymbol *Sym = getFunctionSymbol(Reloc.Index);
- if (Sym->hasTableIndex())
- return Sym->getTableIndex();
- return 0;
- }
+ case R_WEBASSEMBLY_TABLE_INDEX_SLEB:
+ return getFunctionSymbol(Reloc.Index)->getTableIndex();
case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
case R_WEBASSEMBLY_MEMORY_ADDR_I32:
case R_WEBASSEMBLY_MEMORY_ADDR_LEB:
if (auto *Sym = dyn_cast<DefinedData>(getDataSymbol(Reloc.Index)))
return Sym->getVirtualAddress() + Reloc.Addend;
- return Reloc.Addend;
+ return 0;
case R_WEBASSEMBLY_TYPE_INDEX_LEB:
return TypeMap[Reloc.Index];
case R_WEBASSEMBLY_FUNCTION_INDEX_LEB:
- return getFunctionSymbol(Reloc.Index)->getOutputIndex();
+ return getFunctionSymbol(Reloc.Index)->getFunctionIndex();
case R_WEBASSEMBLY_GLOBAL_INDEX_LEB:
- return getGlobalSymbol(Reloc.Index)->getOutputIndex();
+ return getGlobalSymbol(Reloc.Index)->getGlobalIndex();
default:
llvm_unreachable("unknown relocation type");
}
@@ -104,6 +129,22 @@
Bin.release();
WasmObj.reset(Obj);
+ // Build up a map of function indices to table indices for use when
+ // verifying the existing table index relocations
+ uint32_t TotalFunctions =
+ WasmObj->getNumImportedFunctions() + WasmObj->functions().size();
+ TableEntries.resize(TotalFunctions);
+ for (const WasmElemSegment &Seg : WasmObj->elements()) {
+ if (Seg.Offset.Opcode != WASM_OPCODE_I32_CONST)
+ fatal(toString(this) + ": invalid table elements");
+ uint32_t Offset = Seg.Offset.Value.Int32;
+ for (uint32_t Index = 0; Index < Seg.Functions.size(); Index++) {
+
+ uint32_t FunctionIndex = Seg.Functions[Index];
+ TableEntries[FunctionIndex] = Offset + Index;
+ }
+ }
+
// Find the code and data sections. Wasm objects can have at most one code
// and one data section.
for (const SectionRef &Sec : WasmObj->sections()) {
@@ -117,6 +158,11 @@
TypeMap.resize(getWasmObj()->types().size());
TypeIsUsed.resize(getWasmObj()->types().size(), false);
+ ArrayRef<StringRef> Comdats = WasmObj->linkingData().Comdats;
+ UsedComdats.resize(Comdats.size());
+ for (unsigned I = 0; I < Comdats.size(); ++I)
+ UsedComdats[I] = Symtab->addComdat(Comdats[I]);
+
// Populate `Segments`.
for (const WasmSegment &S : WasmObj->dataSegments()) {
InputSegment *Seg = make<InputSegment>(S, this);
@@ -153,10 +199,10 @@
}
bool ObjFile::isExcludedByComdat(InputChunk *Chunk) const {
- StringRef S = Chunk->getComdat();
- if (S.empty())
+ uint32_t C = Chunk->getComdat();
+ if (C == UINT32_MAX)
return false;
- return !Symtab->addComdat(S, this);
+ return !UsedComdats[C];
}
FunctionSymbol *ObjFile::getFunctionSymbol(uint32_t Index) const {
diff --git a/wasm/InputFiles.h b/wasm/InputFiles.h
index 97d41d8..f0fd267 100644
--- a/wasm/InputFiles.h
+++ b/wasm/InputFiles.h
@@ -25,8 +25,8 @@
using llvm::object::WasmSymbol;
using llvm::wasm::WasmGlobal;
using llvm::wasm::WasmImport;
-using llvm::wasm::WasmSignature;
using llvm::wasm::WasmRelocation;
+using llvm::wasm::WasmSignature;
namespace lld {
namespace wasm {
@@ -94,12 +94,17 @@
uint32_t calcNewIndex(const WasmRelocation &Reloc) const;
uint32_t calcNewValue(const WasmRelocation &Reloc) const;
+ uint32_t calcExpectedValue(const WasmRelocation &Reloc) const;
const WasmSection *CodeSection = nullptr;
const WasmSection *DataSection = nullptr;
+ // Maps input type indices to output type indices
std::vector<uint32_t> TypeMap;
std::vector<bool> TypeIsUsed;
+ // Maps function indices to table indices
+ std::vector<uint32_t> TableEntries;
+ std::vector<bool> UsedComdats;
std::vector<InputSegment *> Segments;
std::vector<InputFunction *> Functions;
std::vector<InputGlobal *> Globals;
diff --git a/wasm/InputGlobal.h b/wasm/InputGlobal.h
index 53af220..16a6b7d 100644
--- a/wasm/InputGlobal.h
+++ b/wasm/InputGlobal.h
@@ -30,11 +30,11 @@
const WasmGlobalType &getType() const { return Global.Type; }
- uint32_t getOutputIndex() const { return OutputIndex.getValue(); }
- bool hasOutputIndex() const { return OutputIndex.hasValue(); }
- void setOutputIndex(uint32_t Index) {
- assert(!hasOutputIndex());
- OutputIndex = Index;
+ uint32_t getGlobalIndex() const { return GlobalIndex.getValue(); }
+ bool hasGlobalIndex() const { return GlobalIndex.hasValue(); }
+ void setGlobalIndex(uint32_t Index) {
+ assert(!hasGlobalIndex());
+ GlobalIndex = Index;
}
bool Live = false;
@@ -42,7 +42,7 @@
WasmGlobal Global;
protected:
- llvm::Optional<uint32_t> OutputIndex;
+ llvm::Optional<uint32_t> GlobalIndex;
};
} // namespace wasm
diff --git a/wasm/MarkLive.cpp b/wasm/MarkLive.cpp
index 22211c1..9b72697 100644
--- a/wasm/MarkLive.cpp
+++ b/wasm/MarkLive.cpp
@@ -73,8 +73,25 @@
InputChunk *C = Q.pop_back_val();
for (const WasmRelocation Reloc : C->getRelocations()) {
- if (Reloc.Type != R_WEBASSEMBLY_TYPE_INDEX_LEB)
- Enqueue(C->File->getSymbol(Reloc.Index));
+ if (Reloc.Type == R_WEBASSEMBLY_TYPE_INDEX_LEB)
+ continue;
+ Symbol *Sym = C->File->getSymbol(Reloc.Index);
+
+ // If the function has been assigned the special index zero in the table,
+ // the relocation doesn't pull in the function body, since the function
+ // won't actually go in the table (the runtime will trap attempts to call
+ // that index, since we don't use it). A function with a table index of
+ // zero is only reachable via "call", not via "call_indirect". The stub
+ // functions used for weak-undefined symbols have this behaviour (compare
+ // equal to null pointer, only reachable via direct call).
+ if (Reloc.Type == R_WEBASSEMBLY_TABLE_INDEX_SLEB ||
+ Reloc.Type == R_WEBASSEMBLY_TABLE_INDEX_I32) {
+ FunctionSymbol *FuncSym = cast<FunctionSymbol>(Sym);
+ if (FuncSym->hasTableIndex() && FuncSym->getTableIndex() == 0)
+ continue;
+ }
+
+ Enqueue(Sym);
}
}
diff --git a/wasm/Options.td b/wasm/Options.td
index 6260d4d..a68e350 100644
--- a/wasm/Options.td
+++ b/wasm/Options.td
@@ -11,6 +11,11 @@
def _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
}
+multiclass B<string name, string help1, string help2> {
+ def NAME: Flag<["--", "-"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--", "-"], "no-" # name>, HelpText<help2>;
+}
+
// The follow flags are shared with the ELF linker
def color_diagnostics: F<"color-diagnostics">,
HelpText<"Use colors in diagnostics">;
@@ -18,6 +23,10 @@
def color_diagnostics_eq: J<"color-diagnostics=">,
HelpText<"Use colors in diagnostics">;
+defm demangle: B<"demangle",
+ "Demangle symbol names",
+ "Do not demangle symbol names">;
+
def entry: S<"entry">, MetaVarName<"<entry>">,
HelpText<"Name of entry point symbol">;
@@ -27,8 +36,9 @@
def fatal_warnings: F<"fatal-warnings">,
HelpText<"Treat warnings as errors">;
-def gc_sections: F<"gc-sections">,
- HelpText<"Enable garbage collection of unused sections">;
+defm gc_sections: B<"gc-sections",
+ "Enable garbage collection of unused sections",
+ "Disable garbage collection of unused sections">;
def help: F<"help">, HelpText<"Print option help">;
@@ -48,17 +58,12 @@
def no_fatal_warnings: F<"no-fatal-warnings">;
-def no_gc_sections: F<"no-gc-sections">,
- HelpText<"Disable garbage collection of unused sections">;
-
-def no_print_gc_sections: F<"no-print-gc-sections">,
- HelpText<"Do not list removed unused sections">;
-
def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
HelpText<"Path to file to write output">;
-def print_gc_sections: F<"print-gc-sections">,
- HelpText<"List removed unused sections">;
+defm print_gc_sections: B<"print-gc-sections",
+ "List removed unused sections",
+ "Do not list removed unused sections">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
@@ -91,27 +96,31 @@
def allow_undefined_file_s: Separate<["-"], "allow-undefined-file">,
Alias<allow_undefined_file>;
-def check_signatures: F<"check-signatures">,
- HelpText<"Check function signatures">;
+defm check_signatures: B<"check-signatures",
+ "Check function signatures",
+ "Don't check function signatures">;
defm export: Eq<"export">,
HelpText<"Force a symbol to be exported">;
+def export_table: F<"export-table">,
+ HelpText<"Export function table to the environment">;
+
def global_base: J<"global-base=">,
HelpText<"Where to start to place global data">;
def import_memory: F<"import-memory">,
HelpText<"Import memory from the environment">;
+def import_table: F<"import-table">,
+ HelpText<"Import function table from the environment">;
+
def initial_memory: J<"initial-memory=">,
HelpText<"Initial size of the linear memory">;
def max_memory: J<"max-memory=">,
HelpText<"Maximum size of the linear memory">;
-def no_check_signatures: F<"no-check-signatures">,
- HelpText<"Don't check function signatures">;
-
def no_entry: F<"no-entry">,
HelpText<"Do not output any entry point">;
diff --git a/wasm/OutputSections.cpp b/wasm/OutputSections.cpp
index 915f0aa..9683a2d 100644
--- a/wasm/OutputSections.cpp
+++ b/wasm/OutputSections.cpp
@@ -108,9 +108,8 @@
memcpy(Buf, CodeSectionHeader.data(), CodeSectionHeader.size());
// Write code section bodies
- parallelForEach(Functions, [&](const InputChunk *Chunk) {
- Chunk->writeTo(Buf);
- });
+ parallelForEach(Functions,
+ [&](const InputChunk *Chunk) { Chunk->writeTo(Buf); });
}
uint32_t CodeSection::numRelocations() const {
diff --git a/wasm/SymbolTable.cpp b/wasm/SymbolTable.cpp
index 6375358..153b507 100644
--- a/wasm/SymbolTable.cpp
+++ b/wasm/SymbolTable.cpp
@@ -119,11 +119,13 @@
}
DefinedFunction *SymbolTable::addSyntheticFunction(StringRef Name,
- const WasmSignature *Type,
- uint32_t Flags) {
+ uint32_t Flags,
+ InputFunction *Function) {
DEBUG(dbgs() << "addSyntheticFunction: " << Name << "\n");
assert(!find(Name));
- return replaceSymbol<DefinedFunction>(insert(Name).first, Name, Flags, Type);
+ SyntheticFunctions.emplace_back(Function);
+ return replaceSymbol<DefinedFunction>(insert(Name).first, Name, Flags,
+ nullptr, Function);
}
DefinedData *SymbolTable::addSyntheticDataSymbol(StringRef Name,
@@ -137,6 +139,7 @@
InputGlobal *Global) {
DEBUG(dbgs() << "addSyntheticGlobal: " << Name << " -> " << Global << "\n");
assert(!find(Name));
+ SyntheticGlobals.emplace_back(Global);
return replaceSymbol<DefinedGlobal>(insert(Name).first, Name, Flags, nullptr,
Global);
}
@@ -301,6 +304,6 @@
}
}
-bool SymbolTable::addComdat(StringRef Name, const ObjFile *File) {
- return Comdats.insert({Name, File}).first->second == File;
+bool SymbolTable::addComdat(StringRef Name) {
+ return Comdats.insert(CachedHashStringRef(Name)).second;
}
diff --git a/wasm/SymbolTable.h b/wasm/SymbolTable.h
index 466ed10..6fb5c15 100644
--- a/wasm/SymbolTable.h
+++ b/wasm/SymbolTable.h
@@ -13,11 +13,11 @@
#include "InputFiles.h"
#include "Symbols.h"
#include "llvm/ADT/CachedHashString.h"
-#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/Support/raw_ostream.h"
-using llvm::wasm::WasmSignature;
using llvm::wasm::WasmGlobalType;
+using llvm::wasm::WasmSignature;
namespace lld {
namespace wasm {
@@ -41,6 +41,8 @@
void addFile(InputFile *File);
std::vector<ObjFile *> ObjectFiles;
+ std::vector<InputFunction *> SyntheticFunctions;
+ std::vector<InputGlobal *> SyntheticGlobals;
void reportRemainingUndefines();
@@ -63,14 +65,13 @@
void addLazy(ArchiveFile *F, const Archive::Symbol *Sym);
- bool addComdat(StringRef Name, const ObjFile *File);
+ bool addComdat(StringRef Name);
DefinedData *addSyntheticDataSymbol(StringRef Name, uint32_t Flags);
DefinedGlobal *addSyntheticGlobal(StringRef Name, uint32_t Flags,
InputGlobal *Global);
- DefinedFunction *addSyntheticFunction(StringRef Name,
- const WasmSignature *Type,
- uint32_t Flags);
+ DefinedFunction *addSyntheticFunction(StringRef Name, uint32_t Flags,
+ InputFunction *Function);
private:
std::pair<Symbol *, bool> insert(StringRef Name);
@@ -78,7 +79,7 @@
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
std::vector<Symbol *> SymVector;
- llvm::DenseMap<StringRef, const ObjFile *> Comdats;
+ llvm::DenseSet<llvm::CachedHashStringRef> Comdats;
};
extern SymbolTable *Symtab;
diff --git a/wasm/Symbols.cpp b/wasm/Symbols.cpp
index 742c887..bcebcdc 100644
--- a/wasm/Symbols.cpp
+++ b/wasm/Symbols.cpp
@@ -39,28 +39,6 @@
llvm_unreachable("invalid symbol kind");
}
-bool Symbol::hasOutputIndex() const {
- if (auto *F = dyn_cast<DefinedFunction>(this))
- if (F->Function)
- return F->Function->hasOutputIndex();
- if (auto *G = dyn_cast<DefinedGlobal>(this))
- if (G->Global)
- return G->Global->hasOutputIndex();
- return OutputIndex != INVALID_INDEX;
-}
-
-uint32_t Symbol::getOutputIndex() const {
- assert(!isa<DataSymbol>(this));
- if (auto *F = dyn_cast<DefinedFunction>(this))
- if (F->Function)
- return F->Function->getOutputIndex();
- if (auto *G = dyn_cast<DefinedGlobal>(this))
- if (G->Global)
- return G->Global->getOutputIndex();
- assert(OutputIndex != INVALID_INDEX);
- return OutputIndex;
-}
-
InputChunk *Symbol::getChunk() const {
if (auto *F = dyn_cast<DefinedFunction>(this))
return F->Function;
@@ -89,13 +67,6 @@
OutputSymbolIndex = Index;
}
-void Symbol::setOutputIndex(uint32_t Index) {
- DEBUG(dbgs() << "setOutputIndex " << Name << " -> " << Index << "\n");
- assert(!isa<DataSymbol>(this));
- assert(OutputIndex == INVALID_INDEX);
- OutputIndex = Index;
-}
-
bool Symbol::isWeak() const {
return (Flags & WASM_SYMBOL_BINDING_MASK) == WASM_SYMBOL_BINDING_WEAK;
}
@@ -117,6 +88,25 @@
Flags |= WASM_SYMBOL_VISIBILITY_DEFAULT;
}
+uint32_t FunctionSymbol::getFunctionIndex() const {
+ if (auto *F = dyn_cast<DefinedFunction>(this))
+ return F->Function->getFunctionIndex();
+ assert(FunctionIndex != INVALID_INDEX);
+ return FunctionIndex;
+}
+
+void FunctionSymbol::setFunctionIndex(uint32_t Index) {
+ DEBUG(dbgs() << "setFunctionIndex " << Name << " -> " << Index << "\n");
+ assert(FunctionIndex == INVALID_INDEX);
+ FunctionIndex = Index;
+}
+
+bool FunctionSymbol::hasFunctionIndex() const {
+ if (auto *F = dyn_cast<DefinedFunction>(this))
+ return F->Function->hasFunctionIndex();
+ return FunctionIndex != INVALID_INDEX;
+}
+
uint32_t FunctionSymbol::getTableIndex() const {
if (auto *F = dyn_cast<DefinedFunction>(this))
return F->Function->getTableIndex();
@@ -172,6 +162,25 @@
return Segment->OutputSeg->Index;
}
+uint32_t GlobalSymbol::getGlobalIndex() const {
+ if (auto *F = dyn_cast<DefinedGlobal>(this))
+ return F->Global->getGlobalIndex();
+ assert(GlobalIndex != INVALID_INDEX);
+ return GlobalIndex;
+}
+
+void GlobalSymbol::setGlobalIndex(uint32_t Index) {
+ DEBUG(dbgs() << "setGlobalIndex " << Name << " -> " << Index << "\n");
+ assert(GlobalIndex == INVALID_INDEX);
+ GlobalIndex = Index;
+}
+
+bool GlobalSymbol::hasGlobalIndex() const {
+ if (auto *F = dyn_cast<DefinedGlobal>(this))
+ return F->Global->hasGlobalIndex();
+ return GlobalIndex != INVALID_INDEX;
+}
+
DefinedGlobal::DefinedGlobal(StringRef Name, uint32_t Flags, InputFile *File,
InputGlobal *Global)
: GlobalSymbol(Name, DefinedGlobalKind, Flags, File,
@@ -183,7 +192,7 @@
std::string lld::toString(const wasm::Symbol &Sym) {
if (Config->Demangle)
if (Optional<std::string> S = demangleItanium(Sym.getName()))
- return "`" + *S + "'";
+ return *S;
return Sym.getName();
}
diff --git a/wasm/Symbols.h b/wasm/Symbols.h
index 90e62e5..92084f7 100644
--- a/wasm/Symbols.h
+++ b/wasm/Symbols.h
@@ -16,9 +16,9 @@
using llvm::object::Archive;
using llvm::object::WasmSymbol;
-using llvm::wasm::WasmSignature;
using llvm::wasm::WasmGlobal;
using llvm::wasm::WasmGlobalType;
+using llvm::wasm::WasmSignature;
using llvm::wasm::WasmSymbolType;
namespace lld {
@@ -48,8 +48,8 @@
Kind kind() const { return SymbolKind; }
bool isDefined() const {
- return SymbolKind == DefinedFunctionKind ||
- SymbolKind == DefinedDataKind || SymbolKind == DefinedGlobalKind;
+ return SymbolKind == DefinedFunctionKind || SymbolKind == DefinedDataKind ||
+ SymbolKind == DefinedGlobalKind;
}
bool isUndefined() const {
@@ -76,18 +76,8 @@
void setHidden(bool IsHidden);
- uint32_t getOutputIndex() const;
-
- // Returns true if an output index has been set for this symbol
- bool hasOutputIndex() const;
-
- // Set the output index of the symbol, in the Wasm index space of the output
- // object - that is, for defined symbols only, its position in the list of
- // Wasm imports+code for functions, imports+globals for globals.
- void setOutputIndex(uint32_t Index);
-
- // Get/set the output symbol index, in the Symbol index space. This is
- // only used for relocatable output.
+ // Get/set the index in the output symbol table. This is only used for
+ // relocatable output.
uint32_t getOutputSymbolIndex() const;
void setOutputSymbolIndex(uint32_t Index);
@@ -101,7 +91,6 @@
Kind SymbolKind;
uint32_t Flags;
InputFile *File;
- uint32_t OutputIndex = INVALID_INDEX;
uint32_t OutputSymbolIndex = INVALID_INDEX;
};
@@ -114,13 +103,15 @@
const WasmSignature *getFunctionType() const { return FunctionType; }
+ // Get/set the table index
+ void setTableIndex(uint32_t Index);
uint32_t getTableIndex() const;
-
- // Returns true if a table index has been set for this symbol
bool hasTableIndex() const;
- // Set the table index of the symbol
- void setTableIndex(uint32_t Index);
+ // Get/set the function index
+ uint32_t getFunctionIndex() const;
+ void setFunctionIndex(uint32_t Index);
+ bool hasFunctionIndex() const;
protected:
FunctionSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F,
@@ -128,20 +119,16 @@
: Symbol(Name, K, Flags, F), FunctionType(Type) {}
uint32_t TableIndex = INVALID_INDEX;
+ uint32_t FunctionIndex = INVALID_INDEX;
const WasmSignature *FunctionType;
};
class DefinedFunction : public FunctionSymbol {
public:
- // Primary constructor for file-defined functions.
DefinedFunction(StringRef Name, uint32_t Flags, InputFile *F,
InputFunction *Function);
- // Second constructor used when creating synthetic functions.
- DefinedFunction(StringRef Name, uint32_t Flags, const WasmSignature *Type)
- : FunctionSymbol(Name, DefinedFunctionKind, Flags, nullptr, Type) {}
-
static bool classof(const Symbol *S) {
return S->kind() == DefinedFunctionKind;
}
@@ -218,6 +205,11 @@
const WasmGlobalType *getGlobalType() const { return GlobalType; }
+ // Get/set the global index
+ uint32_t getGlobalIndex() const;
+ void setGlobalIndex(uint32_t Index);
+ bool hasGlobalIndex() const;
+
protected:
GlobalSymbol(StringRef Name, Kind K, uint32_t Flags, InputFile *F,
const WasmGlobalType *GlobalType)
@@ -226,6 +218,7 @@
// Explicit function type, needed for undefined or synthetic functions only.
// For regular defined globals this information comes from the InputChunk.
const WasmGlobalType *GlobalType;
+ uint32_t GlobalIndex = INVALID_INDEX;
};
class DefinedGlobal : public GlobalSymbol {
diff --git a/wasm/Writer.cpp b/wasm/Writer.cpp
index 550372a..a98cfc6 100644
--- a/wasm/Writer.cpp
+++ b/wasm/Writer.cpp
@@ -17,9 +17,11 @@
#include "WriterUtils.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
+#include "lld/Common/Strings.h"
#include "lld/Common/Threads.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/BinaryFormat/Wasm.h"
+#include "llvm/Object/WasmTraits.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
@@ -37,36 +39,14 @@
static constexpr int kStackAlignment = 16;
static constexpr int kInitialTableOffset = 1;
+static constexpr const char *kFunctionTableName = "__indirect_function_table";
namespace {
-// Traits for using WasmSignature in a DenseMap.
-struct WasmSignatureDenseMapInfo {
- static WasmSignature getEmptyKey() {
- WasmSignature Sig;
- Sig.ReturnType = 1;
- return Sig;
- }
- static WasmSignature getTombstoneKey() {
- WasmSignature Sig;
- Sig.ReturnType = 2;
- return Sig;
- }
- static unsigned getHashValue(const WasmSignature &Sig) {
- unsigned H = hash_value(Sig.ReturnType);
- for (int32_t Param : Sig.ParamTypes)
- H = hash_combine(H, Param);
- return H;
- }
- static bool isEqual(const WasmSignature &LHS, const WasmSignature &RHS) {
- return LHS == RHS;
- }
-};
-
// An init entry to be written to either the synthetic init func or the
// linking metadata.
struct WasmInitEntry {
- const Symbol *Sym;
+ const FunctionSymbol *Sym;
uint32_t Priority;
};
@@ -92,8 +72,7 @@
void layoutMemory();
void createHeader();
void createSections();
- SyntheticSection *createSyntheticSection(uint32_t Type,
- StringRef Name = "");
+ SyntheticSection *createSyntheticSection(uint32_t Type, StringRef Name = "");
// Builtin sections
void createTypeSection();
@@ -117,9 +96,10 @@
uint64_t FileSize = 0;
uint32_t NumMemoryPages = 0;
+ uint32_t MaxMemoryPages = 0;
std::vector<const WasmSignature *> Types;
- DenseMap<WasmSignature, int32_t, WasmSignatureDenseMapInfo> TypeIndices;
+ DenseMap<WasmSignature, int32_t> TypeIndices;
std::vector<const Symbol *> ImportedSymbols;
unsigned NumImportedFunctions = 0;
unsigned NumImportedGlobals = 0;
@@ -143,20 +123,12 @@
} // anonymous namespace
-static void debugPrint(const char *fmt, ...) {
- if (!errorHandler().Verbose)
- return;
- fprintf(stderr, "lld: ");
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
-
void Writer::createImportSection() {
uint32_t NumImports = ImportedSymbols.size();
if (Config->ImportMemory)
++NumImports;
+ if (Config->ImportTable)
+ ++NumImports;
if (NumImports == 0)
return;
@@ -173,6 +145,21 @@
Import.Kind = WASM_EXTERNAL_MEMORY;
Import.Memory.Flags = 0;
Import.Memory.Initial = NumMemoryPages;
+ if (MaxMemoryPages != 0) {
+ Import.Memory.Flags |= WASM_LIMITS_FLAG_HAS_MAX;
+ Import.Memory.Maximum = MaxMemoryPages;
+ }
+ writeImport(OS, Import);
+ }
+
+ if (Config->ImportTable) {
+ uint32_t TableSize = kInitialTableOffset + IndirectFunctions.size();
+ WasmImport Import;
+ Import.Module = "env";
+ Import.Field = kFunctionTableName;
+ Import.Kind = WASM_EXTERNAL_TABLE;
+ Import.Table.ElemType = WASM_TYPE_ANYFUNC;
+ Import.Table.Limits = {WASM_LIMITS_FLAG_HAS_MAX, TableSize, TableSize};
writeImport(OS, Import);
}
@@ -219,9 +206,13 @@
SyntheticSection *Section = createSyntheticSection(WASM_SEC_MEMORY);
raw_ostream &OS = Section->getStream();
+ bool HasMax = MaxMemoryPages != 0;
writeUleb128(OS, 1, "memory count");
- writeUleb128(OS, 0, "memory limits flags");
+ writeUleb128(OS, HasMax ? static_cast<unsigned>(WASM_LIMITS_FLAG_HAS_MAX) : 0,
+ "memory limits flags");
writeUleb128(OS, NumMemoryPages, "initial pages");
+ if (HasMax)
+ writeUleb128(OS, MaxMemoryPages, "max pages");
}
void Writer::createGlobalSection() {
@@ -245,8 +236,11 @@
}
void Writer::createTableSection() {
- // Always output a table section, even if there are no indirect calls.
- // There are two reasons for this:
+ if (Config->ImportTable)
+ return;
+
+ // Always output a table section (or table import), even if there are no
+ // indirect calls. There are two reasons for this:
// 1. For executables it is useful to have an empty table slot at 0
// which can be filled with a null function call handler.
// 2. If we don't do this, any program that contains a call_indirect but
@@ -259,16 +253,16 @@
raw_ostream &OS = Section->getStream();
writeUleb128(OS, 1, "table count");
- writeU8(OS, WASM_TYPE_ANYFUNC, "table type");
- writeUleb128(OS, WASM_LIMITS_FLAG_HAS_MAX, "table flags");
- writeUleb128(OS, TableSize, "table initial size");
- writeUleb128(OS, TableSize, "table max size");
+ WasmLimits Limits = {WASM_LIMITS_FLAG_HAS_MAX, TableSize, TableSize};
+ writeTableType(OS, WasmTable{WASM_TYPE_ANYFUNC, Limits});
}
void Writer::createExportSection() {
bool ExportMemory = !Config->Relocatable && !Config->ImportMemory;
+ bool ExportTable = !Config->Relocatable && Config->ExportTable;
- uint32_t NumExports = (ExportMemory ? 1 : 0) + ExportedSymbols.size();
+ uint32_t NumExports =
+ (ExportMemory ? 1 : 0) + (ExportTable ? 1 : 0) + ExportedSymbols.size();
if (!NumExports)
return;
@@ -279,6 +273,8 @@
if (ExportMemory)
writeExport(OS, {"memory", WASM_EXTERNAL_MEMORY, 0});
+ if (ExportTable)
+ writeExport(OS, {kFunctionTableName, WASM_EXTERNAL_TABLE, 0});
unsigned FakeGlobalIndex = NumImportedGlobals + InputGlobals.size();
@@ -287,10 +283,10 @@
WasmExport Export;
DEBUG(dbgs() << "Export: " << Name << "\n");
- if (isa<DefinedFunction>(Sym))
- Export = {Name, WASM_EXTERNAL_FUNCTION, Sym->getOutputIndex()};
- else if (isa<DefinedGlobal>(Sym))
- Export = {Name, WASM_EXTERNAL_GLOBAL, Sym->getOutputIndex()};
+ if (auto *F = dyn_cast<DefinedFunction>(Sym))
+ Export = {Name, WASM_EXTERNAL_FUNCTION, F->getFunctionIndex()};
+ else if (auto *G = dyn_cast<DefinedGlobal>(Sym))
+ Export = {Name, WASM_EXTERNAL_GLOBAL, G->getGlobalIndex()};
else if (isa<DefinedData>(Sym))
Export = {Name, WASM_EXTERNAL_GLOBAL, FakeGlobalIndex++};
else
@@ -317,7 +313,7 @@
uint32_t TableIndex = kInitialTableOffset;
for (const FunctionSymbol *Sym : IndirectFunctions) {
assert(Sym->getTableIndex() == TableIndex);
- writeUleb128(OS, Sym->getOutputIndex(), "function index");
+ writeUleb128(OS, Sym->getFunctionIndex(), "function index");
++TableIndex;
}
}
@@ -428,14 +424,16 @@
writeU8(Sub.OS, Kind, "sym kind");
writeUleb128(Sub.OS, Flags, "sym flags");
- switch (Kind) {
- case llvm::wasm::WASM_SYMBOL_TYPE_FUNCTION:
- case llvm::wasm::WASM_SYMBOL_TYPE_GLOBAL:
- writeUleb128(Sub.OS, Sym->getOutputIndex(), "index");
+ if (auto *F = dyn_cast<FunctionSymbol>(Sym)) {
+ writeUleb128(Sub.OS, F->getFunctionIndex(), "index");
if (Sym->isDefined())
writeStr(Sub.OS, Sym->getName(), "sym name");
- break;
- case llvm::wasm::WASM_SYMBOL_TYPE_DATA:
+ } else if (auto *G = dyn_cast<GlobalSymbol>(Sym)) {
+ writeUleb128(Sub.OS, G->getGlobalIndex(), "index");
+ if (Sym->isDefined())
+ writeStr(Sub.OS, Sym->getName(), "sym name");
+ } else {
+ assert(isa<DataSymbol>(Sym));
writeStr(Sub.OS, Sym->getName(), "sym name");
if (auto *DataSym = dyn_cast<DefinedData>(Sym)) {
writeUleb128(Sub.OS, DataSym->getOutputSegmentIndex(), "index");
@@ -443,7 +441,6 @@
"data offset");
writeUleb128(Sub.OS, DataSym->getSize(), "data size");
}
- break;
}
}
@@ -471,23 +468,26 @@
Sub.writeTo(OS);
}
- struct ComdatEntry { unsigned Kind; uint32_t Index; };
- std::map<StringRef,std::vector<ComdatEntry>> Comdats;
+ struct ComdatEntry {
+ unsigned Kind;
+ uint32_t Index;
+ };
+ std::map<StringRef, std::vector<ComdatEntry>> Comdats;
for (const InputFunction *F : InputFunctions) {
- StringRef Comdat = F->getComdat();
+ StringRef Comdat = F->getComdatName();
if (!Comdat.empty())
Comdats[Comdat].emplace_back(
- ComdatEntry{WASM_COMDAT_FUNCTION, F->getOutputIndex()});
+ ComdatEntry{WASM_COMDAT_FUNCTION, F->getFunctionIndex()});
}
for (uint32_t I = 0; I < Segments.size(); ++I) {
const auto &InputSegments = Segments[I]->InputSegments;
if (InputSegments.empty())
continue;
- StringRef Comdat = InputSegments[0]->getComdat();
+ StringRef Comdat = InputSegments[0]->getComdatName();
#ifndef NDEBUG
for (const InputSegment *IS : InputSegments)
- assert(IS->getComdat() == Comdat);
+ assert(IS->getComdatName() == Comdat);
#endif
if (!Comdat.empty())
Comdats[Comdat].emplace_back(ComdatEntry{WASM_COMDAT_DATA, I});
@@ -528,15 +528,17 @@
// and InputFunctions are numbered in order with imported functions coming
// first.
for (const Symbol *S : ImportedSymbols) {
- if (!isa<FunctionSymbol>(S))
- continue;
- writeUleb128(Sub.OS, S->getOutputIndex(), "import index");
- writeStr(Sub.OS, S->getName(), "symbol name");
+ if (auto *F = dyn_cast<FunctionSymbol>(S)) {
+ writeUleb128(Sub.OS, F->getFunctionIndex(), "func index");
+ Optional<std::string> Name = demangleItanium(F->getName());
+ writeStr(Sub.OS, Name ? StringRef(*Name) : F->getName(), "symbol name");
+ }
}
for (const InputFunction *F : InputFunctions) {
if (!F->getName().empty()) {
- writeUleb128(Sub.OS, F->getOutputIndex(), "func index");
- writeStr(Sub.OS, F->getName(), "symbol name");
+ writeUleb128(Sub.OS, F->getFunctionIndex(), "func index");
+ Optional<std::string> Name = demangleItanium(F->getName());
+ writeStr(Sub.OS, Name ? StringRef(*Name) : F->getName(), "symbol name");
}
}
@@ -562,7 +564,7 @@
void Writer::layoutMemory() {
uint32_t MemoryPtr = 0;
MemoryPtr = Config->GlobalBase;
- debugPrint("mem: global base = %d\n", Config->GlobalBase);
+ log("mem: global base = " + Twine(Config->GlobalBase));
createOutputSegments();
@@ -574,8 +576,8 @@
for (OutputSegment *Seg : Segments) {
MemoryPtr = alignTo(MemoryPtr, Seg->Alignment);
Seg->StartVA = MemoryPtr;
- debugPrint("mem: %-15s offset=%-8d size=%-8d align=%d\n",
- Seg->Name.str().c_str(), MemoryPtr, Seg->Size, Seg->Alignment);
+ log(formatv("mem: {0,-15} offset={1,-8} size={2,-8} align={3}", Seg->Name,
+ MemoryPtr, Seg->Size, Seg->Alignment));
MemoryPtr += Seg->Size;
}
@@ -583,29 +585,46 @@
if (WasmSym::DataEnd)
WasmSym::DataEnd->setVirtualAddress(MemoryPtr);
- debugPrint("mem: static data = %d\n", MemoryPtr - Config->GlobalBase);
+ log("mem: static data = " + Twine(MemoryPtr - Config->GlobalBase));
// Stack comes after static data and bss
if (!Config->Relocatable) {
MemoryPtr = alignTo(MemoryPtr, kStackAlignment);
if (Config->ZStackSize != alignTo(Config->ZStackSize, kStackAlignment))
error("stack size must be " + Twine(kStackAlignment) + "-byte aligned");
- debugPrint("mem: stack size = %d\n", Config->ZStackSize);
- debugPrint("mem: stack base = %d\n", MemoryPtr);
+ log("mem: stack size = " + Twine(Config->ZStackSize));
+ log("mem: stack base = " + Twine(MemoryPtr));
MemoryPtr += Config->ZStackSize;
WasmSym::StackPointer->Global->Global.InitExpr.Value.Int32 = MemoryPtr;
- debugPrint("mem: stack top = %d\n", MemoryPtr);
+ log("mem: stack top = " + Twine(MemoryPtr));
// Set `__heap_base` to directly follow the end of the stack. We don't
// allocate any heap memory up front, but instead really on the malloc/brk
// implementation growing the memory at runtime.
WasmSym::HeapBase->setVirtualAddress(MemoryPtr);
- debugPrint("mem: heap base = %d\n", MemoryPtr);
+ log("mem: heap base = " + Twine(MemoryPtr));
}
+ if (Config->InitialMemory != 0) {
+ if (Config->InitialMemory != alignTo(Config->InitialMemory, WasmPageSize))
+ error("initial memory must be " + Twine(WasmPageSize) + "-byte aligned");
+ if (MemoryPtr > Config->InitialMemory)
+ error("initial memory too small, " + Twine(MemoryPtr) + " bytes needed");
+ else
+ MemoryPtr = Config->InitialMemory;
+ }
uint32_t MemSize = alignTo(MemoryPtr, WasmPageSize);
NumMemoryPages = MemSize / WasmPageSize;
- debugPrint("mem: total pages = %d\n", NumMemoryPages);
+ log("mem: total pages = " + Twine(NumMemoryPages));
+
+ if (Config->MaxMemory != 0) {
+ if (Config->MaxMemory != alignTo(Config->MaxMemory, WasmPageSize))
+ error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned");
+ if (MemoryPtr > Config->MaxMemory)
+ error("maximum memory too small, " + Twine(MemoryPtr) + " bytes needed");
+ MaxMemoryPages = Config->MaxMemory / WasmPageSize;
+ log("mem: max pages = " + Twine(MaxMemoryPages));
+ }
}
SyntheticSection *Writer::createSyntheticSection(uint32_t Type,
@@ -654,12 +673,11 @@
continue;
DEBUG(dbgs() << "import: " << Sym->getName() << "\n");
- Sym->setOutputIndex(ImportedSymbols.size());
ImportedSymbols.emplace_back(Sym);
- if (isa<FunctionSymbol>(Sym))
- ++NumImportedFunctions;
+ if (auto *F = dyn_cast<FunctionSymbol>(Sym))
+ F->setFunctionIndex(NumImportedFunctions++);
else
- ++NumImportedGlobals;
+ cast<GlobalSymbol>(Sym)->setGlobalIndex(NumImportedGlobals++);
}
}
@@ -693,8 +711,9 @@
for (Symbol *Sym : File->getSymbols()) {
if (Sym->getFile() != File)
continue;
- if (!Sym->isLive())
- return;
+ // (Since this is relocatable output, GC is not performed so symbols must
+ // be live.)
+ assert(Sym->isLive());
Sym->setOutputSymbolIndex(SymbolIndex++);
SymtabEntries.emplace_back(Sym);
}
@@ -746,14 +765,20 @@
void Writer::assignIndexes() {
uint32_t FunctionIndex = NumImportedFunctions + InputFunctions.size();
+ auto AddDefinedFunction = [&](InputFunction *Func) {
+ if (!Func->Live)
+ return;
+ InputFunctions.emplace_back(Func);
+ Func->setFunctionIndex(FunctionIndex++);
+ };
+
+ for (InputFunction *Func : Symtab->SyntheticFunctions)
+ AddDefinedFunction(Func);
+
for (ObjFile *File : Symtab->ObjectFiles) {
DEBUG(dbgs() << "Functions: " << File->getName() << "\n");
- for (InputFunction *Func : File->Functions) {
- if (!Func->Live)
- continue;
- InputFunctions.emplace_back(Func);
- Func->setOutputIndex(FunctionIndex++);
- }
+ for (InputFunction *Func : File->Functions)
+ AddDefinedFunction(Func);
}
uint32_t TableIndex = kInitialTableOffset;
@@ -766,7 +791,7 @@
if (Reloc.Type == R_WEBASSEMBLY_TABLE_INDEX_I32 ||
Reloc.Type == R_WEBASSEMBLY_TABLE_INDEX_SLEB) {
FunctionSymbol *Sym = File->getFunctionSymbol(Reloc.Index);
- if (Sym->hasTableIndex() || !Sym->hasOutputIndex())
+ if (Sym->hasTableIndex() || !Sym->hasFunctionIndex())
continue;
Sym->setTableIndex(TableIndex++);
IndirectFunctions.emplace_back(Sym);
@@ -797,13 +822,13 @@
auto AddDefinedGlobal = [&](InputGlobal *Global) {
if (Global->Live) {
DEBUG(dbgs() << "AddDefinedGlobal: " << GlobalIndex << "\n");
- Global->setOutputIndex(GlobalIndex++);
+ Global->setGlobalIndex(GlobalIndex++);
InputGlobals.push_back(Global);
}
};
- if (WasmSym::StackPointer)
- AddDefinedGlobal(WasmSym::StackPointer->Global);
+ for (InputGlobal *Global : Symtab->SyntheticGlobals)
+ AddDefinedGlobal(Global);
for (ObjFile *File : Symtab->ObjectFiles) {
DEBUG(dbgs() << "Globals: " << File->getName() << "\n");
@@ -848,9 +873,6 @@
// Create synthetic "__wasm_call_ctors" function based on ctor functions
// in input object.
void Writer::createCtorFunction() {
- uint32_t FunctionIndex = NumImportedFunctions + InputFunctions.size();
- WasmSym::CallCtors->setOutputIndex(FunctionIndex);
-
// First write the body's contents to a string.
std::string BodyContent;
{
@@ -858,7 +880,7 @@
writeUleb128(OS, 0, "num locals");
for (const WasmInitEntry &F : InitFunctions) {
writeU8(OS, OPCODE_CALL, "CALL");
- writeUleb128(OS, F.Sym->getOutputIndex(), "function index");
+ writeUleb128(OS, F.Sym->getFunctionIndex(), "function index");
}
writeU8(OS, OPCODE_END, "END");
}
@@ -871,14 +893,8 @@
OS << BodyContent;
}
- const WasmSignature *Sig = WasmSym::CallCtors->getFunctionType();
- SyntheticFunction *F = make<SyntheticFunction>(
- *Sig, std::move(FunctionBody), WasmSym::CallCtors->getName());
-
- F->setOutputIndex(FunctionIndex);
- F->Live = true;
- WasmSym::CallCtors->Function = F;
- InputFunctions.emplace_back(F);
+ ArrayRef<uint8_t> Body = toArrayRef(Saver.save(FunctionBody));
+ cast<SyntheticFunction>(WasmSym::CallCtors->Function)->setBody(Body);
}
// Populate InitFunctions vector with init functions from all input objects.
diff --git a/wasm/WriterUtils.cpp b/wasm/WriterUtils.cpp
index e2494b5..4c0d09e 100644
--- a/wasm/WriterUtils.cpp
+++ b/wasm/WriterUtils.cpp
@@ -126,6 +126,11 @@
writeInitExpr(OS, Global.InitExpr);
}
+void wasm::writeTableType(raw_ostream &OS, const llvm::wasm::WasmTable &Type) {
+ writeU8(OS, WASM_TYPE_ANYFUNC, "table type");
+ writeLimits(OS, Type.Limits);
+}
+
void wasm::writeImport(raw_ostream &OS, const WasmImport &Import) {
writeStr(OS, Import.Module, "import module name");
writeStr(OS, Import.Field, "import field name");
@@ -140,6 +145,9 @@
case WASM_EXTERNAL_MEMORY:
writeLimits(OS, Import.Memory);
break;
+ case WASM_EXTERNAL_TABLE:
+ writeTableType(OS, Import.Table);
+ break;
default:
fatal("unsupported import type: " + Twine(Import.Kind));
}
@@ -158,6 +166,9 @@
case WASM_EXTERNAL_MEMORY:
writeUleb128(OS, Export.Index, "memory index");
break;
+ case WASM_EXTERNAL_TABLE:
+ writeUleb128(OS, Export.Index, "table index");
+ break;
default:
fatal("unsupported export type: " + Twine(Export.Kind));
}
@@ -174,6 +185,8 @@
return "F32";
case ValType::F64:
return "F64";
+ case ValType::EXCEPT_REF:
+ return "except_ref";
}
llvm_unreachable("Invalid wasm::ValType");
}
@@ -195,5 +208,5 @@
std::string lld::toString(const WasmGlobalType &Sig) {
return (Sig.Mutable ? "var " : "const ") +
- toString(static_cast<ValType>(Sig.Type));
+ toString(static_cast<ValType>(Sig.Type));
}
diff --git a/wasm/WriterUtils.h b/wasm/WriterUtils.h
index 6721ef4..74d727b 100644
--- a/wasm/WriterUtils.h
+++ b/wasm/WriterUtils.h
@@ -17,28 +17,6 @@
using llvm::raw_ostream;
-// Needed for WasmSignatureDenseMapInfo
-inline bool operator==(const llvm::wasm::WasmSignature &LHS,
- const llvm::wasm::WasmSignature &RHS) {
- return LHS.ReturnType == RHS.ReturnType && LHS.ParamTypes == RHS.ParamTypes;
-}
-
-inline bool operator!=(const llvm::wasm::WasmSignature &LHS,
- const llvm::wasm::WasmSignature &RHS) {
- return !(LHS == RHS);
-}
-
-// Used for general comparison
-inline bool operator==(const llvm::wasm::WasmGlobalType &LHS,
- const llvm::wasm::WasmGlobalType &RHS) {
- return LHS.Type == RHS.Type && LHS.Mutable == RHS.Mutable;
-}
-
-inline bool operator!=(const llvm::wasm::WasmGlobalType &LHS,
- const llvm::wasm::WasmGlobalType &RHS) {
- return !(LHS == RHS);
-}
-
namespace lld {
namespace wasm {
@@ -69,13 +47,15 @@
void writeGlobal(raw_ostream &OS, const llvm::wasm::WasmGlobal &Global);
+void writeTableType(raw_ostream &OS, const llvm::wasm::WasmTable &Type);
+
void writeImport(raw_ostream &OS, const llvm::wasm::WasmImport &Import);
void writeExport(raw_ostream &OS, const llvm::wasm::WasmExport &Export);
} // namespace wasm
-std::string toString(const llvm::wasm::ValType Type);
+std::string toString(llvm::wasm::ValType Type);
std::string toString(const llvm::wasm::WasmSignature &Sig);
std::string toString(const llvm::wasm::WasmGlobalType &Sig);