aboutsummaryrefslogtreecommitdiff
path: root/COFF
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-04-16 16:03:39 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-04-16 16:03:39 +0000
commitd2d3ebb81992e107edf95c1c136d7a342d9b1418 (patch)
treebb1af8fff2b1400cf240e3b2532a1e5d22a121da /COFF
parent16787c9ce0b96aaa669d7fab3a495916b35ce758 (diff)
Notes
Diffstat (limited to 'COFF')
-rw-r--r--COFF/CMakeLists.txt5
-rw-r--r--COFF/Chunks.cpp9
-rw-r--r--COFF/Chunks.h4
-rw-r--r--COFF/Config.h14
-rw-r--r--COFF/Driver.cpp287
-rw-r--r--COFF/Driver.h4
-rw-r--r--COFF/DriverUtils.cpp71
-rw-r--r--COFF/Error.cpp74
-rw-r--r--COFF/Error.h10
-rw-r--r--COFF/ICF.cpp9
-rw-r--r--COFF/InputFiles.cpp94
-rw-r--r--COFF/InputFiles.h11
-rw-r--r--COFF/LTO.cpp140
-rw-r--r--COFF/LTO.h56
-rw-r--r--COFF/Librarian.cpp13
-rw-r--r--COFF/MapFile.cpp114
-rw-r--r--COFF/MapFile.h22
-rw-r--r--COFF/ModuleDef.cpp22
-rw-r--r--COFF/Options.td8
-rw-r--r--COFF/PDB.cpp109
-rw-r--r--COFF/SymbolTable.cpp173
-rw-r--r--COFF/SymbolTable.h17
-rw-r--r--COFF/Symbols.cpp9
-rw-r--r--COFF/Symbols.h78
-rw-r--r--COFF/Writer.cpp61
-rw-r--r--COFF/Writer.h51
26 files changed, 1034 insertions, 431 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt
index 70a33b9fdd81..8f24e36c0eca 100644
--- a/COFF/CMakeLists.txt
+++ b/COFF/CMakeLists.txt
@@ -15,6 +15,8 @@ add_lld_library(lldCOFF
ICF.cpp
InputFiles.cpp
Librarian.cpp
+ LTO.cpp
+ MapFile.cpp
MarkLive.cpp
ModuleDef.cpp
PDB.cpp
@@ -25,6 +27,7 @@ add_lld_library(lldCOFF
LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD}
+ BitReader
Core
DebugInfoCodeView
DebugInfoMSF
@@ -40,7 +43,7 @@ add_lld_library(lldCOFF
LINK_LIBS
lldCore
- ${PTHREAD_LIB}
+ ${LLVM_PTHREAD_LIB}
DEPENDS
COFFOptionsTableGen
diff --git a/COFF/Chunks.cpp b/COFF/Chunks.cpp
index 7f0dfa92ec10..10eeedd88e55 100644
--- a/COFF/Chunks.cpp
+++ b/COFF/Chunks.cpp
@@ -11,6 +11,7 @@
#include "Error.h"
#include "InputFiles.h"
#include "Symbols.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/COFF.h"
#include "llvm/Support/Debug.h"
@@ -61,7 +62,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_AMD64_SECTION: add16(Off, Sym->getSectionIndex()); break;
case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break;
default:
- fatal("unsupported relocation type");
+ fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
}
@@ -76,7 +77,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break;
case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break;
default:
- fatal("unsupported relocation type");
+ fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
}
@@ -136,7 +137,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break;
case IMAGE_REL_ARM_SECREL: add32(Off, Sym->getSecrel()); break;
default:
- fatal("unsupported relocation type");
+ fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
}
}
@@ -226,7 +227,7 @@ void SectionChunk::printDiscardedMessage() const {
// Removed by dead-stripping. If it's removed by ICF, ICF already
// printed out the name, so don't repeat that here.
if (Sym && this == Repl)
- outs() << "Discarded " << Sym->getName() << "\n";
+ message("Discarded " + Sym->getName());
}
StringRef SectionChunk::getDebugName() {
diff --git a/COFF/Chunks.h b/COFF/Chunks.h
index 59e36b84c9b0..44d7f31afc67 100644
--- a/COFF/Chunks.h
+++ b/COFF/Chunks.h
@@ -187,10 +187,10 @@ public:
const coff_section *Header;
-private:
- // A file this chunk was created from.
+ // The file that this chunk was created from.
ObjectFile *File;
+private:
StringRef SectionName;
std::vector<SectionChunk *> AssocChildren;
llvm::iterator_range<const coff_relocation *> Relocs;
diff --git a/COFF/Config.h b/COFF/Config.h
index 0fa3338aa28c..31534aeb3971 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -80,14 +80,16 @@ struct Configuration {
SymbolBody *Entry = nullptr;
bool NoEntry = false;
std::string OutputFile;
+ bool ColorDiagnostics;
bool DoGC = true;
bool DoICF = true;
+ uint64_t ErrorLimit = 20;
bool Relocatable = true;
bool Force = false;
bool Debug = false;
bool WriteSymtab = true;
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
- StringRef PDBPath;
+ llvm::SmallString<128> PDBPath;
// Symbols in this set are considered as live by the garbage collector.
std::set<SymbolBody *> GCRoot;
@@ -103,6 +105,8 @@ struct Configuration {
std::map<std::string, int> DLLOrder;
SymbolBody *DelayLoadHelper = nullptr;
+ bool SaveTemps = false;
+
// Used for SafeSEH.
Symbol *SEHTable = nullptr;
Symbol *SEHCount = nullptr;
@@ -111,7 +115,9 @@ struct Configuration {
unsigned LTOOptLevel = 2;
// Used for /opt:lldltojobs=N
- unsigned LTOJobs = 1;
+ unsigned LTOJobs = 0;
+ // Used for /opt:lldltopartitions=N
+ unsigned LTOPartitions = 1;
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
std::map<StringRef, StringRef> Merge;
@@ -135,6 +141,9 @@ struct Configuration {
// Used for /alternatename.
std::map<StringRef, StringRef> AlternateNames;
+ // Used for /lldmap.
+ std::string MapFile;
+
uint64_t ImageBase = -1;
uint64_t StackReserve = 1024 * 1024;
uint64_t StackCommit = 4096;
@@ -151,6 +160,7 @@ struct Configuration {
bool TerminalServerAware = true;
bool LargeAddressAware = false;
bool HighEntropyVA = false;
+ bool AppContainer = false;
// This is for debugging.
bool DebugPdb = false;
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index 4dabd9ebcc6d..3e7f10bf8d11 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -19,6 +19,7 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/LibDriver/LibDriver.h"
+#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
@@ -31,17 +32,11 @@
#include <algorithm>
#include <memory>
-#ifdef _MSC_VER
-// <future> depends on <eh.h> for __uncaught_exception.
-#include <eh.h>
-#endif
-
#include <future>
using namespace llvm;
using namespace llvm::COFF;
using llvm::sys::Process;
-using llvm::sys::fs::OpenFlags;
using llvm::sys::fs::file_magic;
using llvm::sys::fs::identify_magic;
@@ -55,11 +50,16 @@ BumpPtrAllocator BAlloc;
StringSaver Saver{BAlloc};
std::vector<SpecificAllocBase *> SpecificAllocBase::Instances;
-bool link(ArrayRef<const char *> Args) {
+bool link(ArrayRef<const char *> Args, raw_ostream &Diag) {
+ ErrorCount = 0;
+ ErrorOS = &Diag;
+ Argv0 = Args[0];
Config = make<Configuration>();
+ Config->ColorDiagnostics =
+ (ErrorOS == &llvm::errs() && Process::StandardErrHasColors());
Driver = make<LinkerDriver>();
Driver->link(Args);
- return true;
+ return !ErrorCount;
}
// Drop directory components and replace extension with ".exe" or ".dll".
@@ -121,10 +121,12 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) {
return Symtab.addFile(make<ArchiveFile>(MBRef));
if (Magic == file_magic::bitcode)
return Symtab.addFile(make<BitcodeFile>(MBRef));
+
if (Magic == file_magic::coff_cl_gl_object)
- fatal(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
+ error(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
"Recompile without /GL");
- Symtab.addFile(make<ObjectFile>(MBRef));
+ else
+ Symtab.addFile(make<ObjectFile>(MBRef));
}
void LinkerDriver::enqueuePath(StringRef Path) {
@@ -134,12 +136,10 @@ void LinkerDriver::enqueuePath(StringRef Path) {
enqueueTask([=]() {
auto MBOrErr = Future->get();
if (MBOrErr.second)
- fatal(MBOrErr.second, "could not open " + PathStr);
- Driver->addBuffer(std::move(MBOrErr.first));
+ error("could not open " + PathStr + ": " + MBOrErr.second.message());
+ else
+ Driver->addBuffer(std::move(MBOrErr.first));
});
-
- if (Config->OutputFile == "")
- Config->OutputFile = getOutputPath(Path);
}
void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
@@ -151,17 +151,18 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
}
InputFile *Obj;
- if (Magic == file_magic::coff_object)
+ if (Magic == file_magic::coff_object) {
Obj = make<ObjectFile>(MB);
- else if (Magic == file_magic::bitcode)
+ } else if (Magic == file_magic::bitcode) {
Obj = make<BitcodeFile>(MB);
- else
- fatal("unknown file type: " + MB.getBufferIdentifier());
+ } else {
+ error("unknown file type: " + MB.getBufferIdentifier());
+ return;
+ }
Obj->ParentName = ParentName;
Symtab.addFile(Obj);
- if (Config->Verbose)
- outs() << "Loaded " << toString(Obj) << " for " << SymName << "\n";
+ log("Loaded " + toString(Obj) + " for " + SymName);
}
void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
@@ -234,7 +235,7 @@ void LinkerDriver::parseDirectives(StringRef S) {
case OPT_throwingnew:
break;
default:
- fatal(Arg->getSpelling() + " is not allowed in .drectve");
+ error(Arg->getSpelling() + " is not allowed in .drectve");
}
}
}
@@ -402,7 +403,8 @@ static unsigned parseDebugType(StringRef Arg) {
DebugTypes |= StringSwitch<unsigned>(Type.lower())
.Case("cv", static_cast<unsigned>(DebugType::CV))
.Case("pdata", static_cast<unsigned>(DebugType::PData))
- .Case("fixup", static_cast<unsigned>(DebugType::Fixup));
+ .Case("fixup", static_cast<unsigned>(DebugType::Fixup))
+ .Default(0);
return DebugTypes;
}
@@ -418,6 +420,132 @@ static std::string getMapFile(const opt::InputArgList &Args) {
return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str();
}
+std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
+ std::vector<MemoryBufferRef> V;
+ Error Err = Error::success();
+ for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
+ Archive::Child C =
+ check(COrErr,
+ File->getFileName() + ": could not get the child of the archive");
+ MemoryBufferRef MBRef =
+ check(C.getMemoryBufferRef(),
+ File->getFileName() +
+ ": could not get the buffer for a child of the archive");
+ V.push_back(MBRef);
+ }
+ if (Err)
+ fatal(File->getFileName() +
+ ": Archive::children failed: " + toString(std::move(Err)));
+ return V;
+}
+
+// A helper function for filterBitcodeFiles.
+static bool needsRebuilding(MemoryBufferRef MB) {
+ // The MSVC linker doesn't support thin archives, so if it's a thin
+ // archive, we always need to rebuild it.
+ std::unique_ptr<Archive> File =
+ check(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier());
+ if (File->isThin())
+ return true;
+
+ // Returns true if the archive contains at least one bitcode file.
+ for (MemoryBufferRef Member : getArchiveMembers(File.get()))
+ if (identify_magic(Member.getBuffer()) == file_magic::bitcode)
+ return true;
+ return false;
+}
+
+// Opens a given path as an archive file and removes bitcode files
+// from them if exists. This function is to appease the MSVC linker as
+// their linker doesn't like archive files containing non-native
+// object files.
+//
+// If a given archive doesn't contain bitcode files, the archive path
+// is returned as-is. Otherwise, a new temporary file is created and
+// its path is returned.
+static Optional<std::string>
+filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
+ std::unique_ptr<MemoryBuffer> MB = check(
+ MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
+ MemoryBufferRef MBRef = MB->getMemBufferRef();
+ file_magic Magic = identify_magic(MBRef.getBuffer());
+
+ if (Magic == file_magic::bitcode)
+ return None;
+ if (Magic != file_magic::archive)
+ return Path.str();
+ if (!needsRebuilding(MBRef))
+ return Path.str();
+
+ std::unique_ptr<Archive> File =
+ check(Archive::create(MBRef),
+ MBRef.getBufferIdentifier() + ": failed to parse archive");
+
+ std::vector<NewArchiveMember> New;
+ for (MemoryBufferRef Member : getArchiveMembers(File.get()))
+ if (identify_magic(Member.getBuffer()) != file_magic::bitcode)
+ New.emplace_back(Member);
+
+ if (New.empty())
+ return None;
+
+ log("Creating a temporary archive for " + Path + " to remove bitcode files");
+
+ SmallString<128> S;
+ if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path),
+ ".lib", S))
+ fatal(EC, "cannot create a temporary file");
+ std::string Temp = S.str();
+ TemporaryFiles.push_back(Temp);
+
+ std::pair<StringRef, std::error_code> Ret =
+ llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU,
+ /*Deterministics=*/true,
+ /*Thin=*/false);
+ if (Ret.second)
+ error("failed to create a new archive " + S.str() + ": " + Ret.first);
+ return Temp;
+}
+
+// Create response file contents and invoke the MSVC linker.
+void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
+ std::string Rsp = "/nologo ";
+ std::vector<std::string> Temps;
+
+ for (auto *Arg : Args) {
+ switch (Arg->getOption().getID()) {
+ case OPT_linkrepro:
+ case OPT_lldmap:
+ case OPT_lldmap_file:
+ case OPT_lldsavetemps:
+ case OPT_msvclto:
+ // LLD-specific options are stripped.
+ break;
+ case OPT_opt:
+ if (!StringRef(Arg->getValue()).startswith("lld"))
+ Rsp += toString(Arg) + " ";
+ break;
+ case OPT_INPUT: {
+ if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
+ if (Optional<std::string> S = filterBitcodeFiles(*Path, Temps))
+ Rsp += quote(*S) + " ";
+ continue;
+ }
+ Rsp += quote(Arg->getValue()) + " ";
+ break;
+ }
+ default:
+ Rsp += toString(Arg) + " ";
+ }
+ }
+
+ std::vector<StringRef> ObjectFiles = Symtab.compileBitcodeFiles();
+ runMSVCLinker(Rsp, ObjectFiles);
+
+ for (StringRef Path : Temps)
+ sys::fs::remove(Path);
+}
+
void LinkerDriver::enqueueTask(std::function<void()> Task) {
TaskQueue.push_back(std::move(Task));
}
@@ -451,6 +579,22 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Parse command line options.
opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
+ // Parse and evaluate -mllvm options.
+ std::vector<const char *> V;
+ V.push_back("lld-link (LLVM option parsing)");
+ for (auto *Arg : Args.filtered(OPT_mllvm))
+ V.push_back(Arg->getValue());
+ cl::ParseCommandLineOptions(V.size(), V.data());
+
+ // Handle /errorlimit early, because error() depends on it.
+ if (auto *Arg = Args.getLastArg(OPT_errorlimit)) {
+ int N = 20;
+ StringRef S = Arg->getValue();
+ if (S.getAsInteger(10, N))
+ error(Arg->getSpelling() + " number expected, but got " + S);
+ Config->ErrorLimit = N;
+ }
+
// Handle /help
if (Args.hasArg(OPT_help)) {
printHelp(ArgsArr[0]);
@@ -467,12 +611,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (ErrOrWriter) {
Tar = std::move(*ErrOrWriter);
} else {
- errs() << "/linkrepro: failed to open " << Path << ": "
- << toString(ErrOrWriter.takeError()) << '\n';
+ error("/linkrepro: failed to open " + Path + ": " +
+ toString(ErrOrWriter.takeError()));
}
}
- if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end())
+ if (!Args.hasArgNoClaim(OPT_INPUT))
fatal("no input files");
// Construct search path list.
@@ -508,9 +652,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /noentry
if (Args.hasArg(OPT_noentry)) {
- if (!Args.hasArg(OPT_dll))
- fatal("/noentry must be specified with /dll");
- Config->NoEntry = true;
+ if (Args.hasArg(OPT_dll))
+ Config->NoEntry = true;
+ else
+ error("/noentry must be specified with /dll");
}
// Handle /dll
@@ -521,12 +666,17 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Handle /fixed
if (Args.hasArg(OPT_fixed)) {
- if (Args.hasArg(OPT_dynamicbase))
- fatal("/fixed must not be specified with /dynamicbase");
- Config->Relocatable = false;
- Config->DynamicBase = false;
+ if (Args.hasArg(OPT_dynamicbase)) {
+ error("/fixed must not be specified with /dynamicbase");
+ } else {
+ Config->Relocatable = false;
+ Config->DynamicBase = false;
+ }
}
+ if (Args.hasArg(OPT_appcontainer))
+ Config->AppContainer = true;
+
// Handle /machine
if (auto *Arg = Args.getLastArg(OPT_machine))
Config->Machine = getMachineType(Arg->getValue());
@@ -596,20 +746,31 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
StringRef OptLevel = StringRef(S).substr(7);
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
Config->LTOOptLevel > 3)
- fatal("/opt:lldlto: invalid optimization level: " + OptLevel);
+ error("/opt:lldlto: invalid optimization level: " + OptLevel);
continue;
}
if (StringRef(S).startswith("lldltojobs=")) {
StringRef Jobs = StringRef(S).substr(11);
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
- fatal("/opt:lldltojobs: invalid job count: " + Jobs);
+ error("/opt:lldltojobs: invalid job count: " + Jobs);
+ continue;
+ }
+ if (StringRef(S).startswith("lldltopartitions=")) {
+ StringRef N = StringRef(S).substr(17);
+ if (N.getAsInteger(10, Config->LTOPartitions) ||
+ Config->LTOPartitions == 0)
+ error("/opt:lldltopartitions: invalid partition count: " + N);
continue;
}
if (S != "ref" && S != "lbr" && S != "nolbr")
- fatal("/opt: unknown option: " + S);
+ error("/opt: unknown option: " + S);
}
}
+ // Handle /lldsavetemps
+ if (Args.hasArg(OPT_lldsavetemps))
+ Config->SaveTemps = true;
+
// Handle /failifmismatch
for (auto *Arg : Args.filtered(OPT_failifmismatch))
checkFailIfMismatch(Arg->getValue());
@@ -658,6 +819,11 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
Config->DumpPdb = Args.hasArg(OPT_dumppdb);
Config->DebugPdb = Args.hasArg(OPT_debugpdb);
+ Config->MapFile = getMapFile(Args);
+
+ if (ErrorCount)
+ return;
+
// Create a list of input files. Files can be given as arguments
// for /defaultlib option.
std::vector<MemoryBufferRef> MBs;
@@ -678,7 +844,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// We should have inferred a machine type by now from the input files, but if
// not we assume x64.
if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
- errs() << "warning: /machine is not specified. x64 is assumed.\n";
+ warn("/machine is not specified. x64 is assumed");
Config->Machine = AMD64;
}
@@ -715,8 +881,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
if (S.empty())
fatal("entry point must be defined");
Config->Entry = addUndefined(S);
- if (Config->Verbose)
- outs() << "Entry name inferred: " << S << "\n";
+ log("Entry name inferred: " + S);
}
// Handle /export
@@ -749,6 +914,22 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
}
+ // Set default image name if neither /out or /def set it.
+ if (Config->OutputFile.empty()) {
+ Config->OutputFile =
+ getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue());
+ }
+
+ // Put the PDB next to the image if no /pdb flag was passed.
+ if (Config->Debug && Config->PDBPath.empty()) {
+ Config->PDBPath = Config->OutputFile;
+ sys::path::replace_extension(Config->PDBPath, ".pdb");
+ }
+
+ // Disable PDB generation if the user requested it.
+ if (Args.hasArg(OPT_nopdb))
+ Config->PDBPath = "";
+
// Set default image base if /base is not given.
if (Config->ImageBase == uint64_t(-1))
Config->ImageBase = getDefaultImageBase();
@@ -801,6 +982,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
addUndefined(mangle("_load_config_used"));
} while (run());
+ if (ErrorCount)
+ return;
+
+ // If /msvclto is given, we use the MSVC linker to link LTO output files.
+ // This is useful because MSVC link.exe can generate complete PDBs.
+ if (Args.hasArg(OPT_msvclto)) {
+ invokeMSVC(Args);
+ exit(0);
+ }
+
// Do LTO by compiling bitcode input files to a set of native COFF files then
// link those files.
Symtab.addCombinedLTOObjects();
@@ -818,10 +1009,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
}
// Handle /safeseh.
- if (Args.hasArg(OPT_safeseh))
+ if (Args.hasArg(OPT_safeseh)) {
for (ObjectFile *File : Symtab.ObjectFiles)
if (!File->SEHCompat)
- fatal("/safeseh: " + File->getName() + " is not compatible with SEH");
+ error("/safeseh: " + File->getName() + " is not compatible with SEH");
+ if (ErrorCount)
+ return;
+ }
// Windows specific -- when we are creating a .dll file, we also
// need to create a .lib file.
@@ -846,17 +1040,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
// Write the result.
writeResult(&Symtab);
- // Create a symbol map file containing symbol VAs and their names
- // to help debugging.
- std::string MapFile = getMapFile(Args);
- if (!MapFile.empty()) {
- std::error_code EC;
- raw_fd_ostream Out(MapFile, EC, OpenFlags::F_Text);
- if (EC)
- fatal(EC, "could not create the symbol map " + MapFile);
- Symtab.printMap(Out);
- }
-
// Call exit to avoid calling destructors.
exit(0);
}
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 44894269fcbe..4566f73eef31 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -107,6 +107,8 @@ private:
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
+ void invokeMSVC(llvm::opt::InputArgList &Args);
+
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
void addBuffer(std::unique_ptr<MemoryBuffer> MB);
void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
@@ -178,6 +180,8 @@ void checkFailIfMismatch(StringRef Arg);
std::unique_ptr<MemoryBuffer>
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
+void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects);
+
// Create enum with OPT_xxx values for each option in Options.td
enum {
OPT_INVALID = 0,
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 14dd004f1c04..a9c1c9d5593e 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -44,31 +44,33 @@ namespace {
class Executor {
public:
explicit Executor(StringRef S) : Saver(Alloc), Prog(Saver.save(S)) {}
- void add(StringRef S) { Args.push_back(Saver.save(S).data()); }
- void add(std::string &S) { Args.push_back(Saver.save(S).data()); }
- void add(Twine S) { Args.push_back(Saver.save(S).data()); }
- void add(const char *S) { Args.push_back(Saver.save(S).data()); }
+ void add(StringRef S) { Args.push_back(Saver.save(S)); }
+ void add(std::string &S) { Args.push_back(Saver.save(S)); }
+ void add(Twine S) { Args.push_back(Saver.save(S)); }
+ void add(const char *S) { Args.push_back(Saver.save(S)); }
void run() {
ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog);
if (auto EC = ExeOrErr.getError())
fatal(EC, "unable to find " + Prog + " in PATH: ");
- const char *Exe = Saver.save(*ExeOrErr).data();
+ StringRef Exe = Saver.save(*ExeOrErr);
Args.insert(Args.begin(), Exe);
- Args.push_back(nullptr);
- if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
- for (const char *S : Args)
- if (S)
- errs() << S << " ";
- fatal("ExecuteAndWait failed");
- }
+
+ std::vector<const char *> Vec;
+ for (StringRef S : Args)
+ Vec.push_back(S.data());
+ Vec.push_back(nullptr);
+
+ if (sys::ExecuteAndWait(Args[0], Vec.data()) != 0)
+ fatal("ExecuteAndWait failed: " +
+ llvm::join(Args.begin(), Args.end(), " "));
}
private:
BumpPtrAllocator Alloc;
StringSaver Saver;
StringRef Prog;
- std::vector<const char *> Args;
+ std::vector<StringRef> Args;
};
} // anonymous namespace
@@ -167,8 +169,7 @@ void parseMerge(StringRef S) {
if (!Inserted) {
StringRef Existing = Pair.first->second;
if (Existing != To)
- errs() << "warning: " << S << ": already merged into " << Existing
- << "\n";
+ warn(S + ": already merged into " + Existing);
}
}
@@ -282,11 +283,19 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) {
namespace {
class TemporaryFile {
public:
- TemporaryFile(StringRef Prefix, StringRef Extn) {
+ TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") {
SmallString<128> S;
if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
fatal(EC, "cannot create a temporary file");
Path = S.str();
+
+ if (!Contents.empty()) {
+ std::error_code EC;
+ raw_fd_ostream OS(Path, EC, sys::fs::F_None);
+ if (EC)
+ fatal(EC, "failed to open " + Path);
+ OS << Contents;
+ }
}
TemporaryFile(TemporaryFile &&Obj) {
@@ -542,7 +551,7 @@ void fixupExports() {
Export *Existing = Pair.first->second;
if (E == *Existing || E.Name != Existing->Name)
continue;
- errs() << "warning: duplicate /export option: " << E.Name << "\n";
+ warn("duplicate /export option: " + E.Name);
}
Config->Exports = std::move(V);
@@ -617,6 +626,26 @@ convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
return File.getMemoryBuffer();
}
+// Run MSVC link.exe for given in-memory object files.
+// Command line options are copied from those given to LLD.
+// This is for the /msvclto option.
+void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) {
+ // Write the in-memory object files to disk.
+ std::vector<TemporaryFile> Temps;
+ for (StringRef S : Objects) {
+ Temps.emplace_back("lto", "obj", S);
+ Rsp += quote(Temps.back().Path) + " ";
+ }
+
+ log("link.exe " + Rsp);
+
+ // Run MSVC link.exe.
+ Temps.emplace_back("lto", "rsp", Rsp);
+ Executor E("link.exe");
+ E.add(Twine("@" + Temps.back().Path));
+ E.run();
+}
+
// Create OptTable
// Create prefix string literals used in Options.td
@@ -653,16 +682,16 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
// Print the real command line if response files are expanded.
if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) {
- outs() << "Command line:";
+ std::string Msg = "Command line:";
for (const char *S : Argv)
- outs() << " " << S;
- outs() << "\n";
+ Msg += " " + std::string(S);
+ message(Msg);
}
if (MissingCount)
fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
- errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n";
+ warn("ignoring unknown argument: " + Arg->getSpelling());
return Args;
}
diff --git a/COFF/Error.cpp b/COFF/Error.cpp
index b2bd557413df..b2c7c89bd36c 100644
--- a/COFF/Error.cpp
+++ b/COFF/Error.cpp
@@ -8,11 +8,14 @@
//===----------------------------------------------------------------------===//
#include "Error.h"
+#include "Config.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
+#include <mutex>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
@@ -21,10 +24,68 @@
using namespace llvm;
namespace lld {
+// The functions defined in this file can be called from multiple threads,
+// but outs() or errs() are not thread-safe. We protect them using a mutex.
+static std::mutex Mu;
+
namespace coff {
+StringRef Argv0;
+uint64_t ErrorCount;
+raw_ostream *ErrorOS;
+
+static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) {
+ // Dealloc/destroy ManagedStatic variables before calling
+ // _exit(). In a non-LTO build, this is a nop. In an LTO
+ // build allows us to get the output of -time-passes.
+ llvm_shutdown();
+
+ outs().flush();
+ errs().flush();
+ _exit(Val);
+}
+
+static void print(StringRef S, raw_ostream::Colors C) {
+ *ErrorOS << Argv0 + ": ";
+ if (Config->ColorDiagnostics) {
+ ErrorOS->changeColor(C, true);
+ *ErrorOS << S;
+ ErrorOS->resetColor();
+ } else {
+ *ErrorOS << S;
+ }
+}
+
+void log(const Twine &Msg) {
+ if (Config->Verbose) {
+ std::lock_guard<std::mutex> Lock(Mu);
+ outs() << Argv0 << ": " << Msg << "\n";
+ }
+}
+
+void message(const Twine &Msg) {
+ std::lock_guard<std::mutex> Lock(Mu);
+ outs() << Msg << "\n";
+ outs().flush();
+}
+
+void error(const Twine &Msg) {
+ std::lock_guard<std::mutex> Lock(Mu);
+
+ if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
+ print("error: ", raw_ostream::RED);
+ *ErrorOS << Msg << "\n";
+ } else if (ErrorCount == Config->ErrorLimit) {
+ print("error: ", raw_ostream::RED);
+ *ErrorOS << "too many errors emitted, stopping now"
+ << " (use /ERRORLIMIT:0 to see all errors)\n";
+ exitLld(1);
+ }
+
+ ++ErrorCount;
+}
void fatal(const Twine &Msg) {
- if (sys::Process::StandardErrHasColors()) {
+ if (Config->ColorDiagnostics) {
errs().changeColor(raw_ostream::RED, /*bold=*/true);
errs() << "error: ";
errs().resetColor();
@@ -32,10 +93,7 @@ void fatal(const Twine &Msg) {
errs() << "error: ";
}
errs() << Msg << "\n";
-
- outs().flush();
- errs().flush();
- _exit(1);
+ exitLld(1);
}
void fatal(std::error_code EC, const Twine &Msg) {
@@ -46,5 +104,11 @@ void fatal(llvm::Error &Err, const Twine &Msg) {
fatal(errorToErrorCode(std::move(Err)), Msg);
}
+void warn(const Twine &Msg) {
+ std::lock_guard<std::mutex> Lock(Mu);
+ print("warning: ", raw_ostream::MAGENTA);
+ *ErrorOS << Msg << "\n";
+}
+
} // namespace coff
} // namespace lld
diff --git a/COFF/Error.h b/COFF/Error.h
index 47549327db2b..a4f44fb1e36c 100644
--- a/COFF/Error.h
+++ b/COFF/Error.h
@@ -16,11 +16,19 @@
namespace lld {
namespace coff {
+extern uint64_t ErrorCount;
+extern llvm::raw_ostream *ErrorOS;
+extern llvm::StringRef Argv0;
+
+void log(const Twine &Msg);
+void message(const Twine &Msg);
+void warn(const Twine &Msg);
+void error(const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix);
LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix);
-template <class T> T check(ErrorOr<T> &&V, const Twine &Prefix) {
+template <class T> T check(ErrorOr<T> V, const Twine &Prefix) {
if (auto EC = V.getError())
fatal(EC, Prefix);
return std::move(*V);
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index 196fbe2610ea..19468c0fac5e 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -231,19 +231,16 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
++Cnt;
} while (Repeat);
- if (Config->Verbose)
- outs() << "\nICF needed " << Cnt << " iterations\n";
+ log("ICF needed " + Twine(Cnt) + " iterations");
// Merge sections in the same colors.
forEachColor([&](size_t Begin, size_t End) {
if (End - Begin == 1)
return;
- if (Config->Verbose)
- outs() << "Selected " << Chunks[Begin]->getDebugName() << "\n";
+ log("Selected " + Chunks[Begin]->getDebugName());
for (size_t I = Begin + 1; I < End; ++I) {
- if (Config->Verbose)
- outs() << " Removed " << Chunks[I]->getDebugName() << "\n";
+ log(" Removed " + Chunks[I]->getDebugName());
Chunks[Begin]->replace(Chunks[I]);
}
});
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index cde355cd3f34..cb56e13014db 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -19,8 +19,6 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
-#include "llvm/IR/LLVMContext.h"
-#include "llvm/LTO/legacy/LTOModule.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/COFF.h"
@@ -41,13 +39,23 @@ using namespace llvm::support::endian;
using llvm::Triple;
using llvm::support::ulittle32_t;
-using llvm::sys::fs::file_magic;
-using llvm::sys::fs::identify_magic;
namespace lld {
namespace coff {
-LLVMContext BitcodeFile::Context;
+/// Checks that Source is compatible with being a weak alias to Target.
+/// If Source is Undefined and has no weak alias set, makes it a weak
+/// alias to Target.
+static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
+ SymbolBody *Source, SymbolBody *Target) {
+ auto *U = dyn_cast<Undefined>(Source);
+ if (!U)
+ return;
+ else if (!U->WeakAlias)
+ U->WeakAlias = Target;
+ else if (U->WeakAlias != Target)
+ Symtab->reportDuplicate(Source->symbol(), F);
+}
ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
@@ -177,15 +185,9 @@ void ObjectFile::initializeSymbols() {
I += Sym.getNumberOfAuxSymbols();
LastSectionNumber = Sym.getSectionNumber();
}
- for (auto WeakAlias : WeakAliases) {
- auto *U = dyn_cast<Undefined>(WeakAlias.first);
- if (!U)
- continue;
- // Report an error if two undefined symbols have different weak aliases.
- if (U->WeakAlias && U->WeakAlias != SparseSymbolBodies[WeakAlias.second])
- Symtab->reportDuplicate(U->symbol(), this);
- U->WeakAlias = SparseSymbolBodies[WeakAlias.second];
- }
+ for (auto WeakAlias : WeakAliases)
+ checkAndSetWeakAlias(Symtab, this, WeakAlias.first,
+ SparseSymbolBodies[WeakAlias.second]);
}
SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
@@ -200,7 +202,10 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
if (Sym.isCommon()) {
auto *C = new (Alloc) CommonChunk(Sym);
Chunks.push_back(C);
- return Symtab->addCommon(this, Sym, C)->body();
+ COFFObj->getSymbolName(Sym, Name);
+ Symbol *S =
+ Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C);
+ return S->body();
}
if (Sym.isAbsolute()) {
COFFObj->getSymbolName(Sym, Name);
@@ -247,10 +252,14 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
}
DefinedRegular *B;
- if (Sym.isExternal())
- B = cast<DefinedRegular>(Symtab->addRegular(this, Sym, SC)->body());
- else
- B = new (Alloc) DefinedRegular(this, Sym, SC);
+ if (Sym.isExternal()) {
+ COFFObj->getSymbolName(Sym, Name);
+ Symbol *S =
+ Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC);
+ B = cast<DefinedRegular>(S->body());
+ } else
+ B = new (Alloc) DefinedRegular(this, /*Name*/ "", SC->isCOMDAT(),
+ /*IsExternal*/ false, Sym.getGeneric(), SC);
if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
SC->setSymbol(B);
@@ -329,39 +338,32 @@ void ImportFile::parse() {
}
void BitcodeFile::parse() {
- Context.enableDebugTypeODRUniquing();
- ErrorOr<std::unique_ptr<LTOModule>> ModOrErr = LTOModule::createFromBuffer(
- Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions());
- M = check(std::move(ModOrErr), "could not create LTO module");
-
- StringSaver Saver(Alloc);
- for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) {
- lto_symbol_attributes Attrs = M->getSymbolAttributes(I);
- if ((Attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
- continue;
-
- StringRef SymName = Saver.save(M->getSymbolName(I));
- int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK;
- if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) {
- SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body());
+ Obj = check(lto::InputFile::create(MemoryBufferRef(
+ MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
+ for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
+ StringRef SymName = Saver.save(ObjSym.getName());
+ Symbol *Sym;
+ if (ObjSym.isUndefined()) {
+ Sym = Symtab->addUndefined(SymName, this, false);
+ } else if (ObjSym.isCommon()) {
+ Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize());
+ } else if (ObjSym.isWeak() && ObjSym.isIndirect()) {
+ // Weak external.
+ Sym = Symtab->addUndefined(SymName, this, true);
+ std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
+ SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback));
+ checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias);
} else {
- bool Replaceable =
- (SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common
- (Attrs & LTO_SYMBOL_COMDAT) || // comdat
- (SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external
- (Attrs & LTO_SYMBOL_ALIAS)));
- SymbolBodies.push_back(
- Symtab->addBitcode(this, SymName, Replaceable)->body());
+ bool IsCOMDAT = ObjSym.getComdatIndex() != -1;
+ Sym = Symtab->addRegular(this, SymName, IsCOMDAT);
}
+ SymbolBodies.push_back(Sym->body());
}
-
- Directives = M->getLinkerOpts();
+ Directives = Obj->getCOFFLinkerOpts();
}
MachineTypes BitcodeFile::getMachineType() {
- if (!M)
- return IMAGE_FILE_MACHINE_UNKNOWN;
- switch (Triple(M->getTargetTriple()).getArch()) {
+ switch (Triple(Obj->getTargetTriple()).getArch()) {
case Triple::x86_64:
return AMD64;
case Triple::x86:
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index 1b5d42939cca..9e02b2fc68bb 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -13,8 +13,7 @@
#include "lld/Core/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
-#include "llvm/IR/LLVMContext.h"
-#include "llvm/LTO/legacy/LTOModule.h"
+#include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFF.h"
#include "llvm/Support/StringSaver.h"
@@ -25,7 +24,6 @@
namespace lld {
namespace coff {
-using llvm::LTOModule;
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
using llvm::COFF::MachineTypes;
using llvm::object::Archive;
@@ -174,7 +172,6 @@ public:
private:
void parse() override;
- llvm::BumpPtrAllocator Alloc;
llvm::BumpPtrAllocator StringAllocAux;
llvm::StringSaver StringAlloc;
@@ -191,16 +188,12 @@ public:
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
MachineTypes getMachineType() override;
- std::unique_ptr<LTOModule> takeModule() { return std::move(M); }
-
- static llvm::LLVMContext Context;
+ std::unique_ptr<llvm::lto::InputFile> Obj;
private:
void parse() override;
std::vector<SymbolBody *> SymbolBodies;
- llvm::BumpPtrAllocator Alloc;
- std::unique_ptr<LTOModule> M;
};
} // namespace coff
diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp
new file mode 100644
index 000000000000..6883b3b4c2c8
--- /dev/null
+++ b/COFF/LTO.cpp
@@ -0,0 +1,140 @@
+//===- LTO.cpp ------------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LTO.h"
+#include "Config.h"
+#include "Error.h"
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "lld/Core/TargetOptionsCommandFlags.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/LTO/Config.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/Object/SymbolicFile.h"
+#include "llvm/Support/CodeGen.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <system_error>
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::object;
+
+using namespace lld;
+using namespace lld::coff;
+
+static void diagnosticHandler(const DiagnosticInfo &DI) {
+ SmallString<128> ErrStorage;
+ raw_svector_ostream OS(ErrStorage);
+ DiagnosticPrinterRawOStream DP(OS);
+ DI.print(DP);
+ warn(ErrStorage);
+}
+
+static void checkError(Error E) {
+ handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
+ error(EIB.message());
+ return Error::success();
+ });
+}
+
+static void saveBuffer(StringRef Buffer, const Twine &Path) {
+ std::error_code EC;
+ raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
+ if (EC)
+ error("cannot create " + Path + ": " + EC.message());
+ OS << Buffer;
+}
+
+static std::unique_ptr<lto::LTO> createLTO() {
+ lto::Config Conf;
+ Conf.Options = InitTargetOptionsFromCodeGenFlags();
+ Conf.RelocModel = Reloc::PIC_;
+ Conf.DisableVerify = true;
+ Conf.DiagHandler = diagnosticHandler;
+ Conf.OptLevel = Config->LTOOptLevel;
+ if (Config->SaveTemps)
+ checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
+ /*UseInputModulePath*/ true));
+ lto::ThinBackend Backend;
+ if (Config->LTOJobs != 0)
+ Backend = lto::createInProcessThinBackend(Config->LTOJobs);
+ return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
+ Config->LTOPartitions);
+}
+
+BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
+
+BitcodeCompiler::~BitcodeCompiler() = default;
+
+static void undefine(Symbol *S) {
+ replaceBody<Undefined>(S, S->body()->getName());
+}
+
+void BitcodeCompiler::add(BitcodeFile &F) {
+ lto::InputFile &Obj = *F.Obj;
+ unsigned SymNum = 0;
+ std::vector<SymbolBody *> SymBodies = F.getSymbols();
+ std::vector<lto::SymbolResolution> Resols(SymBodies.size());
+
+ // Provide a resolution to the LTO API for each symbol.
+ for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
+ SymbolBody *B = SymBodies[SymNum];
+ Symbol *Sym = B->symbol();
+ lto::SymbolResolution &R = Resols[SymNum];
+ ++SymNum;
+
+ // Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
+ // reports two symbols for module ASM defined. Without this check, lld
+ // flags an undefined in IR with a definition in ASM as prevailing.
+ // Once IRObjectFile is fixed to report only one symbol this hack can
+ // be removed.
+ R.Prevailing = !ObjSym.isUndefined() && B->getFile() == &F;
+ R.VisibleToRegularObj = Sym->IsUsedInRegularObj;
+ if (R.Prevailing)
+ undefine(Sym);
+ }
+ checkError(LTOObj->add(std::move(F.Obj), Resols));
+}
+
+// Merge all the bitcode files we have seen, codegen the result
+// and return the resulting objects.
+std::vector<StringRef> BitcodeCompiler::compile() {
+ unsigned MaxTasks = LTOObj->getMaxTasks();
+ Buff.resize(MaxTasks);
+
+ checkError(LTOObj->run([&](size_t Task) {
+ return llvm::make_unique<lto::NativeObjectStream>(
+ llvm::make_unique<raw_svector_ostream>(Buff[Task]));
+ }));
+
+ std::vector<StringRef> Ret;
+ for (unsigned I = 0; I != MaxTasks; ++I) {
+ if (Buff[I].empty())
+ continue;
+ if (Config->SaveTemps) {
+ if (I == 0)
+ saveBuffer(Buff[I], Config->OutputFile + ".lto.obj");
+ else
+ saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.obj");
+ }
+ Ret.emplace_back(Buff[I].data(), Buff[I].size());
+ }
+ return Ret;
+}
diff --git a/COFF/LTO.h b/COFF/LTO.h
new file mode 100644
index 000000000000..194a4cce8ada
--- /dev/null
+++ b/COFF/LTO.h
@@ -0,0 +1,56 @@
+//===- LTO.h ----------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides a way to combine bitcode files into one COFF
+// file by compiling them using LLVM.
+//
+// If LTO is in use, your input files are not in regular COFF files
+// but instead LLVM bitcode files. In that case, the linker has to
+// convert bitcode files into the native format so that we can create
+// a COFF file that contains native code. This file provides that
+// functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_LTO_H
+#define LLD_COFF_LTO_H
+
+#include "lld/Core/LLVM.h"
+#include "llvm/ADT/SmallString.h"
+#include <memory>
+#include <vector>
+
+namespace llvm {
+namespace lto {
+class LTO;
+}
+}
+
+namespace lld {
+namespace coff {
+
+class BitcodeFile;
+class InputFile;
+
+class BitcodeCompiler {
+public:
+ BitcodeCompiler();
+ ~BitcodeCompiler();
+
+ void add(BitcodeFile &F);
+ std::vector<StringRef> compile();
+
+private:
+ std::unique_ptr<llvm::lto::LTO> LTOObj;
+ std::vector<SmallString<0>> Buff;
+};
+}
+}
+
+#endif
diff --git a/COFF/Librarian.cpp b/COFF/Librarian.cpp
index 4c597fad7345..3ce72822180b 100644
--- a/COFF/Librarian.cpp
+++ b/COFF/Librarian.cpp
@@ -104,7 +104,18 @@ static ImportNameType getNameType(StringRef Sym, StringRef ExtName) {
static std::string replace(StringRef S, StringRef From, StringRef To) {
size_t Pos = S.find(From);
- assert(Pos != StringRef::npos);
+
+ // From and To may be mangled, but substrings in S may not.
+ if (Pos == StringRef::npos && From.startswith("_") && To.startswith("_")) {
+ From = From.substr(1);
+ To = To.substr(1);
+ Pos = S.find(From);
+ }
+
+ if (Pos == StringRef::npos) {
+ error(S + ": replacing '" + From + "' with '" + To + "' failed");
+ return "";
+ }
return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str();
}
diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp
new file mode 100644
index 000000000000..43dd8dc35810
--- /dev/null
+++ b/COFF/MapFile.cpp
@@ -0,0 +1,114 @@
+//===- MapFile.cpp --------------------------------------------------------===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the /lldmap option. It shows lists in order and
+// hierarchically the output sections, input sections, input files and
+// symbol:
+//
+// Address Size Align Out In File Symbol
+// =================================================================
+// 00201000 00000015 4 .text
+// 00201000 0000000e 4 .text
+// 00201000 0000000e 4 test.o
+// 0020100e 00000000 0 local
+// 00201005 00000000 0 f(int)
+//
+//===----------------------------------------------------------------------===//
+
+#include "MapFile.h"
+#include "Error.h"
+#include "Symbols.h"
+#include "Writer.h"
+
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+using namespace lld;
+using namespace lld::coff;
+
+static void writeOutSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
+ uint64_t Align, StringRef Name) {
+ OS << format("%08llx %08llx %5lld ", Address, Size, Align)
+ << left_justify(Name, 7);
+}
+
+static void writeInSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
+ uint64_t Align, StringRef Name) {
+ // Pass an empty name to align the text to the correct column.
+ writeOutSecLine(OS, Address, Size, Align, "");
+ OS << ' ' << left_justify(Name, 7);
+}
+
+static void writeFileLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
+ uint64_t Align, StringRef Name) {
+ // Pass an empty name to align the text to the correct column.
+ writeInSecLine(OS, Address, Size, Align, "");
+ OS << ' ' << left_justify(Name, 7);
+}
+
+static void writeSymbolLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
+ StringRef Name) {
+ // Pass an empty name to align the text to the correct column.
+ writeFileLine(OS, Address, Size, 0, "");
+ OS << ' ' << left_justify(Name, 7);
+}
+
+static void writeSectionChunk(raw_fd_ostream &OS, const SectionChunk *SC,
+ StringRef &PrevName) {
+ StringRef Name = SC->getSectionName();
+ if (Name != PrevName) {
+ writeInSecLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), Name);
+ OS << '\n';
+ PrevName = Name;
+ }
+ coff::ObjectFile *File = SC->File;
+ if (!File)
+ return;
+ writeFileLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(),
+ toString(File));
+ OS << '\n';
+ ArrayRef<SymbolBody *> Syms = File->getSymbols();
+ for (SymbolBody *Sym : Syms) {
+ auto *DR = dyn_cast<DefinedRegular>(Sym);
+ if (!DR || DR->getChunk() != SC ||
+ DR->getCOFFSymbol().isSectionDefinition())
+ continue;
+ writeSymbolLine(OS, DR->getRVA(), SC->getSize(), toString(*Sym));
+ OS << '\n';
+ }
+}
+
+static void writeMapFile2(raw_fd_ostream &OS,
+ ArrayRef<OutputSection *> OutputSections) {
+ OS << "Address Size Align Out In File Symbol\n";
+
+ for (OutputSection *Sec : OutputSections) {
+ uint32_t VA = Sec->getRVA();
+ writeOutSecLine(OS, VA, Sec->getVirtualSize(), /*Align=*/PageSize,
+ Sec->getName());
+ OS << '\n';
+ StringRef PrevName = "";
+ for (Chunk *C : Sec->getChunks())
+ if (const auto *SC = dyn_cast<SectionChunk>(C))
+ writeSectionChunk(OS, SC, PrevName);
+ }
+}
+
+void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
+ if (Config->MapFile.empty())
+ return;
+
+ std::error_code EC;
+ raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
+ if (EC)
+ fatal("cannot open " + Config->MapFile + ": " + EC.message());
+ writeMapFile2(OS, OutputSections);
+}
diff --git a/COFF/MapFile.h b/COFF/MapFile.h
new file mode 100644
index 000000000000..0d0d68ce3ead
--- /dev/null
+++ b/COFF/MapFile.h
@@ -0,0 +1,22 @@
+//===- MapFile.h ------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Linker
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_MAPFILE_H
+#define LLD_COFF_MAPFILE_H
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace lld {
+namespace coff {
+class OutputSection;
+void writeMapFile(llvm::ArrayRef<OutputSection *> OutputSections);
+}
+}
+
+#endif
diff --git a/COFF/ModuleDef.cpp b/COFF/ModuleDef.cpp
index a273b6f535db..c9a40ac5ab8c 100644
--- a/COFF/ModuleDef.cpp
+++ b/COFF/ModuleDef.cpp
@@ -163,17 +163,25 @@ private:
case KwHeapsize:
parseNumbers(&Config->HeapReserve, &Config->HeapCommit);
return;
- case KwLibrary:
- parseName(&Config->OutputFile, &Config->ImageBase);
- if (!StringRef(Config->OutputFile).endswith_lower(".dll"))
- Config->OutputFile += ".dll";
- return;
case KwStacksize:
parseNumbers(&Config->StackReserve, &Config->StackCommit);
return;
- case KwName:
- parseName(&Config->OutputFile, &Config->ImageBase);
+ case KwLibrary:
+ case KwName: {
+ bool IsDll = Tok.K == KwLibrary; // Check before parseName.
+ std::string Name;
+ parseName(&Name, &Config->ImageBase);
+
+ // Append the appropriate file extension if not already present.
+ StringRef Ext = IsDll ? ".dll" : ".exe";
+ if (!StringRef(Name).endswith_lower(Ext))
+ Name += Ext;
+
+ // Set the output file, but don't override /out if it was already passed.
+ if (Config->OutputFile.empty())
+ Config->OutputFile = Name;
return;
+ }
case KwVersion:
parseVersion(&Config->MajorImageVersion, &Config->MinorImageVersion);
return;
diff --git a/COFF/Options.td b/COFF/Options.td
index 9dfbcc8e188c..7b5573b31cd3 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -21,6 +21,8 @@ def base : P<"base", "Base address of the program">;
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
def delayload : P<"delayload", "Delay loaded DLL name">;
def entry : P<"entry", "Name of entry point symbol">;
+def errorlimit : P<"errorlimit",
+ "Maximum number of errors to emit before stopping (0 = no limit)">;
def export : P<"export", "Export a function">;
// No help text because /failifmismatch is not intended to be used by the user.
def failifmismatch : P<"failifmismatch", "">;
@@ -28,6 +30,8 @@ def heap : P<"heap", "Size of the heap">;
def implib : P<"implib", "Import library name">;
def libpath : P<"libpath", "Additional library search path">;
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
+def lldsavetemps : F<"lldsavetemps">,
+ HelpText<"Save temporary files instead of deleting them">;
def machine : P<"machine", "Specify target platform">;
def merge : P<"merge", "Combine sections">;
def mllvm : P<"mllvm", "Options to pass to LLVM">;
@@ -78,6 +82,8 @@ def force_unresolved : F<"force:unresolved">;
defm allowbind: B<"allowbind", "Disable DLL binding">;
defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">;
+defm appcontainer : B<"appcontainer",
+ "Image can only be run in an app container">;
defm dynamicbase : B<"dynamicbase",
"Disable address space layout randomization">;
defm fixed : B<"fixed", "Enable base relocations">;
@@ -91,7 +97,9 @@ def help : F<"help">;
def help_q : Flag<["/?", "-?"], "">, Alias<help>;
// LLD extensions
+def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">;
def nosymtab : F<"nosymtab">;
+def msvclto : F<"msvclto">;
// Flags for debugging
def debugpdb : F<"debugpdb">;
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index 923fc64c7734..e32bcd20a541 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -20,20 +20,23 @@
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
-#include "llvm/DebugInfo/MSF/ByteStream.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MSFCommon.h"
-#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
-#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h"
-#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
-#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h"
-#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
-#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h"
-#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
-#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h"
+#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
#include "llvm/Object/COFF.h"
+#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
@@ -79,32 +82,49 @@ static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
return Data.slice(4);
}
-// Merge .debug$T sections and returns it.
-static std::vector<uint8_t> mergeDebugT(SymbolTable *Symtab) {
- ScopedPrinter W(outs());
+static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
+ codeview::TypeTableBuilder &TypeTable) {
+ // Start the TPI or IPI stream header.
+ TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
+
+ // Flatten the in memory type table.
+ TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
+ // FIXME: Hash types.
+ TpiBuilder.addTypeRecord(Rec, None);
+ });
+}
+// Merge .debug$T sections into IpiData and TpiData.
+static void mergeDebugT(SymbolTable *Symtab, pdb::PDBFileBuilder &Builder,
+ codeview::TypeTableBuilder &TypeTable,
+ codeview::TypeTableBuilder &IDTable) {
// Visit all .debug$T sections to add them to Builder.
- codeview::TypeTableBuilder Builder(BAlloc);
for (ObjectFile *File : Symtab->ObjectFiles) {
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
if (Data.empty())
continue;
- msf::ByteStream Stream(Data);
+ BinaryByteStream Stream(Data, support::little);
codeview::CVTypeArray Types;
- msf::StreamReader Reader(Stream);
+ BinaryStreamReader Reader(Stream);
+ // Follow type servers. If the same type server is encountered more than
+ // once for this instance of `PDBTypeServerHandler` (for example if many
+ // object files reference the same TypeServer), the types from the
+ // TypeServer will only be visited once.
+ pdb::PDBTypeServerHandler Handler;
+ Handler.addSearchPath(llvm::sys::path::parent_path(File->getName()));
if (auto EC = Reader.readArray(Types, Reader.getLength()))
fatal(EC, "Reader::readArray failed");
- if (!codeview::mergeTypeStreams(Builder, Types))
- fatal("codeview::mergeTypeStreams failed");
+ if (auto Err =
+ codeview::mergeTypeStreams(IDTable, TypeTable, &Handler, Types))
+ fatal(Err, "codeview::mergeTypeStreams failed");
}
- // Construct section contents.
- std::vector<uint8_t> V;
- Builder.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
- V.insert(V.end(), Rec.begin(), Rec.end());
- });
- return V;
+ // Construct TPI stream contents.
+ addTypeInfo(Builder.getTpiBuilder(), TypeTable);
+
+ // Construct IPI stream contents.
+ addTypeInfo(Builder.getIpiBuilder(), IDTable);
}
static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) {
@@ -115,6 +135,8 @@ static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) {
TypeDatabase TDB;
TypeDumpVisitor TDV(TDB, &W, false);
+ // Use a default implementation that does not follow type servers and instead
+ // just dumps the contents of the TypeServer2 record.
CVTypeDumper TypeDumper(TDB);
if (auto EC = TypeDumper.dump(Data, TDV))
fatal(EC, "CVTypeDumper::dump failed");
@@ -126,9 +148,9 @@ static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) {
if (Data.empty())
return;
- msf::ByteStream Stream(Data);
+ BinaryByteStream Stream(Data, llvm::support::little);
CVSymbolArray Symbols;
- msf::StreamReader Reader(Stream);
+ BinaryStreamReader Reader(Stream);
if (auto EC = Reader.readArray(Symbols, Reader.getLength()))
fatal(EC, "StreamReader.readArray<CVSymbolArray> failed");
@@ -148,17 +170,6 @@ static void dumpCodeView(SymbolTable *Symtab) {
}
}
-static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
- ArrayRef<uint8_t> Data) {
- msf::ByteStream Stream(Data);
- codeview::CVTypeArray Records;
- msf::StreamReader Reader(Stream);
- if (auto EC = Reader.readArray(Records, Reader.getLength()))
- fatal(EC, "Reader.readArray failed");
- for (const codeview::CVType &Rec : Records)
- TpiBuilder.addTypeRecord(Rec);
-}
-
// Creates a PDB file.
void coff::createPDB(StringRef Path, SymbolTable *Symtab,
ArrayRef<uint8_t> SectionTable,
@@ -177,9 +188,12 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab,
// Add an Info stream.
auto &InfoBuilder = Builder.getInfoBuilder();
- InfoBuilder.setAge(DI->PDB70.Age);
- InfoBuilder.setGuid(
- *reinterpret_cast<const pdb::PDB_UniqueId *>(&DI->PDB70.Signature));
+ InfoBuilder.setAge(DI ? DI->PDB70.Age : 0);
+
+ pdb::PDB_UniqueId uuid{};
+ if (DI)
+ memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid));
+ InfoBuilder.setGuid(uuid);
// Should be the current time, but set 0 for reproducibilty.
InfoBuilder.setSignature(0);
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
@@ -188,18 +202,9 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab,
auto &DbiBuilder = Builder.getDbiBuilder();
DbiBuilder.setVersionHeader(pdb::PdbDbiV110);
- // Add an empty TPI stream.
- auto &TpiBuilder = Builder.getTpiBuilder();
- TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
- std::vector<uint8_t> TpiData;
- if (Config->DebugPdb) {
- TpiData = mergeDebugT(Symtab);
- addTypeInfo(TpiBuilder, TpiData);
- }
-
- // Add an empty IPI stream.
- auto &IpiBuilder = Builder.getIpiBuilder();
- IpiBuilder.setVersionHeader(pdb::PdbTpiV80);
+ codeview::TypeTableBuilder TypeTable(BAlloc);
+ codeview::TypeTableBuilder IDTable(BAlloc);
+ mergeDebugT(Symtab, Builder, TypeTable, IDTable);
// Add Section Contributions.
std::vector<pdb::SectionContrib> Contribs =
@@ -214,7 +219,7 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab,
pdb::DbiStreamBuilder::createSectionMap(Sections);
DbiBuilder.setSectionMap(SectionMap);
- ExitOnErr(DbiBuilder.addModuleInfo("", "* Linker *"));
+ ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
// Add COFF section header stream.
ExitOnErr(
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index 9cc0b75c1510..310eab274526 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -11,10 +11,10 @@
#include "Config.h"
#include "Driver.h"
#include "Error.h"
+#include "LTO.h"
#include "Memory.h"
#include "Symbols.h"
#include "llvm/IR/LLVMContext.h"
-#include "llvm/LTO/legacy/LTOCodeGenerator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>
@@ -24,11 +24,40 @@ using namespace llvm;
namespace lld {
namespace coff {
+enum SymbolPreference {
+ SP_EXISTING = -1,
+ SP_CONFLICT = 0,
+ SP_NEW = 1,
+};
+
+/// Checks if an existing symbol S should be kept or replaced by a new symbol.
+/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol
+/// should be kept, and SP_CONFLICT if no valid resolution exists.
+static SymbolPreference compareDefined(Symbol *S, bool WasInserted,
+ bool NewIsCOMDAT) {
+ // If the symbol wasn't previously known, the new symbol wins by default.
+ if (WasInserted || !isa<Defined>(S->body()))
+ return SP_NEW;
+
+ // If the existing symbol is a DefinedRegular, both it and the new symbol
+ // must be comdats. In that case, we have no reason to prefer one symbol
+ // over the other, and we keep the existing one. If one of the symbols
+ // is not a comdat, we report a conflict.
+ if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
+ if (NewIsCOMDAT && R->isCOMDAT())
+ return SP_EXISTING;
+ else
+ return SP_CONFLICT;
+ }
+
+ // Existing symbol is not a DefinedRegular; new symbol wins.
+ return SP_NEW;
+}
+
SymbolTable *Symtab;
void SymbolTable::addFile(InputFile *File) {
- if (Config->Verbose)
- outs() << "Reading " << toString(File) << "\n";
+ log("Reading " + toString(File));
File->parse();
MachineTypes MT = File->getMachineType();
@@ -51,8 +80,7 @@ void SymbolTable::addFile(InputFile *File) {
if (S.empty())
return;
- if (Config->Verbose)
- outs() << "Directives: " << toString(File) << ": " << S << "\n";
+ log("Directives: " + toString(File) + ": " + S);
Driver->parseDirectives(S);
}
@@ -106,12 +134,11 @@ void SymbolTable::reportRemainingUndefines() {
return;
for (SymbolBody *B : Config->GCRoot)
if (Undefs.count(B))
- errs() << "<root>: undefined symbol: " << B->getName() << "\n";
+ warn("<root>: undefined symbol: " + B->getName());
for (ObjectFile *File : ObjectFiles)
for (SymbolBody *Sym : File->getSymbols())
if (Undefs.count(Sym))
- errs() << toString(File) << ": undefined symbol: " << Sym->getName()
- << "\n";
+ warn(toString(File) + ": undefined symbol: " + Sym->getName());
if (!Config->Force)
fatal("link failed");
}
@@ -163,7 +190,7 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
}
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
- fatal("duplicate symbol: " + toString(*Existing->body()) + " in " +
+ error("duplicate symbol: " + toString(*Existing->body()) + " in " +
toString(Existing->body()->getFile()) + " and in " +
(NewFile ? toString(NewFile) : "(internal)"));
}
@@ -204,59 +231,35 @@ Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) {
return S;
}
-Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym,
+Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
+ const coff_symbol_generic *Sym,
SectionChunk *C) {
- StringRef Name;
- F->getCOFFObj()->getSymbolName(Sym, Name);
- Symbol *S;
- bool WasInserted;
- std::tie(S, WasInserted) = insert(Name);
- S->IsUsedInRegularObj = true;
- if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
- replaceBody<DefinedRegular>(S, F, Sym, C);
- else if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
- if (!C->isCOMDAT() || !R->isCOMDAT())
- reportDuplicate(S, F);
- } else if (auto *B = dyn_cast<DefinedBitcode>(S->body())) {
- if (B->IsReplaceable)
- replaceBody<DefinedRegular>(S, F, Sym, C);
- else if (!C->isCOMDAT())
- reportDuplicate(S, F);
- } else
- replaceBody<DefinedRegular>(S, F, Sym, C);
- return S;
-}
-
-Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) {
Symbol *S;
bool WasInserted;
std::tie(S, WasInserted) = insert(N);
- if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) {
- replaceBody<DefinedBitcode>(S, F, N, IsReplaceable);
- return S;
+ if (!isa<BitcodeFile>(F))
+ S->IsUsedInRegularObj = true;
+ SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT);
+ if (SP == SP_CONFLICT) {
+ reportDuplicate(S, F);
+ } else if (SP == SP_NEW) {
+ replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C);
}
- if (isa<DefinedCommon>(S->body()))
- return S;
- if (IsReplaceable)
- if (isa<DefinedRegular>(S->body()) || isa<DefinedBitcode>(S->body()))
- return S;
- reportDuplicate(S, F);
return S;
}
-Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym,
- CommonChunk *C) {
- StringRef Name;
- F->getCOFFObj()->getSymbolName(Sym, Name);
+Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
+ const coff_symbol_generic *Sym, CommonChunk *C) {
Symbol *S;
bool WasInserted;
- std::tie(S, WasInserted) = insert(Name);
- S->IsUsedInRegularObj = true;
+ std::tie(S, WasInserted) = insert(N);
+ if (!isa<BitcodeFile>(F))
+ S->IsUsedInRegularObj = true;
if (WasInserted || !isa<DefinedCOFF>(S->body()))
- replaceBody<DefinedCommon>(S, F, Sym, C);
+ replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
else if (auto *DC = dyn_cast<DefinedCommon>(S->body()))
- if (Sym.getValue() > DC->getSize())
- replaceBody<DefinedCommon>(S, F, Sym, C);
+ if (Size > DC->getSize())
+ replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
return S;
}
@@ -345,75 +348,21 @@ SymbolBody *SymbolTable::addUndefined(StringRef Name) {
return addUndefined(Name, nullptr, false)->body();
}
-void SymbolTable::printMap(llvm::raw_ostream &OS) {
- for (ObjectFile *File : ObjectFiles) {
- OS << toString(File) << ":\n";
- for (SymbolBody *Body : File->getSymbols())
- if (auto *R = dyn_cast<DefinedRegular>(Body))
- if (R->getChunk()->isLive())
- OS << Twine::utohexstr(Config->ImageBase + R->getRVA())
- << " " << R->getName() << "\n";
- }
+std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
+ LTO.reset(new BitcodeCompiler);
+ for (BitcodeFile *F : BitcodeFiles)
+ LTO->add(*F);
+ return LTO->compile();
}
void SymbolTable::addCombinedLTOObjects() {
if (BitcodeFiles.empty())
return;
-
- // Create an object file and add it to the symbol table by replacing any
- // DefinedBitcode symbols with the definitions in the object file.
- LTOCodeGenerator CG(BitcodeFile::Context);
- CG.setOptLevel(Config->LTOOptLevel);
- for (ObjectFile *Obj : createLTOObjects(&CG))
+ for (StringRef Object : compileBitcodeFiles()) {
+ auto *Obj = make<ObjectFile>(MemoryBufferRef(Object, "lto.tmp"));
Obj->parse();
-}
-
-// Combine and compile bitcode files and then return the result
-// as a vector of regular COFF object files.
-std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
- // All symbols referenced by non-bitcode objects, including GC roots, must be
- // preserved. We must also replace bitcode symbols with undefined symbols so
- // that they may be replaced with real definitions without conflicting.
- for (BitcodeFile *File : BitcodeFiles)
- for (SymbolBody *Body : File->getSymbols()) {
- if (!isa<DefinedBitcode>(Body))
- continue;
- if (Body->symbol()->IsUsedInRegularObj)
- CG->addMustPreserveSymbol(Body->getName());
- replaceBody<Undefined>(Body->symbol(), Body->getName());
- }
-
- CG->setModule(BitcodeFiles[0]->takeModule());
- for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I)
- CG->addModule(BitcodeFiles[I]->takeModule().get());
-
- bool DisableVerify = true;
-#ifdef NDEBUG
- DisableVerify = false;
-#endif
- if (!CG->optimize(DisableVerify, false, false, false))
- fatal(""); // optimize() should have emitted any error message.
-
- Objs.resize(Config->LTOJobs);
- // Use std::list to avoid invalidation of pointers in OSPtrs.
- std::list<raw_svector_ostream> OSs;
- std::vector<raw_pwrite_stream *> OSPtrs;
- for (SmallString<0> &Obj : Objs) {
- OSs.emplace_back(Obj);
- OSPtrs.push_back(&OSs.back());
+ ObjectFiles.push_back(Obj);
}
-
- if (!CG->compileOptimized(OSPtrs))
- fatal(""); // compileOptimized() should have emitted any error message.
-
- std::vector<ObjectFile *> ObjFiles;
- for (SmallString<0> &Obj : Objs) {
- auto *ObjFile = make<ObjectFile>(MemoryBufferRef(Obj, "<LTO object>"));
- ObjectFiles.push_back(ObjFile);
- ObjFiles.push_back(ObjFile);
- }
-
- return ObjFiles;
}
} // namespace coff
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 703821f2e124..764dd5318775 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -11,6 +11,7 @@
#define LLD_COFF_SYMBOL_TABLE_H
#include "InputFiles.h"
+#include "LTO.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
@@ -69,13 +70,11 @@ public:
void mangleMaybe(SymbolBody *B);
StringRef findMangle(StringRef Name);
- // Print a layout map to OS.
- void printMap(llvm::raw_ostream &OS);
-
// Build a set of COFF objects representing the combined contents of
// BitcodeFiles and add them to the symbol table. Called after all files are
// added and before the writer writes results to a file.
void addCombinedLTOObjects();
+ std::vector<StringRef> compileBitcodeFiles();
// The writer needs to handle DLL import libraries specially in
// order to create the import descriptor table.
@@ -93,9 +92,12 @@ public:
Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
- Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C);
- Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable);
- Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C);
+ Symbol *addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
+ const llvm::object::coff_symbol_generic *S = nullptr,
+ SectionChunk *C = nullptr);
+ Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
+ const llvm::object::coff_symbol_generic *S = nullptr,
+ CommonChunk *C = nullptr);
Symbol *addImportData(StringRef N, ImportFile *F);
Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
uint16_t Machine);
@@ -113,12 +115,11 @@ private:
StringRef findByPrefix(StringRef Prefix);
void addCombinedLTOObject(ObjectFile *Obj);
- std::vector<ObjectFile *> createLTOObjects(llvm::LTOCodeGenerator *CG);
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
std::vector<BitcodeFile *> BitcodeFiles;
- std::vector<SmallString<0>> Objs;
+ std::unique_ptr<BitcodeCompiler> LTO;
};
extern SymbolTable *Symtab;
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index c44537d37135..993e920ce7f7 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -30,7 +30,7 @@ namespace lld {
namespace coff {
StringRef SymbolBody::getName() {
- // DefinedCOFF names are read lazily for a performance reason.
+ // COFF symbol names are read lazily for a performance reason.
// Non-external symbol names are never used by the linker except for logging
// or debugging. Their internal references are resolved not by name but by
// symbol index. And because they are not external, no one can refer them by
@@ -39,7 +39,7 @@ StringRef SymbolBody::getName() {
// is a waste of time.
if (Name.empty()) {
auto *D = cast<DefinedCOFF>(this);
- D->File->getCOFFObj()->getSymbolName(D->Sym, Name);
+ cast<ObjectFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
}
return Name;
}
@@ -47,15 +47,14 @@ StringRef SymbolBody::getName() {
InputFile *SymbolBody::getFile() {
if (auto *Sym = dyn_cast<DefinedCOFF>(this))
return Sym->File;
- if (auto *Sym = dyn_cast<DefinedBitcode>(this))
- return Sym->File;
if (auto *Sym = dyn_cast<Lazy>(this))
return Sym->File;
return nullptr;
}
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
- size_t SymSize = File->getCOFFObj()->getSymbolTableEntrySize();
+ size_t SymSize =
+ cast<ObjectFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
if (SymSize == sizeof(coff_symbol16))
return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym));
assert(SymSize == sizeof(coff_symbol32));
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index 1ca7366364d7..1b83f73ff20c 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -30,7 +30,6 @@ using llvm::object::coff_import_header;
using llvm::object::coff_symbol_generic;
class ArchiveFile;
-class BitcodeFile;
class InputFile;
class ObjectFile;
struct Symbol;
@@ -52,13 +51,12 @@ public:
DefinedImportDataKind,
DefinedAbsoluteKind,
DefinedRelativeKind,
- DefinedBitcodeKind,
UndefinedKind,
LazyKind,
LastDefinedCOFFKind = DefinedCommonKind,
- LastDefinedKind = DefinedBitcodeKind,
+ LastDefinedKind = DefinedRelativeKind,
};
Kind kind() const { return static_cast<Kind>(SymbolKind); }
@@ -81,7 +79,7 @@ protected:
friend SymbolTable;
explicit SymbolBody(Kind K, StringRef N = "")
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
- IsReplaceable(false), WrittenToSymtab(false), Name(N) {}
+ WrittenToSymtab(false), Name(N) {}
const unsigned SymbolKind : 8;
unsigned IsExternal : 1;
@@ -89,11 +87,9 @@ protected:
// This bit is used by the \c DefinedRegular subclass.
unsigned IsCOMDAT : 1;
- // This bit is used by the \c DefinedBitcode subclass.
- unsigned IsReplaceable : 1;
-
public:
- // This bit is used by Writer::createSymbolAndStringTable().
+ // This bit is used by Writer::createSymbolAndStringTable() to prevent
+ // symbols from being written to the symbol table more than once.
unsigned WrittenToSymtab : 1;
protected:
@@ -104,7 +100,7 @@ protected:
// etc.
class Defined : public SymbolBody {
public:
- Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {}
+ Defined(Kind K, StringRef N) : SymbolBody(K, N) {}
static bool classof(const SymbolBody *S) {
return S->kind() <= LastDefinedKind;
@@ -127,22 +123,25 @@ public:
bool isExecutable();
};
-// Symbols defined via a COFF object file.
+// Symbols defined via a COFF object file or bitcode file. For COFF files, this
+// stores a coff_symbol_generic*, and names of internal symbols are lazily
+// loaded through that. For bitcode files, Sym is nullptr and the name is stored
+// as a StringRef.
class DefinedCOFF : public Defined {
friend SymbolBody;
public:
- DefinedCOFF(Kind K, ObjectFile *F, COFFSymbolRef S)
- : Defined(K), File(F), Sym(S.getGeneric()) {}
+ DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S)
+ : Defined(K, N), File(F), Sym(S) {}
static bool classof(const SymbolBody *S) {
return S->kind() <= LastDefinedCOFFKind;
}
- ObjectFile *getFile() { return File; }
+ InputFile *getFile() { return File; }
COFFSymbolRef getCOFFSymbol();
- ObjectFile *File;
+ InputFile *File;
protected:
const coff_symbol_generic *Sym;
@@ -151,10 +150,13 @@ protected:
// Regular defined symbols read from object file symbol tables.
class DefinedRegular : public DefinedCOFF {
public:
- DefinedRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C)
- : DefinedCOFF(DefinedRegularKind, F, S), Data(&C->Repl) {
- IsExternal = S.isExternal();
- IsCOMDAT = C->isCOMDAT();
+ DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT,
+ bool IsExternal = false,
+ const coff_symbol_generic *S = nullptr,
+ SectionChunk *C = nullptr)
+ : DefinedCOFF(DefinedRegularKind, F, N, S), Data(C ? &C->Repl : nullptr) {
+ this->IsExternal = IsExternal;
+ this->IsCOMDAT = IsCOMDAT;
}
static bool classof(const SymbolBody *S) {
@@ -172,9 +174,11 @@ private:
class DefinedCommon : public DefinedCOFF {
public:
- DefinedCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C)
- : DefinedCOFF(DefinedCommonKind, F, S), Data(C) {
- IsExternal = S.isExternal();
+ DefinedCommon(InputFile *F, StringRef N, uint64_t Size,
+ const coff_symbol_generic *S = nullptr,
+ CommonChunk *C = nullptr)
+ : DefinedCOFF(DefinedCommonKind, F, N, S), Data(C), Size(Size) {
+ this->IsExternal = true;
}
static bool classof(const SymbolBody *S) {
@@ -185,8 +189,9 @@ public:
private:
friend SymbolTable;
- uint64_t getSize() { return Sym->Value; }
+ uint64_t getSize() const { return Size; }
CommonChunk *Data;
+ uint64_t Size;
};
// Absolute symbols.
@@ -340,26 +345,6 @@ private:
LocalImportChunk *Data;
};
-class DefinedBitcode : public Defined {
- friend SymbolBody;
-public:
- DefinedBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable)
- : Defined(DefinedBitcodeKind, N), File(F) {
- // IsReplaceable tracks whether the bitcode symbol may be replaced with some
- // other (defined, common or bitcode) symbol. This is the case for common,
- // comdat and weak external symbols. We try to replace bitcode symbols with
- // "real" symbols (see SymbolTable::add{Regular,Bitcode}), and resolve the
- // result against the real symbol from the combined LTO object.
- this->IsReplaceable = IsReplaceable;
- }
-
- static bool classof(const SymbolBody *S) {
- return S->kind() == DefinedBitcodeKind;
- }
-
- BitcodeFile *File;
-};
-
inline uint64_t Defined::getRVA() {
switch (kind()) {
case DefinedAbsoluteKind:
@@ -376,8 +361,6 @@ inline uint64_t Defined::getRVA() {
return cast<DefinedCommon>(this)->getRVA();
case DefinedRegularKind:
return cast<DefinedRegular>(this)->getRVA();
- case DefinedBitcodeKind:
- llvm_unreachable("There is no address for a bitcode symbol.");
case LazyKind:
case UndefinedKind:
llvm_unreachable("Cannot get the address for an undefined symbol.");
@@ -401,10 +384,9 @@ struct Symbol {
// This field is used to store the Symbol's SymbolBody. This instantiation of
// AlignedCharArrayUnion gives us a struct with a char array field that is
// large and aligned enough to store any derived class of SymbolBody.
- llvm::AlignedCharArrayUnion<DefinedRegular, DefinedCommon, DefinedAbsolute,
- DefinedRelative, Lazy, Undefined,
- DefinedImportData, DefinedImportThunk,
- DefinedLocalImport, DefinedBitcode>
+ llvm::AlignedCharArrayUnion<
+ DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy,
+ Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport>
Body;
SymbolBody *body() {
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index 71217ebeb60a..8762b88c4d6b 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -12,6 +12,7 @@
#include "DLL.h"
#include "Error.h"
#include "InputFiles.h"
+#include "MapFile.h"
#include "Memory.h"
#include "PDB.h"
#include "SymbolTable.h"
@@ -39,7 +40,6 @@ using namespace llvm::support::endian;
using namespace lld;
using namespace lld::coff;
-static const int PageSize = 4096;
static const int SectorSize = 512;
static const int DOSStubSize = 64;
static const int NumberfOfDataDirectory = 16;
@@ -163,51 +163,6 @@ namespace coff {
void writeResult(SymbolTable *T) { Writer(T).run(); }
-// OutputSection represents a section in an output file. It's a
-// container of chunks. OutputSection and Chunk are 1:N relationship.
-// Chunks cannot belong to more than one OutputSections. The writer
-// creates multiple OutputSections and assign them unique,
-// non-overlapping file offsets and RVAs.
-class OutputSection {
-public:
- OutputSection(StringRef N) : Name(N), Header({}) {}
- void setRVA(uint64_t);
- void setFileOffset(uint64_t);
- void addChunk(Chunk *C);
- StringRef getName() { return Name; }
- std::vector<Chunk *> &getChunks() { return Chunks; }
- void addPermissions(uint32_t C);
- void setPermissions(uint32_t C);
- uint32_t getPermissions() { return Header.Characteristics & PermMask; }
- uint32_t getCharacteristics() { return Header.Characteristics; }
- uint64_t getRVA() { return Header.VirtualAddress; }
- uint64_t getFileOff() { return Header.PointerToRawData; }
- void writeHeaderTo(uint8_t *Buf);
-
- // Returns the size of this section in an executable memory image.
- // This may be smaller than the raw size (the raw size is multiple
- // of disk sector size, so there may be padding at end), or may be
- // larger (if that's the case, the loader reserves spaces after end
- // of raw data).
- uint64_t getVirtualSize() { return Header.VirtualSize; }
-
- // Returns the size of the section in the output file.
- uint64_t getRawSize() { return Header.SizeOfRawData; }
-
- // Set offset into the string table storing this section name.
- // Used only when the name is longer than 8 bytes.
- void setStringTableOff(uint32_t V) { StringTableOff = V; }
-
- // N.B. The section index is one based.
- uint32_t SectionIndex = 0;
-
-private:
- StringRef Name;
- coff_section Header;
- uint32_t StringTableOff = 0;
- std::vector<Chunk *> Chunks;
-};
-
void OutputSection::setRVA(uint64_t RVA) {
Header.VirtualAddress = RVA;
for (Chunk *C : Chunks)
@@ -303,8 +258,14 @@ void Writer::run() {
sortExceptionTable();
writeBuildId();
- if (!Config->PDBPath.empty())
- createPDB(Config->PDBPath, Symtab, SectionTable, BuildId->DI);
+ if (!Config->PDBPath.empty()) {
+ const llvm::codeview::DebugInfo *DI = nullptr;
+ if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV))
+ DI = BuildId->DI;
+ createPDB(Config->PDBPath, Symtab, SectionTable, DI);
+ }
+
+ writeMapFile(OutputSections);
if (auto EC = Buffer->commit())
fatal(EC, "failed to write the output file");
@@ -641,6 +602,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
PE->SizeOfStackCommit = Config->StackCommit;
PE->SizeOfHeapReserve = Config->HeapReserve;
PE->SizeOfHeapCommit = Config->HeapCommit;
+ if (Config->AppContainer)
+ PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER;
if (Config->DynamicBase)
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
if (Config->HighEntropyVA)
@@ -830,7 +793,7 @@ void Writer::writeBuildId() {
"only PDB 7.0 is supported");
assert(sizeof(Res) == sizeof(BuildId->DI->PDB70.Signature) &&
"signature size mismatch");
- memcpy(BuildId->DI->PDB70.Signature, Res,
+ memcpy(BuildId->DI->PDB70.Signature, Res.Bytes.data(),
sizeof(codeview::PDB70DebugInfo::Signature));
// TODO(compnerd) track the Age
BuildId->DI->PDB70.Age = 1;
diff --git a/COFF/Writer.h b/COFF/Writer.h
index 0d26090177d8..fef575423878 100644
--- a/COFF/Writer.h
+++ b/COFF/Writer.h
@@ -10,14 +10,65 @@
#ifndef LLD_COFF_WRITER_H
#define LLD_COFF_WRITER_H
+#include "Chunks.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/COFF.h"
+#include <cstdint>
#include <vector>
namespace lld {
namespace coff {
class SymbolTable;
+static const int PageSize = 4096;
+
void writeResult(SymbolTable *T);
+// OutputSection represents a section in an output file. It's a
+// container of chunks. OutputSection and Chunk are 1:N relationship.
+// Chunks cannot belong to more than one OutputSections. The writer
+// creates multiple OutputSections and assign them unique,
+// non-overlapping file offsets and RVAs.
+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; }
+ std::vector<Chunk *> &getChunks() { return Chunks; }
+ void addPermissions(uint32_t C);
+ void setPermissions(uint32_t C);
+ uint32_t getPermissions() { return Header.Characteristics & PermMask; }
+ uint32_t getCharacteristics() { return Header.Characteristics; }
+ uint64_t getRVA() { return Header.VirtualAddress; }
+ uint64_t getFileOff() { return Header.PointerToRawData; }
+ void writeHeaderTo(uint8_t *Buf);
+
+ // Returns the size of this section in an executable memory image.
+ // This may be smaller than the raw size (the raw size is multiple
+ // of disk sector size, so there may be padding at end), or may be
+ // larger (if that's the case, the loader reserves spaces after end
+ // of raw data).
+ uint64_t getVirtualSize() { return Header.VirtualSize; }
+
+ // Returns the size of the section in the output file.
+ uint64_t getRawSize() { return Header.SizeOfRawData; }
+
+ // Set offset into the string table storing this section name.
+ // Used only when the name is longer than 8 bytes.
+ void setStringTableOff(uint32_t V) { StringTableOff = V; }
+
+ // N.B. The section index is one based.
+ uint32_t SectionIndex = 0;
+
+private:
+ llvm::StringRef Name;
+ llvm::object::coff_section Header;
+ uint32_t StringTableOff = 0;
+ std::vector<Chunk *> Chunks;
+};
+
}
}