aboutsummaryrefslogtreecommitdiffstats
path: root/COFF
diff options
context:
space:
mode:
Diffstat (limited to 'COFF')
-rw-r--r--COFF/CMakeLists.txt2
-rw-r--r--COFF/Config.h5
-rw-r--r--COFF/DLL.cpp4
-rw-r--r--COFF/DebugTypes.cpp27
-rw-r--r--COFF/Driver.cpp255
-rw-r--r--COFF/Driver.h22
-rw-r--r--COFF/DriverUtils.cpp59
-rw-r--r--COFF/ICF.cpp8
-rw-r--r--COFF/InputFiles.cpp156
-rw-r--r--COFF/InputFiles.h59
-rw-r--r--COFF/LTO.cpp17
-rw-r--r--COFF/MapFile.cpp15
-rw-r--r--COFF/MinGW.cpp14
-rw-r--r--COFF/Options.td39
-rw-r--r--COFF/PDB.cpp59
-rw-r--r--COFF/PDB.h9
-rw-r--r--COFF/SymbolTable.cpp366
-rw-r--r--COFF/SymbolTable.h22
-rw-r--r--COFF/Symbols.cpp43
-rw-r--r--COFF/Symbols.h52
-rw-r--r--COFF/Writer.cpp94
21 files changed, 950 insertions, 377 deletions
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt
index c7ef7c47fea1..7c5e8b79b7fe 100644
--- a/COFF/CMakeLists.txt
+++ b/COFF/CMakeLists.txt
@@ -28,8 +28,10 @@ add_lld_library(lldCOFF
BinaryFormat
Core
DebugInfoCodeView
+ DebugInfoDWARF
DebugInfoMSF
DebugInfoPDB
+ Demangle
LibDriver
LTO
MC
diff --git a/COFF/Config.h b/COFF/Config.h
index 1b0e24042710..309e1fbf99e3 100644
--- a/COFF/Config.h
+++ b/COFF/Config.h
@@ -122,6 +122,7 @@ struct Configuration {
bool dll = false;
StringRef implib;
std::vector<Export> exports;
+ bool hadExplicitExports;
std::set<std::string> delayLoads;
std::map<std::string, int> dllOrder;
Symbol *delayLoadHelper = nullptr;
@@ -189,6 +190,10 @@ struct Configuration {
// Used for /thinlto-object-suffix-replace:
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
+ // Used for /lto-obj-path:
+ llvm::StringRef ltoObjPath;
+
+ uint64_t align = 4096;
uint64_t imageBase = -1;
uint64_t fileAlign = 512;
uint64_t stackReserve = 1024 * 1024;
diff --git a/COFF/DLL.cpp b/COFF/DLL.cpp
index 40d1f463aa3f..39d9fbab63d5 100644
--- a/COFF/DLL.cpp
+++ b/COFF/DLL.cpp
@@ -135,7 +135,7 @@ private:
static std::vector<std::vector<DefinedImportData *>>
binImports(const std::vector<DefinedImportData *> &imports) {
// Group DLL-imported symbols by DLL name because that's how
- // symbols are layed out in the import descriptor table.
+ // symbols are laid out in the import descriptor table.
auto less = [](const std::string &a, const std::string &b) {
return config->dllOrder[a] < config->dllOrder[b];
};
@@ -188,7 +188,7 @@ public:
// Initial contents for delay-loaded functions.
// This code calls __delayLoadHelper2 function to resolve a symbol
-// and then overwrites its jump table slot with the result
+// which then overwrites its jump table slot with the result
// for subsequent function calls.
static const uint8_t thunkX64[] = {
0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]
diff --git a/COFF/DebugTypes.cpp b/COFF/DebugTypes.cpp
index 78c1c78b408d..6c7d70ee8dcb 100644
--- a/COFF/DebugTypes.cpp
+++ b/COFF/DebugTypes.cpp
@@ -17,11 +17,12 @@
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/Support/Path.h"
-using namespace lld;
-using namespace lld::coff;
using namespace llvm;
using namespace llvm::codeview;
+namespace lld {
+namespace coff {
+
namespace {
// The TypeServerSource class represents a PDB type server, a file referenced by
// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
@@ -96,27 +97,25 @@ TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {
GC.push_back(std::unique_ptr<TpiSource>(this));
}
-TpiSource *lld::coff::makeTpiSource(const ObjFile *f) {
+TpiSource *makeTpiSource(const ObjFile *f) {
return new TpiSource(TpiSource::Regular, f);
}
-TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f,
+TpiSource *makeUseTypeServerSource(const ObjFile *f,
const TypeServer2Record *ts) {
TypeServerSource::enqueue(f, *ts);
return new UseTypeServerSource(f, ts);
}
-TpiSource *lld::coff::makePrecompSource(const ObjFile *f) {
+TpiSource *makePrecompSource(const ObjFile *f) {
return new PrecompSource(f);
}
-TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f,
+TpiSource *makeUsePrecompSource(const ObjFile *f,
const PrecompRecord *precomp) {
return new UsePrecompSource(f, precomp);
}
-namespace lld {
-namespace coff {
template <>
const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
assert(source->kind == TpiSource::UsingPCH);
@@ -128,8 +127,6 @@ const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
assert(source->kind == TpiSource::UsingPDB);
return ((const UseTypeServerSource *)source)->typeServerDependency;
}
-} // namespace coff
-} // namespace lld
std::map<std::string, std::pair<std::string, TypeServerSource *>>
TypeServerSource::instances;
@@ -210,8 +207,7 @@ TypeServerSource::findFromFile(const ObjFile *dependentFile) {
// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
// moved here.
-Expected<llvm::pdb::NativeSession *>
-lld::coff::findTypeServerSource(const ObjFile *f) {
+Expected<llvm::pdb::NativeSession *> findTypeServerSource(const ObjFile *f) {
Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
if (!ts)
return ts.takeError();
@@ -231,7 +227,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
if (!it.second)
return; // another OBJ already scheduled this PDB for load
- driver->enqueuePath(*p, false);
+ driver->enqueuePath(*p, false, false);
}
// Create an instance of TypeServerSource or an error string if the PDB couldn't
@@ -239,7 +235,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
// will be merged in. NOTE - a PDB load failure is not a link error: some
// debug info will simply be missing from the final PDB - that is the default
// accepted behavior.
-void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) {
+void loadTypeServerSource(llvm::MemoryBufferRef m) {
std::string path = normalizePdbPath(m.getBufferIdentifier());
Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
@@ -266,3 +262,6 @@ Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
return info.takeError();
return new TypeServerSource(m, session.release());
}
+
+} // namespace coff
+} // namespace lld
diff --git a/COFF/Driver.cpp b/COFF/Driver.cpp
index d7af50b9318f..30967a39b4ca 100644
--- a/COFF/Driver.cpp
+++ b/COFF/Driver.cpp
@@ -27,6 +27,7 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/LTO/LTO.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/COFFModuleDefinition.h"
@@ -36,6 +37,7 @@
#include "llvm/Option/Option.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TarWriter.h"
@@ -62,16 +64,16 @@ LinkerDriver *driver;
bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) {
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
errorHandler().errorOS = &diag;
- errorHandler().colorDiagnostics = diag.has_colors();
errorHandler().errorLimitExceededMsg =
"too many errors emitted, stopping now"
" (use /errorlimit:0 to see all errors)";
errorHandler().exitEarly = canExitEarly;
- config = make<Configuration>();
+ enableColors(diag.has_colors());
+ config = make<Configuration>();
symtab = make<SymbolTable>();
-
driver = make<LinkerDriver>();
+
driver->link(args);
// Call exit() if we can to avoid calling destructors.
@@ -169,7 +171,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
}
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
- bool wholeArchive) {
+ bool wholeArchive, bool lazy) {
StringRef filename = mb->getBufferIdentifier();
MemoryBufferRef mbref = takeBuffer(std::move(mb));
@@ -184,19 +186,28 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
if (wholeArchive) {
std::unique_ptr<Archive> file =
CHECK(Archive::create(mbref), filename + ": failed to parse archive");
+ Archive *archive = file.get();
+ make<std::unique_ptr<Archive>>(std::move(file)); // take ownership
- for (MemoryBufferRef m : getArchiveMembers(file.get()))
- addArchiveBuffer(m, "<whole-archive>", filename, 0);
+ int memberIndex = 0;
+ for (MemoryBufferRef m : getArchiveMembers(archive))
+ addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++);
return;
}
symtab->addFile(make<ArchiveFile>(mbref));
break;
case file_magic::bitcode:
- symtab->addFile(make<BitcodeFile>(mbref, "", 0));
+ if (lazy)
+ symtab->addFile(make<LazyObjFile>(mbref));
+ else
+ symtab->addFile(make<BitcodeFile>(mbref, "", 0));
break;
case file_magic::coff_object:
case file_magic::coff_import_library:
- symtab->addFile(make<ObjFile>(mbref));
+ if (lazy)
+ symtab->addFile(make<LazyObjFile>(mbref));
+ else
+ symtab->addFile(make<ObjFile>(mbref));
break;
case file_magic::pdb:
loadTypeServerSource(mbref);
@@ -217,7 +228,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
}
}
-void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
+void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
auto future =
std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
std::string pathStr = path;
@@ -237,7 +248,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
else
error(msg + "; did you mean '" + nearest + "'");
} else
- driver->addBuffer(std::move(mbOrErr.first), wholeArchive);
+ driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
});
}
@@ -268,13 +279,12 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef mb, StringRef symName,
}
void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
- StringRef symName,
+ const Archive::Symbol &sym,
StringRef parentName) {
- auto reportBufferError = [=](Error &&e,
- StringRef childName) {
+ auto reportBufferError = [=](Error &&e, StringRef childName) {
fatal("could not get the buffer for the member defining symbol " +
- symName + ": " + parentName + "(" + childName + "): " +
+ toCOFFString(sym) + ": " + parentName + "(" + childName + "): " +
toString(std::move(e)));
};
@@ -285,7 +295,8 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
reportBufferError(mbOrErr.takeError(), check(c.getFullName()));
MemoryBufferRef mb = mbOrErr.get();
enqueueTask([=]() {
- driver->addArchiveBuffer(mb, symName, parentName, offsetInArchive);
+ driver->addArchiveBuffer(mb, toCOFFString(sym), parentName,
+ offsetInArchive);
});
return;
}
@@ -293,15 +304,17 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
std::string childName = CHECK(
c.getFullName(),
"could not get the filename for the member defining symbol " +
- symName);
+ toCOFFString(sym));
auto future = std::make_shared<std::future<MBErrPair>>(
createFutureForFile(childName));
enqueueTask([=]() {
auto mbOrErr = future->get();
if (mbOrErr.second)
reportBufferError(errorCodeToError(mbOrErr.second), childName);
- driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), symName,
- parentName, /* OffsetInArchive */ 0);
+ // Pass empty string as archive name so that the original filename is
+ // used as the buffer identifier.
+ driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
+ toCOFFString(sym), "", /*OffsetInArchive=*/0);
});
}
@@ -355,7 +368,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
break;
case OPT_defaultlib:
if (Optional<StringRef> path = findLib(arg->getValue()))
- enqueuePath(*path, false);
+ enqueuePath(*path, false, false);
break;
case OPT_entry:
config->entry = addUndefined(mangle(arg->getValue()));
@@ -590,6 +603,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
for (auto *arg : args) {
switch (arg->getOption().getID()) {
case OPT_linkrepro:
+ case OPT_reproduce:
case OPT_INPUT:
case OPT_defaultlib:
case OPT_libpath:
@@ -704,8 +718,7 @@ static std::string getImplibPath() {
return out.str();
}
-//
-// The import name is caculated as the following:
+// The import name is calculated as follows:
//
// | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY
// -----+----------------+---------------------+------------------
@@ -987,30 +1000,37 @@ static void parsePDBAltPath(StringRef altPath) {
config->pdbAltPath = buf;
}
-/// Check that at most one resource obj file was used.
+/// Convert resource files and potentially merge input resource object
+/// trees into one resource tree.
/// Call after ObjFile::Instances is complete.
-static void diagnoseMultipleResourceObjFiles() {
- // The .rsrc$01 section in a resource obj file contains a tree description
- // of resources. Merging multiple resource obj files would require merging
- // the trees instead of using usual linker section merging semantics.
- // Since link.exe disallows linking more than one resource obj file with
- // LNK4078, mirror that. The normal use of resource files is to give the
- // linker many .res files, which are then converted to a single resource obj
- // file internally, so this is not a big restriction in practice.
- ObjFile *resourceObjFile = nullptr;
- for (ObjFile *f : ObjFile::instances) {
- if (!f->isResourceObjFile)
- continue;
+void LinkerDriver::convertResources() {
+ std::vector<ObjFile *> resourceObjFiles;
- if (!resourceObjFile) {
- resourceObjFile = f;
- continue;
- }
+ for (ObjFile *f : ObjFile::instances) {
+ if (f->isResourceObjFile())
+ resourceObjFiles.push_back(f);
+ }
- error(toString(f) +
+ if (!config->mingw &&
+ (resourceObjFiles.size() > 1 ||
+ (resourceObjFiles.size() == 1 && !resources.empty()))) {
+ error((!resources.empty() ? "internal .obj file created from .res files"
+ : toString(resourceObjFiles[1])) +
": more than one resource obj file not allowed, already got " +
- toString(resourceObjFile));
+ toString(resourceObjFiles.front()));
+ return;
+ }
+
+ if (resources.empty() && resourceObjFiles.size() <= 1) {
+ // No resources to convert, and max one resource object file in
+ // the input. Keep that preconverted resource section as is.
+ for (ObjFile *f : resourceObjFiles)
+ f->includeResourceChunks();
+ return;
}
+ ObjFile *f = make<ObjFile>(convertResToCOFF(resources, resourceObjFiles));
+ symtab->addFile(f);
+ f->includeResourceChunks();
}
// In MinGW, if no symbols are chosen to be exported, then all symbols are
@@ -1051,6 +1071,26 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
});
}
+// lld has a feature to create a tar file containing all input files as well as
+// all command line options, so that other people can run lld again with exactly
+// the same inputs. This feature is accessible via /linkrepro and /reproduce.
+//
+// /linkrepro and /reproduce are very similar, but /linkrepro takes a directory
+// name while /reproduce takes a full path. We have /linkrepro for compatibility
+// with Microsoft link.exe.
+Optional<std::string> getReproduceFile(const opt::InputArgList &args) {
+ if (auto *arg = args.getLastArg(OPT_reproduce))
+ return std::string(arg->getValue());
+
+ if (auto *arg = args.getLastArg(OPT_linkrepro)) {
+ SmallString<64> path = StringRef(arg->getValue());
+ sys::path::append(path, "repro.tar");
+ return path.str().str();
+ }
+
+ return None;
+}
+
void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Needed for LTO.
InitializeAllTargetInfos();
@@ -1069,7 +1109,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Parse command line options.
ArgParser parser;
- opt::InputArgList args = parser.parseLINK(argsArr);
+ opt::InputArgList args = parser.parse(argsArr);
// Parse and evaluate -mllvm options.
std::vector<const char *> v;
@@ -1113,17 +1153,15 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// options are handled.
config->mingw = args.hasArg(OPT_lldmingw);
- if (auto *arg = args.getLastArg(OPT_linkrepro)) {
- SmallString<64> path = StringRef(arg->getValue());
- sys::path::append(path, "repro.tar");
-
+ // Handle /linkrepro and /reproduce.
+ if (Optional<std::string> path = getReproduceFile(args)) {
Expected<std::unique_ptr<TarWriter>> errOrWriter =
- TarWriter::create(path, "repro");
+ TarWriter::create(*path, sys::path::stem(*path));
if (errOrWriter) {
tar = std::move(*errOrWriter);
} else {
- error("/linkrepro: failed to open " + path + ": " +
+ error("/linkrepro: failed to open " + *path + ": " +
toString(errOrWriter.takeError()));
}
}
@@ -1139,7 +1177,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
searchPaths.push_back("");
for (auto *arg : args.filtered(OPT_libpath))
searchPaths.push_back(arg->getValue());
- addLibSearchPaths();
+ if (!args.hasArg(OPT_lldignoreenv))
+ addLibSearchPaths();
// Handle /ignore
for (auto *arg : args.filtered(OPT_ignore)) {
@@ -1419,6 +1458,13 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
for (auto *arg : args.filtered(OPT_section))
parseSection(arg->getValue());
+ // Handle /align
+ if (auto *arg = args.getLastArg(OPT_align)) {
+ parseNumbers(arg->getValue(), &config->align);
+ if (!isPowerOf2_64(config->align))
+ error("/align: not a power of two: " + StringRef(arg->getValue()));
+ }
+
// Handle /aligncomm
for (auto *arg : args.filtered(OPT_aligncomm))
parseAligncomm(arg->getValue());
@@ -1464,6 +1510,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
getOldNewOptions(args, OPT_thinlto_prefix_replace);
config->thinLTOObjectSuffixReplace =
getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
+ config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path);
// Handle miscellaneous boolean flags.
config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
config->allowIsolation =
@@ -1528,19 +1575,45 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
return false;
};
- // Create a list of input files. Files can be given as arguments
- // for /defaultlib option.
- for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file))
- if (Optional<StringRef> path = findFile(arg->getValue()))
- enqueuePath(*path, isWholeArchive(*path));
+ // Create a list of input files. These can be given as OPT_INPUT options
+ // and OPT_wholearchive_file options, and we also need to track OPT_start_lib
+ // and OPT_end_lib.
+ bool inLib = false;
+ for (auto *arg : args) {
+ switch (arg->getOption().getID()) {
+ case OPT_end_lib:
+ if (!inLib)
+ error("stray " + arg->getSpelling());
+ inLib = false;
+ break;
+ case OPT_start_lib:
+ if (inLib)
+ error("nested " + arg->getSpelling());
+ inLib = true;
+ break;
+ case OPT_wholearchive_file:
+ if (Optional<StringRef> path = findFile(arg->getValue()))
+ enqueuePath(*path, true, inLib);
+ break;
+ case OPT_INPUT:
+ if (Optional<StringRef> path = findFile(arg->getValue()))
+ enqueuePath(*path, isWholeArchive(*path), inLib);
+ break;
+ default:
+ // Ignore other options.
+ break;
+ }
+ }
+ // Process files specified as /defaultlib. These should be enequeued after
+ // other files, which is why they are in a separate loop.
for (auto *arg : args.filtered(OPT_defaultlib))
if (Optional<StringRef> path = findLib(arg->getValue()))
- enqueuePath(*path, false);
+ enqueuePath(*path, false, false);
// Windows specific -- Create a resource file containing a manifest file.
if (config->manifest == Configuration::Embed)
- addBuffer(createManifestRes(), false);
+ addBuffer(createManifestRes(), false, false);
// Read all input files given via the command line.
run();
@@ -1565,12 +1638,6 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
parseFunctionPadMin(arg, config->machine);
- // Input files can be Windows resource files (.res files). We use
- // WindowsResource to convert resource files to a regular COFF file,
- // then link the resulting file normally.
- if (!resources.empty())
- symtab->addFile(make<ObjFile>(convertResToCOFF(resources)));
-
if (tar)
tar->append("response.txt",
createResponseFile(args, filePaths,
@@ -1746,33 +1813,24 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
u->weakAlias = symtab->addUndefined(to);
}
+ // If any inputs are bitcode files, the LTO code generator may create
+ // references to library functions that are not explicit in the bitcode
+ // file's symbol table. If any of those library functions are defined in a
+ // bitcode file in an archive member, we need to arrange to use LTO to
+ // compile those archive members by adding them to the link beforehand.
+ if (!BitcodeFile::instances.empty())
+ for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
+ symtab->addLibcall(s);
+
// Windows specific -- if __load_config_used can be resolved, resolve it.
if (symtab->findUnderscore("_load_config_used"))
addUndefined(mangle("_load_config_used"));
} while (run());
- if (errorCount())
- return;
-
- // Do LTO by compiling bitcode input files to a set of native COFF files then
- // link those files (unless -thinlto-index-only was given, in which case we
- // resolve symbols and write indices, but don't generate native code or link).
- symtab->addCombinedLTOObjects();
-
- // If -thinlto-index-only is given, we should create only "index
- // files" and not object files. Index file creation is already done
- // in addCombinedLTOObject, so we are done if that's the case.
- if (config->thinLTOIndexOnly)
- return;
-
- // If we generated native object files from bitcode files, this resolves
- // references to the symbols we use from them.
- run();
-
if (args.hasArg(OPT_include_optional)) {
// Handle /includeoptional
for (auto *arg : args.filtered(OPT_include_optional))
- if (dyn_cast_or_null<Lazy>(symtab->find(arg->getValue())))
+ if (dyn_cast_or_null<LazyArchive>(symtab->find(arg->getValue())))
addUndefined(arg->getValue());
while (run());
}
@@ -1795,11 +1853,36 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
run();
}
- // Make sure we have resolved all symbols.
- symtab->reportRemainingUndefines();
+ // At this point, we should not have any symbols that cannot be resolved.
+ // If we are going to do codegen for link-time optimization, check for
+ // unresolvable symbols first, so we don't spend time generating code that
+ // will fail to link anyway.
+ if (!BitcodeFile::instances.empty() && !config->forceUnresolved)
+ symtab->reportUnresolvable();
+ if (errorCount())
+ return;
+
+ // Do LTO by compiling bitcode input files to a set of native COFF files then
+ // link those files (unless -thinlto-index-only was given, in which case we
+ // resolve symbols and write indices, but don't generate native code or link).
+ symtab->addCombinedLTOObjects();
+
+ // If -thinlto-index-only is given, we should create only "index
+ // files" and not object files. Index file creation is already done
+ // in addCombinedLTOObject, so we are done if that's the case.
+ if (config->thinLTOIndexOnly)
+ return;
+
+ // If we generated native object files from bitcode files, this resolves
+ // references to the symbols we use from them.
+ run();
+
+ // Resolve remaining undefined symbols and warn about imported locals.
+ symtab->resolveRemainingUndefines();
if (errorCount())
return;
+ config->hadExplicitExports = !config->exports.empty();
if (config->mingw) {
// In MinGW, all symbols are automatically exported if no symbols
// are chosen to be exported.
@@ -1823,10 +1906,12 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
}
// Windows specific -- when we are creating a .dll file, we also
- // need to create a .lib file.
+ // need to create a .lib file. In MinGW mode, we only do that when the
+ // -implib option is given explicitly, for compatibility with GNU ld.
if (!config->exports.empty() || config->dll) {
fixupExports();
- createImportLibrary(/*asLib=*/false);
+ if (!config->mingw || !config->implib.empty())
+ createImportLibrary(/*asLib=*/false);
assignExportOrdinals();
}
@@ -1870,7 +1955,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
markLive(symtab->getChunks());
// Needs to happen after the last call to addFile().
- diagnoseMultipleResourceObjFiles();
+ convertResources();
// Identify identical COMDAT sections to merge them.
if (config->doICF) {
diff --git a/COFF/Driver.h b/COFF/Driver.h
index 6100c3ca0c9e..cc2f25a6f95e 100644
--- a/COFF/Driver.h
+++ b/COFF/Driver.h
@@ -43,8 +43,8 @@ public:
class ArgParser {
public:
- // Concatenate LINK environment variable and given arguments and parse them.
- llvm::opt::InputArgList parseLINK(std::vector<const char *> args);
+ // Parses command line options.
+ llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
// Tokenizes a given string and then parses as command line options.
llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); }
@@ -56,8 +56,8 @@ public:
parseDirectives(StringRef s);
private:
- // Parses command line options.
- llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
+ // Concatenate LINK environment variable.
+ void addLINK(SmallVector<const char *, 256> &argv);
std::vector<const char *> tokenize(StringRef s);
@@ -72,12 +72,12 @@ public:
void parseDirectives(InputFile *file);
// Used by ArchiveFile to enqueue members.
- void enqueueArchiveMember(const Archive::Child &c, StringRef symName,
+ void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
StringRef parentName);
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
- void enqueuePath(StringRef path, bool wholeArchive);
+ void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
private:
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
@@ -98,6 +98,10 @@ private:
// Library search path. The first element is always "" (current directory).
std::vector<StringRef> searchPaths;
+ // Convert resource files and potentially merge input resource object
+ // trees into one resource tree.
+ void convertResources();
+
void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args);
// We don't want to add the same file more than once.
@@ -120,7 +124,8 @@ private:
StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem();
- void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive);
+ void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
+ bool lazy);
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
StringRef parentName, uint64_t offsetInArchive);
@@ -184,7 +189,8 @@ void assignExportOrdinals();
void checkFailIfMismatch(StringRef arg, InputFile *source);
// Convert Windows resource files (.res files) to a .obj file.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs);
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+ ArrayRef<ObjFile *> objs);
void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);
diff --git a/COFF/DriverUtils.cpp b/COFF/DriverUtils.cpp
index 4360ac23b262..e08b855740a4 100644
--- a/COFF/DriverUtils.cpp
+++ b/COFF/DriverUtils.cpp
@@ -322,7 +322,7 @@ public:
if (!contents.empty()) {
std::error_code ec;
- raw_fd_ostream os(path, ec, sys::fs::F_None);
+ raw_fd_ostream os(path, ec, sys::fs::OF_None);
if (ec)
fatal("failed to open " + path + ": " + ec.message());
os << contents;
@@ -410,7 +410,7 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
// Create the default manifest file as a temporary file.
TemporaryFile Default("defaultxml", "manifest");
std::error_code ec;
- raw_fd_ostream os(Default.path, ec, sys::fs::F_Text);
+ raw_fd_ostream os(Default.path, ec, sys::fs::OF_Text);
if (ec)
fatal("failed to open " + Default.path + ": " + ec.message());
os << defaultXml;
@@ -511,7 +511,7 @@ void createSideBySideManifest() {
if (path == "")
path = config->outputFile + ".manifest";
std::error_code ec;
- raw_fd_ostream out(path, ec, sys::fs::F_Text);
+ raw_fd_ostream out(path, ec, sys::fs::OF_Text);
if (ec)
fatal("failed to create manifest: " + ec.message());
out << createManifestXml();
@@ -700,26 +700,42 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) {
// Convert Windows resource files (.res files) to a .obj file.
// Does what cvtres.exe does, but in-process and cross-platform.
-MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs) {
- object::WindowsResourceParser parser;
+MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
+ ArrayRef<ObjFile *> objs) {
+ object::WindowsResourceParser parser(/* MinGW */ config->mingw);
+ std::vector<std::string> duplicates;
for (MemoryBufferRef mb : mbs) {
std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
if (!rf)
fatal("cannot compile non-resource file as resource");
- std::vector<std::string> duplicates;
if (auto ec = parser.parse(rf, duplicates))
fatal(toString(std::move(ec)));
+ }
+
+ // Note: This processes all .res files before all objs. Ideally they'd be
+ // handled in the same order they were linked (to keep the right one, if
+ // there are duplicates that are tolerated due to forceMultipleRes).
+ for (ObjFile *f : objs) {
+ object::ResourceSectionRef rsf;
+ if (auto ec = rsf.load(f->getCOFFObj()))
+ fatal(toString(f) + ": " + toString(std::move(ec)));
- for (const auto &dupeDiag : duplicates)
- if (config->forceMultipleRes)
- warn(dupeDiag);
- else
- error(dupeDiag);
+ if (auto ec = parser.parse(rsf, f->getName(), duplicates))
+ fatal(toString(std::move(ec)));
}
+ if (config->mingw)
+ parser.cleanUpManifests(duplicates);
+
+ for (const auto &dupeDiag : duplicates)
+ if (config->forceMultipleRes)
+ warn(dupeDiag);
+ else
+ error(dupeDiag);
+
Expected<std::unique_ptr<MemoryBuffer>> e =
llvm::object::writeWindowsResourceCOFF(config->machine, parser,
config->timestamp);
@@ -757,15 +773,15 @@ static void handleColorDiagnostics(opt::InputArgList &args) {
if (!arg)
return;
if (arg->getOption().getID() == OPT_color_diagnostics) {
- errorHandler().colorDiagnostics = true;
+ enableColors(true);
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
- errorHandler().colorDiagnostics = false;
+ enableColors(false);
} else {
StringRef s = arg->getValue();
if (s == "always")
- errorHandler().colorDiagnostics = true;
+ enableColors(true);
else if (s == "never")
- errorHandler().colorDiagnostics = false;
+ enableColors(false);
else if (s != "auto")
error("unknown option: --color-diagnostics=" + s);
}
@@ -792,13 +808,17 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
// We need to get the quoting style for response files before parsing all
// options so we parse here before and ignore all the options but
- // --rsp-quoting.
+ // --rsp-quoting and /lldignoreenv.
+ // (This means --rsp-quoting can't be added through %LINK%.)
opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
- // Expand response files (arguments in the form of @<filename>)
- // and then parse the argument again.
+
+ // Expand response files (arguments in the form of @<filename>) and insert
+ // flags from %LINK% and %_LINK_%, and then parse the argument again.
SmallVector<const char *, 256> expandedArgv(argv.data(),
argv.data() + argv.size());
+ if (!args.hasArg(OPT_lldignoreenv))
+ addLINK(expandedArgv);
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
missingCount);
@@ -868,7 +888,7 @@ ArgParser::parseDirectives(StringRef s) {
// link.exe has an interesting feature. If LINK or _LINK_ environment
// variables exist, their contents are handled as command line strings.
// So you can pass extra arguments using them.
-opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
+void ArgParser::addLINK(SmallVector<const char *, 256> &argv) {
// Concatenate LINK env and command line arguments, and then parse them.
if (Optional<std::string> s = Process::GetEnv("LINK")) {
std::vector<const char *> v = tokenize(*s);
@@ -878,7 +898,6 @@ opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
std::vector<const char *> v = tokenize(*s);
argv.insert(std::next(argv.begin()), v.begin(), v.end());
}
- return parse(argv);
}
std::vector<const char *> ArgParser::tokenize(StringRef s) {
diff --git a/COFF/ICF.cpp b/COFF/ICF.cpp
index 2b2818de3889..c821569e3457 100644
--- a/COFF/ICF.cpp
+++ b/COFF/ICF.cpp
@@ -13,7 +13,7 @@
//
// On Windows, ICF is enabled by default.
//
-// See ELF/ICF.cpp for the details about the algortihm.
+// See ELF/ICF.cpp for the details about the algorithm.
//
//===----------------------------------------------------------------------===//
@@ -77,7 +77,7 @@ private:
// section is insignificant to the user program and the behaviour matches that
// of the Visual C++ linker.
bool ICF::isEligible(SectionChunk *c) {
- // Non-comdat chunks, dead chunks, and writable chunks are not elegible.
+ // Non-comdat chunks, dead chunks, and writable chunks are not eligible.
bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
if (!c->isCOMDAT() || !c->live || writable)
return false;
@@ -274,7 +274,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
for (Symbol *b : sc->symbols())
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
hash += sym->getChunk()->eqClass[cnt % 2];
- // Set MSB to 1 to avoid collisions with non-hash classs.
+ // Set MSB to 1 to avoid collisions with non-hash classes.
sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
});
}
@@ -297,7 +297,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
log("ICF needed " + Twine(cnt) + " iterations");
- // Merge sections in the same classs.
+ // Merge sections in the same classes.
forEachClass([&](size_t begin, size_t end) {
if (end - begin == 1)
return;
diff --git a/COFF/InputFiles.cpp b/COFF/InputFiles.cpp
index c00d5c5b494e..faec3ba160a5 100644
--- a/COFF/InputFiles.cpp
+++ b/COFF/InputFiles.cpp
@@ -47,6 +47,24 @@ using llvm::Triple;
using llvm::support::ulittle32_t;
namespace lld {
+
+// Returns the last element of a path, which is supposed to be a filename.
+static StringRef getBasename(StringRef path) {
+ return sys::path::filename(path, sys::path::Style::windows);
+}
+
+// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
+std::string toString(const coff::InputFile *file) {
+ if (!file)
+ return "<internal>";
+ if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
+ return file->getName();
+
+ return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
+ ")")
+ .str();
+}
+
namespace coff {
std::vector<ObjFile *> ObjFile::instances;
@@ -73,6 +91,10 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
}
}
+static bool ignoredSymbolName(StringRef name) {
+ return name == "@feat.00" || name == "@comp.id";
+}
+
ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
void ArchiveFile::parse() {
@@ -81,20 +103,20 @@ void ArchiveFile::parse() {
// Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &sym : file->symbols())
- symtab->addLazy(this, sym);
+ symtab->addLazyArchive(this, sym);
}
// Returns a buffer pointing to a member file containing a given symbol.
-void ArchiveFile::addMember(const Archive::Symbol *sym) {
+void ArchiveFile::addMember(const Archive::Symbol &sym) {
const Archive::Child &c =
- CHECK(sym->getMember(),
- "could not get the member for symbol " + sym->getName());
+ CHECK(sym.getMember(),
+ "could not get the member for symbol " + toCOFFString(sym));
// Return an empty buffer if we have already returned the same buffer.
if (!seen.insert(c.getChildOffset()).second)
return;
- driver->enqueueArchiveMember(c, sym->getName(), getName());
+ driver->enqueueArchiveMember(c, sym, getName());
}
std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
@@ -116,6 +138,49 @@ std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
return v;
}
+void LazyObjFile::fetch() {
+ if (mb.getBuffer().empty())
+ return;
+
+ InputFile *file;
+ if (isBitcode(mb))
+ file = make<BitcodeFile>(mb, "", 0, std::move(symbols));
+ else
+ file = make<ObjFile>(mb, std::move(symbols));
+ mb = {};
+ symtab->addFile(file);
+}
+
+void LazyObjFile::parse() {
+ if (isBitcode(this->mb)) {
+ // Bitcode file.
+ 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(this, sym.getName());
+ }
+ return;
+ }
+
+ // Native object file.
+ std::unique_ptr<Binary> coffObjPtr = CHECK(createBinary(mb), this);
+ COFFObjectFile *coffObj = cast<COFFObjectFile>(coffObjPtr.get());
+ uint32_t numSymbols = coffObj->getNumberOfSymbols();
+ for (uint32_t i = 0; i < numSymbols; ++i) {
+ COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
+ if (coffSym.isUndefined() || !coffSym.isExternal() ||
+ coffSym.isWeakExternal())
+ continue;
+ StringRef name;
+ coffObj->getSymbolName(coffSym, name);
+ if (coffSym.isAbsolute() && ignoredSymbolName(name))
+ continue;
+ symtab->addLazyObject(this, name);
+ i += coffSym.getNumberOfAuxSymbols();
+ }
+}
+
void ObjFile::parse() {
// Parse a memory buffer as a COFF file.
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
@@ -206,10 +271,6 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
if (def)
c->checksum = def->CheckSum;
- // link.exe uses the presence of .rsrc$01 for LNK4078, so match that.
- if (name == ".rsrc$01")
- isResourceObjFile = true;
-
// CodeView sections are stored to a different vector because they are not
// linked in the regular manner.
if (c->isCodeView())
@@ -226,12 +287,18 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
// for string literals) are subject to string tail merging.
MergeChunk::addSection(c);
+ else if (name == ".rsrc" || name.startswith(".rsrc$"))
+ resourceChunks.push_back(c);
else
chunks.push_back(c);
return c;
}
+void ObjFile::includeResourceChunks() {
+ chunks.insert(chunks.end(), resourceChunks.begin(), resourceChunks.end());
+}
+
void ObjFile::readAssociativeDefinition(
COFFSymbolRef sym, const coff_aux_section_definition *def) {
readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
@@ -315,7 +382,8 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
StringRef name;
coffObj->getSymbolName(sym, name);
if (sc)
- return symtab->addRegular(this, name, sym.getGeneric(), sc);
+ return symtab->addRegular(this, name, sym.getGeneric(), sc,
+ sym.getValue());
// For MinGW symbols named .weak.* that point to a discarded section,
// don't create an Undefined symbol. If nothing ever refers to the symbol,
// everything should be fine. If something actually refers to the symbol
@@ -469,7 +537,7 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
// if the two comdat sections have e.g. different alignment.
// Match that.
if (leaderChunk->getContents() != newChunk.getContents())
- symtab->reportDuplicate(leader, this);
+ symtab->reportDuplicate(leader, this, &newChunk, sym.getValue());
break;
}
@@ -524,13 +592,11 @@ Optional<Symbol *> ObjFile::createDefined(
if (sym.isAbsolute()) {
StringRef name = getName();
- // Skip special symbols.
- if (name == "@comp.id")
- return nullptr;
- if (name == "@feat.00") {
+ if (name == "@feat.00")
feat00Flags = sym.getValue();
+ // Skip special symbols.
+ if (ignoredSymbolName(name))
return nullptr;
- }
if (sym.isExternal())
return symtab->addAbsolute(name, sym);
@@ -552,7 +618,7 @@ Optional<Symbol *> ObjFile::createDefined(
// Comdat handling.
// A comdat symbol consists of two symbol table entries.
// The first symbol entry has the name of the section (e.g. .text), fixed
- // values for the other fields, and one auxilliary record.
+ // values for the other fields, and one auxiliary record.
// The second symbol entry has the name of the comdat symbol, called the
// "comdat leader".
// When this function is called for the first symbol entry of a comdat,
@@ -622,7 +688,7 @@ ArrayRef<uint8_t> ObjFile::getDebugSection(StringRef secName) {
return {};
}
-// OBJ files systematically store critical informations in a .debug$S stream,
+// OBJ files systematically store critical information in a .debug$S stream,
// even if the TU was compiled with no debug info. At least two records are
// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the
// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is
@@ -723,6 +789,37 @@ void ObjFile::initializeDependencies() {
debugTypesObj = makeTpiSource(this);
}
+// Used only for DWARF debug info, which is not common (except in MinGW
+// environments). This returns an optional pair of file name and line
+// number for where the variable was defined.
+Optional<std::pair<StringRef, uint32_t>>
+ObjFile::getVariableLocation(StringRef var) {
+ if (!dwarf) {
+ dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
+ if (!dwarf)
+ return None;
+ }
+ if (config->machine == I386)
+ var.consume_front("_");
+ Optional<std::pair<std::string, unsigned>> ret = dwarf->getVariableLoc(var);
+ if (!ret)
+ return None;
+ return std::make_pair(saver.save(ret->first), ret->second);
+}
+
+// Used only for DWARF debug info, which is not common (except in MinGW
+// environments).
+Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
+ uint32_t sectionIndex) {
+ if (!dwarf) {
+ dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
+ if (!dwarf)
+ return None;
+ }
+
+ return dwarf->getDILineInfo(offset, sectionIndex);
+}
+
StringRef ltrim1(StringRef s, const char *chars) {
if (!s.empty() && strchr(chars, s[0]))
return s.substr(1);
@@ -780,8 +877,9 @@ void ImportFile::parse() {
}
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive)
- : InputFile(BitcodeKind, mb) {
+ uint64_t offsetInArchive,
+ std::vector<Symbol *> &&symbols)
+ : InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
std::string path = mb.getBufferIdentifier().str();
if (config->thinLTOIndexOnly)
path = replaceThinLTOSuffix(mb.getBufferIdentifier());
@@ -860,22 +958,6 @@ std::string replaceThinLTOSuffix(StringRef path) {
return (path + repl).str();
return path;
}
+
} // namespace coff
} // namespace lld
-
-// Returns the last element of a path, which is supposed to be a filename.
-static StringRef getBasename(StringRef path) {
- return sys::path::filename(path, sys::path::Style::windows);
-}
-
-// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
-std::string lld::toString(const coff::InputFile *file) {
- if (!file)
- return "<internal>";
- if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
- return file->getName();
-
- return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
- ")")
- .str();
-}
diff --git a/COFF/InputFiles.h b/COFF/InputFiles.h
index dfad9814a397..672461cd84ba 100644
--- a/COFF/InputFiles.h
+++ b/COFF/InputFiles.h
@@ -10,10 +10,12 @@
#define LLD_COFF_INPUT_FILES_H
#include "Config.h"
+#include "lld/Common/DWARF.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/BinaryFormat/Magic.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h"
@@ -24,6 +26,7 @@
#include <vector>
namespace llvm {
+struct DILineInfo;
namespace pdb {
class DbiModuleDescriptorBuilder;
}
@@ -47,7 +50,6 @@ class Defined;
class DefinedImportData;
class DefinedImportThunk;
class DefinedRegular;
-class Lazy;
class SectionChunk;
class Symbol;
class Undefined;
@@ -56,7 +58,13 @@ class TpiSource;
// The root class of input files.
class InputFile {
public:
- enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind };
+ enum Kind {
+ ArchiveKind,
+ ObjectKind,
+ LazyObjectKind,
+ ImportKind,
+ BitcodeKind
+ };
Kind kind() const { return fileKind; }
virtual ~InputFile() {}
@@ -96,17 +104,35 @@ public:
// Enqueues an archive member load for the given symbol. If we've already
// enqueued a load for the same archive member, this function does nothing,
// which ensures that we don't load the same member more than once.
- void addMember(const Archive::Symbol *sym);
+ void addMember(const Archive::Symbol &sym);
private:
std::unique_ptr<Archive> file;
llvm::DenseSet<uint64_t> seen;
};
+// .obj or .o file between -start-lib and -end-lib.
+class LazyObjFile : public InputFile {
+public:
+ explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {}
+ static bool classof(const InputFile *f) {
+ return f->kind() == LazyObjectKind;
+ }
+ // Makes this object file part of the link.
+ void fetch();
+ // Adds the symbols in this file to the symbol table as LazyObject symbols.
+ void parse() override;
+
+private:
+ std::vector<Symbol *> symbols;
+};
+
// .obj or .o file. This may be a member of an archive file.
class ObjFile : public InputFile {
public:
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
+ explicit ObjFile(MemoryBufferRef m, std::vector<Symbol *> &&symbols)
+ : InputFile(ObjectKind, m), symbols(std::move(symbols)) {}
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
void parse() override;
MachineTypes getMachineType() override;
@@ -135,6 +161,10 @@ public:
return symbols.size() - 1;
}
+ void includeResourceChunks();
+
+ bool isResourceObjFile() const { return !resourceChunks.empty(); }
+
static std::vector<ObjFile *> instances;
// Flags in the absolute @feat.00 symbol if it is present. These usually
@@ -162,9 +192,6 @@ public:
// precompiled object. Any difference indicates out-of-date objects.
llvm::Optional<uint32_t> pchSignature;
- // Whether this is an object file created from .res files.
- bool isResourceObjFile = false;
-
// Whether this file was compiled with /hotpatch.
bool hotPatchable = false;
@@ -177,6 +204,12 @@ public:
// The .debug$T stream if there's one.
llvm::Optional<llvm::codeview::CVTypeArray> debugTypes;
+ llvm::Optional<std::pair<StringRef, uint32_t>>
+ getVariableLocation(StringRef var);
+
+ llvm::Optional<llvm::DILineInfo> getDILineInfo(uint32_t offset,
+ uint32_t sectionIndex);
+
private:
const coff_section* getSection(uint32_t i);
const coff_section *getSection(COFFSymbolRef sym) {
@@ -234,6 +267,8 @@ private:
// chunks and non-section chunks for common symbols.
std::vector<Chunk *> chunks;
+ std::vector<SectionChunk *> resourceChunks;
+
// CodeView debug info sections.
std::vector<SectionChunk *> debugChunks;
@@ -258,6 +293,8 @@ private:
// index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers.
std::vector<Symbol *> symbols;
+
+ DWARFCache *dwarf = nullptr;
};
// This type represents import library members that contain DLL names
@@ -299,7 +336,11 @@ public:
class BitcodeFile : public InputFile {
public:
BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive);
+ uint64_t offsetInArchive)
+ : BitcodeFile(mb, archiveName, offsetInArchive, {}) {}
+ explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName,
+ uint64_t offsetInArchive,
+ std::vector<Symbol *> &&symbols);
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
ArrayRef<Symbol *> getSymbols() { return symbols; }
MachineTypes getMachineType() override;
@@ -312,6 +353,10 @@ private:
std::vector<Symbol *> symbols;
};
+inline bool isBitcode(MemoryBufferRef mb) {
+ return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
+}
+
std::string replaceThinLTOSuffix(StringRef path);
} // namespace coff
diff --git a/COFF/LTO.cpp b/COFF/LTO.cpp
index eb3c60d66077..1c21236dce2b 100644
--- a/COFF/LTO.cpp
+++ b/COFF/LTO.cpp
@@ -39,14 +39,14 @@
using namespace llvm;
using namespace llvm::object;
-using namespace lld;
-using namespace lld::coff;
+namespace lld {
+namespace coff {
// Creates an empty file to and returns a raw_fd_ostream to write to it.
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
std::error_code ec;
auto ret =
- llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
+ std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
if (ec) {
error("cannot open " + file + ": " + ec.message());
return nullptr;
@@ -105,7 +105,7 @@ BitcodeCompiler::BitcodeCompiler() {
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
}
- ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
+ ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
config->ltoPartitions);
}
@@ -160,8 +160,8 @@ std::vector<StringRef> BitcodeCompiler::compile() {
checkError(ltoObj->run(
[&](size_t task) {
- return llvm::make_unique<lto::NativeObjectStream>(
- llvm::make_unique<raw_svector_ostream>(buf[task]));
+ return std::make_unique<lto::NativeObjectStream>(
+ std::make_unique<raw_svector_ostream>(buf[task]));
},
cache));
@@ -177,6 +177,8 @@ std::vector<StringRef> BitcodeCompiler::compile() {
// files. After that, we exit from linker and ThinLTO backend runs in a
// distributed environment.
if (config->thinLTOIndexOnly) {
+ if (!config->ltoObjPath.empty())
+ saveBuffer(buf[0], config->ltoObjPath);
if (indexFile)
indexFile->close();
return {};
@@ -204,3 +206,6 @@ std::vector<StringRef> BitcodeCompiler::compile() {
return ret;
}
+
+} // namespace coff
+} // namespace lld
diff --git a/COFF/MapFile.cpp b/COFF/MapFile.cpp
index f98cf8fa6130..0fea60aab99b 100644
--- a/COFF/MapFile.cpp
+++ b/COFF/MapFile.cpp
@@ -29,14 +29,14 @@
using namespace llvm;
using namespace llvm::object;
-using namespace lld;
-using namespace lld::coff;
+namespace lld {
+namespace coff {
using SymbolMapTy =
DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
-static const std::string indent8 = " "; // 8 spaces
-static const std::string indent16 = " "; // 16 spaces
+static constexpr char indent8[] = " "; // 8 spaces
+static constexpr char indent16[] = " "; // 16 spaces
// Print out the first three columns of a line.
static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
@@ -87,12 +87,12 @@ getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
return ret;
}
-void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
+void writeMapFile(ArrayRef<OutputSection *> outputSections) {
if (config->mapFile.empty())
return;
std::error_code ec;
- raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
+ raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
if (ec)
fatal("cannot open " + config->mapFile + ": " + ec.message());
@@ -122,3 +122,6 @@ void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
}
}
}
+
+} // namespace coff
+} // namespace lld
diff --git a/COFF/MinGW.cpp b/COFF/MinGW.cpp
index 2ca8ca0c058c..270cdaab4d9c 100644
--- a/COFF/MinGW.cpp
+++ b/COFF/MinGW.cpp
@@ -13,11 +13,12 @@
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
-using namespace lld;
-using namespace lld::coff;
using namespace llvm;
using namespace llvm::COFF;
+namespace lld {
+namespace coff {
+
AutoExporter::AutoExporter() {
excludeLibs = {
"libgcc",
@@ -55,7 +56,7 @@ AutoExporter::AutoExporter() {
// C++ symbols
"__rtti_",
"__builtin_",
- // Artifical symbols such as .refptr
+ // Artificial symbols such as .refptr
".",
};
@@ -146,9 +147,9 @@ bool AutoExporter::shouldExport(Defined *sym) const {
return !excludeObjects.count(fileName);
}
-void coff::writeDefFile(StringRef name) {
+void writeDefFile(StringRef name) {
std::error_code ec;
- raw_fd_ostream os(name, ec, sys::fs::F_None);
+ raw_fd_ostream os(name, ec, sys::fs::OF_None);
if (ec)
fatal("cannot open " + name + ": " + ec.message());
@@ -164,3 +165,6 @@ void coff::writeDefFile(StringRef name) {
os << "\n";
}
}
+
+} // namespace coff
+} // namespace lld
diff --git a/COFF/Options.td b/COFF/Options.td
index 024b7be8f78d..9e58d92e634f 100644
--- a/COFF/Options.td
+++ b/COFF/Options.td
@@ -21,9 +21,9 @@ def aligncomm : P<"aligncomm", "Set common symbol alignment">;
def alternatename : P<"alternatename", "Define weak alias">;
def base : P<"base", "Base address of the program">;
def color_diagnostics: Flag<["--"], "color-diagnostics">,
- HelpText<"Use colors in diagnostics">;
+ HelpText<"Use colors in diagnostics">;
def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
- HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">;
+ HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">;
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">;
@@ -34,7 +34,8 @@ def export : P<"export", "Export a function">;
def failifmismatch : P<"failifmismatch", "">;
def filealign : P<"filealign", "Section alignment in the output file">;
def functionpadmin : F<"functionpadmin">;
-def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">;
+def functionpadmin_opt : P<"functionpadmin",
+ "Prepares an image for hotpatching">;
def guard : P<"guard", "Control flow guard">;
def heap : P<"heap", "Size of the heap">;
def ignore : P<"ignore", "Specify warning codes to ignore">;
@@ -42,9 +43,14 @@ def implib : P<"implib", "Import library name">;
def lib : F<"lib">,
HelpText<"Act like lib.exe; must be first argument if present">;
def libpath : P<"libpath", "Additional library search path">;
-def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
-def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">;
-def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">;
+def linkrepro : P<"linkrepro",
+ "Dump linker invocation and input files for debugging">;
+def lldignoreenv : F<"lldignoreenv">,
+ HelpText<"Ignore environment variables like %LIB%">;
+def lldltocache : P<"lldltocache",
+ "Path to ThinLTO cached object file directory">;
+def lldltocachepolicy : P<"lldltocachepolicy",
+ "Pruning policy for the ThinLTO cache">;
def lldsavetemps : F<"lldsavetemps">,
HelpText<"Save temporary files instead of deleting them">;
def machine : P<"machine", "Specify target platform">;
@@ -56,7 +62,7 @@ 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 no_color_diagnostics: F<"no-color-diagnostics">,
- HelpText<"Do not use colors in diagnostics">;
+ HelpText<"Do not use colors in diagnostics">;
def pdb : P<"pdb", "PDB file path">;
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
def section : P<"section", "Specify section attributes">;
@@ -65,7 +71,8 @@ def stub : P<"stub", "Specify DOS stub file">;
def subsystem : P<"subsystem", "Specify subsystem">;
def timestamp : P<"timestamp", "Specify the PE header timestamp">;
def version : P<"version", "Specify a version number in the PE header">;
-def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
+def wholearchive_file : P<"wholearchive",
+ "Include all object files from this library">;
def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
Alias<nodefaultlib>;
@@ -105,6 +112,8 @@ def noentry : F<"noentry">,
def profile : F<"profile">;
def repro : F<"Brepro">,
HelpText<"Use a hash of the executable as the PE header timestamp">;
+def reproduce : P<"reproduce",
+ "Dump linker invocation and input files for debugging">;
def swaprun : P<"swaprun",
"Comma-separated list of 'cd' or 'net'">;
def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
@@ -112,10 +121,11 @@ def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
HelpText<"Make loader run output binary from swap instead of from network">;
def verbose : F<"verbose">;
-def wholearchive_flag : F<"wholearchive">;
+def wholearchive_flag : F<"wholearchive">,
+ HelpText<"Include all object files from all libraries">;
def force : F<"force">,
- HelpText<"Allow undefined and multiply defined symbols when creating executables">;
+ HelpText<"Allow undefined and multiply defined symbols">;
def force_unresolved : F<"force:unresolved">,
HelpText<"Allow undefined symbols when creating executables">;
def force_multiple : F<"force:multiple">,
@@ -162,6 +172,8 @@ def help : F<"help">;
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// LLD extensions
+def end_lib : F<"end-lib">,
+ HelpText<"Ends group of objects treated as if they were in a library">;
def exclude_all_symbols : F<"exclude-all-symbols">;
def export_all_symbols : F<"export-all-symbols">;
defm demangle : B<"demangle",
@@ -173,9 +185,11 @@ def kill_at : F<"kill-at">;
def lldmingw : F<"lldmingw">;
def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
def pdb_source_path : P<"pdbsourcepath",
- "Base path used to make relative source file path absolute in PDB">;
+ "Base path used to make relative source file path absolute in PDB">;
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
+def start_lib : F<"start-lib">,
+ HelpText<"Starts group of objects treated as if they were in a library">;
def thinlto_emit_imports_files :
F<"thinlto-emit-imports-files">,
HelpText<"Emit .imports files with -thinlto-index-only">;
@@ -191,6 +205,9 @@ def thinlto_object_suffix_replace : P<
def thinlto_prefix_replace: P<
"thinlto-prefix-replace",
"'old;new' replace old prefix with new prefix in ThinLTO outputs">;
+def lto_obj_path : P<
+ "lto-obj-path",
+ "output native object for merged LTO unit to this path">;
def dash_dash_version : Flag<["--"], "version">,
HelpText<"Print version information">;
defm threads: B<"threads",
diff --git a/COFF/PDB.cpp b/COFF/PDB.cpp
index a55e5136e040..ea99583b1d80 100644
--- a/COFF/PDB.cpp
+++ b/COFF/PDB.cpp
@@ -51,21 +51,22 @@
#include "llvm/Object/COFF.h"
#include "llvm/Object/CVDebugRecord.h"
#include "llvm/Support/BinaryByteStream.h"
+#include "llvm/Support/CRC.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FormatVariadic.h"
-#include "llvm/Support/JamCRC.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h"
#include <memory>
-using namespace lld;
-using namespace lld::coff;
using namespace llvm;
using namespace llvm::codeview;
using llvm::object::coff_section;
+namespace lld {
+namespace coff {
+
static ExitOnError exitOnErr;
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
@@ -513,16 +514,15 @@ static bool equals_path(StringRef path1, StringRef path2) {
return path1.equals(path2);
#endif
}
-
// Find by name an OBJ provided on the command line
-static ObjFile *findObjByName(StringRef fileNameOnly) {
- SmallString<128> currentPath;
-
+static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly,
+ uint32_t precompSignature) {
for (ObjFile *f : ObjFile::instances) {
StringRef currentFileName = sys::path::filename(f->getName());
- // Compare based solely on the file name (link.exe behavior)
- if (equals_path(currentFileName, fileNameOnly))
+ if (f->pchSignature.hasValue() &&
+ f->pchSignature.getValue() == precompSignature &&
+ equals_path(fileNameOnly, currentFileName))
return f;
}
return nullptr;
@@ -559,22 +559,15 @@ Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) {
// link.exe requires that a precompiled headers object must always be provided
// on the command-line, even if that's not necessary.
- auto precompFile = findObjByName(precompFileName);
+ auto precompFile =
+ findObjWithPrecompSignature(precompFileName, precomp.Signature);
if (!precompFile)
return createFileError(
- precompFileName.str(),
- make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref));
+ precomp.getPrecompFilePath().str(),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
addObjFile(precompFile, &indexMap);
- if (!precompFile->pchSignature)
- fatal(precompFile->getName() + " is not a precompiled headers object");
-
- if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0))
- return createFileError(
- precomp.getPrecompFilePath().str(),
- make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
-
return indexMap;
}
@@ -965,9 +958,7 @@ static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
sc.Imod = secChunk->file->moduleDBI->getModuleIndex();
ArrayRef<uint8_t> contents = secChunk->getContents();
JamCRC crc(0);
- ArrayRef<char> charContents = makeArrayRef(
- reinterpret_cast<const char *>(contents.data()), contents.size());
- crc.update(charContents);
+ crc.update(contents);
sc.DataCrc = crc.getCRC();
} else {
sc.Characteristics = os ? os->header.Characteristics : 0;
@@ -1150,7 +1141,7 @@ void DebugSHandler::finish() {
// string table. Generally the string table subsection appears after the
// checksum table, so we have to do this after looping over all the
// subsections.
- auto newChecksums = make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
+ auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
for (FileChecksumEntry &fc : checksums) {
SmallString<128> filename =
exitOnErr(cVStrTab.getString(fc.FileNameOffset));
@@ -1599,7 +1590,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
}
// Creates a PDB file.
-void coff::createPDB(SymbolTable *symtab,
+void createPDB(SymbolTable *symtab,
ArrayRef<OutputSection *> outputSections,
ArrayRef<uint8_t> sectionTable,
llvm::codeview::DebugInfo *buildId) {
@@ -1693,6 +1684,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
}
void PDBLinker::commit(codeview::GUID *guid) {
+ ExitOnError exitOnErr((config->pdbPath + ": ").str());
// Write to a file.
exitOnErr(builder.commit(config->pdbPath, guid));
}
@@ -1797,10 +1789,10 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
}
// Use CodeView line tables to resolve a file and line number for the given
-// offset into the given chunk and return them, or {"", 0} if a line table was
+// offset into the given chunk and return them, or None if a line table was
// not found.
-std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
- uint32_t addr) {
+Optional<std::pair<StringRef, uint32_t>>
+getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
ExitOnError exitOnErr;
DebugStringTableSubsectionRef cVStrTab;
@@ -1809,7 +1801,7 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
uint32_t offsetInLinetable;
if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
- return {"", 0};
+ return None;
Optional<uint32_t> nameIndex;
Optional<uint32_t> lineNumber;
@@ -1823,14 +1815,17 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
}
StringRef filename =
exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
- return {filename, *lineNumber};
+ return std::make_pair(filename, *lineNumber);
}
nameIndex = entry.NameIndex;
lineNumber = li.getStartLine();
}
}
if (!nameIndex)
- return {"", 0};
+ return None;
StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
- return {filename, *lineNumber};
+ return std::make_pair(filename, *lineNumber);
}
+
+} // namespace coff
+} // namespace lld
diff --git a/COFF/PDB.h b/COFF/PDB.h
index 3ac1adc85c5d..273609ea788c 100644
--- a/COFF/PDB.h
+++ b/COFF/PDB.h
@@ -10,6 +10,7 @@
#define LLD_COFF_PDB_H
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
namespace llvm {
@@ -29,9 +30,9 @@ void createPDB(SymbolTable *symtab,
llvm::ArrayRef<uint8_t> sectionTable,
llvm::codeview::DebugInfo *buildId);
-std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c,
- uint32_t addr);
-}
-}
+llvm::Optional<std::pair<llvm::StringRef, uint32_t>>
+getFileLineCodeView(const SectionChunk *c, uint32_t addr);
+} // namespace coff
+} // namespace lld
#endif
diff --git a/COFF/SymbolTable.cpp b/COFF/SymbolTable.cpp
index 0aff164ee567..869dfc7a2ee5 100644
--- a/COFF/SymbolTable.cpp
+++ b/COFF/SymbolTable.cpp
@@ -15,6 +15,7 @@
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Timer.h"
+#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Object/WindowsMachineFlag.h"
#include "llvm/Support/Debug.h"
@@ -61,6 +62,24 @@ static void errorOrWarn(const Twine &s) {
error(s);
}
+// Causes the file associated with a lazy symbol to be linked in.
+static void forceLazy(Symbol *s) {
+ s->pendingArchiveLoad = true;
+ switch (s->kind()) {
+ case Symbol::Kind::LazyArchiveKind: {
+ auto *l = cast<LazyArchive>(s);
+ l->file->addMember(l->sym);
+ break;
+ }
+ case Symbol::Kind::LazyObjectKind:
+ cast<LazyObject>(s)->file->fetch();
+ break;
+ default:
+ llvm_unreachable(
+ "symbol passed to forceLazy is not a LazyArchive or LazyObject");
+ }
+}
+
// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
// This is generally the global variable or function whose definition contains
// Addr.
@@ -69,7 +88,8 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
for (Symbol *s : sc->file->getSymbols()) {
auto *d = dyn_cast_or_null<DefinedRegular>(s);
- if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr ||
+ if (!d || !d->data || d->file != sc->file || d->getChunk() != sc ||
+ d->getValue() > addr ||
(candidate && d->getValue() < candidate->getValue()))
continue;
@@ -79,6 +99,38 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
return candidate;
}
+static std::vector<std::string> getSymbolLocations(BitcodeFile *file) {
+ std::string res("\n>>> referenced by ");
+ StringRef source = file->obj->getSourceFileName();
+ if (!source.empty())
+ res += source.str() + "\n>>> ";
+ res += toString(file);
+ return {res};
+}
+
+static Optional<std::pair<StringRef, uint32_t>>
+getFileLineDwarf(const SectionChunk *c, uint32_t addr) {
+ Optional<DILineInfo> optionalLineInfo =
+ c->file->getDILineInfo(addr, c->getSectionNumber() - 1);
+ if (!optionalLineInfo)
+ return None;
+ const DILineInfo &lineInfo = *optionalLineInfo;
+ if (lineInfo.FileName == DILineInfo::BadString)
+ return None;
+ return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line);
+}
+
+static Optional<std::pair<StringRef, uint32_t>>
+getFileLine(const SectionChunk *c, uint32_t addr) {
+ // MinGW can optionally use codeview, even if the default is dwarf.
+ Optional<std::pair<StringRef, uint32_t>> fileLine =
+ getFileLineCodeView(c, addr);
+ // If codeview didn't yield any result, check dwarf in MinGW mode.
+ if (!fileLine && config->mingw)
+ fileLine = getFileLineDwarf(c, addr);
+ return fileLine;
+}
+
// Given a file and the index of a symbol in that file, returns a description
// of all references to that symbol from that file. If no debug information is
// available, returns just the name of the file, else one string per actual
@@ -97,11 +149,13 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
for (const coff_relocation &r : sc->getRelocs()) {
if (r.SymbolTableIndex != symIndex)
continue;
- std::pair<StringRef, uint32_t> fileLine =
+ Optional<std::pair<StringRef, uint32_t>> fileLine =
getFileLine(sc, r.VirtualAddress);
Symbol *sym = getSymbol(sc, r.VirtualAddress);
- if (!fileLine.first.empty() || sym)
- locations.push_back({sym, fileLine});
+ if (fileLine)
+ locations.push_back({sym, *fileLine});
+ else if (sym)
+ locations.push_back({sym, {"", 0}});
}
}
@@ -123,13 +177,23 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
return symbolLocations;
}
+std::vector<std::string> getSymbolLocations(InputFile *file,
+ uint32_t symIndex) {
+ if (auto *o = dyn_cast<ObjFile>(file))
+ return getSymbolLocations(o, symIndex);
+ if (auto *b = dyn_cast<BitcodeFile>(file))
+ return getSymbolLocations(b);
+ llvm_unreachable("unsupported file type passed to getSymbolLocations");
+ return {};
+}
+
// For an undefined symbol, stores all files referencing it and the index of
// the undefined symbol in each file.
struct UndefinedDiag {
Symbol *sym;
struct File {
- ObjFile *oFile;
- uint64_t symIndex;
+ InputFile *file;
+ uint32_t symIndex;
};
std::vector<File> files;
};
@@ -143,7 +207,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
size_t i = 0, numRefs = 0;
for (const UndefinedDiag::File &ref : undefDiag.files) {
std::vector<std::string> symbolLocations =
- getSymbolLocations(ref.oFile, ref.symIndex);
+ getSymbolLocations(ref.file, ref.symIndex);
numRefs += symbolLocations.size();
for (const std::string &s : symbolLocations) {
if (i >= maxUndefReferences)
@@ -165,28 +229,33 @@ void SymbolTable::loadMinGWAutomaticImports() {
continue;
if (!sym->isUsedInRegularObj)
continue;
+ if (undef->getWeakAlias())
+ continue;
StringRef name = undef->getName();
if (name.startswith("__imp_"))
continue;
- // If we have an undefined symbol, but we have a Lazy representing a
- // symbol we could load from file, make sure to load that.
- Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str()));
- if (!l || l->pendingArchiveLoad)
+ // If we have an undefined symbol, but we have a lazy symbol we could
+ // load, load it.
+ Symbol *l = find(("__imp_" + name).str());
+ if (!l || l->pendingArchiveLoad || !l->isLazy())
continue;
- log("Loading lazy " + l->getName() + " from " + l->file->getName() +
+ log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() +
" for automatic import");
- l->pendingArchiveLoad = true;
- l->file->addMember(&l->sym);
+ forceLazy(l);
}
}
-bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
+Defined *SymbolTable::impSymbol(StringRef name) {
if (name.startswith("__imp_"))
- return false;
- Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
+ return nullptr;
+ return dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
+}
+
+bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
+ Defined *imp = impSymbol(name);
if (!imp)
return false;
@@ -232,7 +301,97 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
return true;
}
-void SymbolTable::reportRemainingUndefines() {
+/// Helper function for reportUnresolvable and resolveRemainingUndefines.
+/// This function emits an "undefined symbol" diagnostic for each symbol in
+/// undefs. If localImports is not nullptr, it also emits a "locally
+/// defined symbol imported" diagnostic for symbols in localImports.
+/// objFiles and bitcodeFiles (if not nullptr) are used to report where
+/// undefined symbols are referenced.
+static void
+reportProblemSymbols(const SmallPtrSetImpl<Symbol *> &undefs,
+ const DenseMap<Symbol *, Symbol *> *localImports,
+ const std::vector<ObjFile *> objFiles,
+ const std::vector<BitcodeFile *> *bitcodeFiles) {
+
+ // Return early if there is nothing to report (which should be
+ // the common case).
+ if (undefs.empty() && (!localImports || localImports->empty()))
+ return;
+
+ for (Symbol *b : config->gcroot) {
+ if (undefs.count(b))
+ errorOrWarn("<root>: undefined symbol: " + toString(*b));
+ if (localImports)
+ if (Symbol *imp = localImports->lookup(b))
+ warn("<root>: locally defined symbol imported: " + toString(*imp) +
+ " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
+ }
+
+ std::vector<UndefinedDiag> undefDiags;
+ DenseMap<Symbol *, int> firstDiag;
+
+ auto processFile = [&](InputFile *file, ArrayRef<Symbol *> symbols) {
+ uint32_t symIndex = (uint32_t)-1;
+ for (Symbol *sym : symbols) {
+ ++symIndex;
+ if (!sym)
+ continue;
+ if (undefs.count(sym)) {
+ auto it = firstDiag.find(sym);
+ if (it == firstDiag.end()) {
+ firstDiag[sym] = undefDiags.size();
+ undefDiags.push_back({sym, {{file, symIndex}}});
+ } else {
+ undefDiags[it->second].files.push_back({file, symIndex});
+ }
+ }
+ if (localImports)
+ if (Symbol *imp = localImports->lookup(sym))
+ warn(toString(file) +
+ ": locally defined symbol imported: " + toString(*imp) +
+ " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
+ }
+ };
+
+ for (ObjFile *file : objFiles)
+ processFile(file, file->getSymbols());
+
+ if (bitcodeFiles)
+ for (BitcodeFile *file : *bitcodeFiles)
+ processFile(file, file->getSymbols());
+
+ for (const UndefinedDiag &undefDiag : undefDiags)
+ reportUndefinedSymbol(undefDiag);
+}
+
+void SymbolTable::reportUnresolvable() {
+ SmallPtrSet<Symbol *, 8> undefs;
+ for (auto &i : symMap) {
+ Symbol *sym = i.second;
+ auto *undef = dyn_cast<Undefined>(sym);
+ if (!undef)
+ continue;
+ if (undef->getWeakAlias())
+ continue;
+ StringRef name = undef->getName();
+ if (name.startswith("__imp_")) {
+ Symbol *imp = find(name.substr(strlen("__imp_")));
+ if (imp && isa<Defined>(imp))
+ continue;
+ }
+ if (name.contains("_PchSym_"))
+ continue;
+ if (config->mingw && impSymbol(name))
+ continue;
+ undefs.insert(sym);
+ }
+
+ reportProblemSymbols(undefs,
+ /* localImports */ nullptr, ObjFile::instances,
+ &BitcodeFile::instances);
+}
+
+void SymbolTable::resolveRemainingUndefines() {
SmallPtrSet<Symbol *, 8> undefs;
DenseMap<Symbol *, Symbol *> localImports;
@@ -290,46 +449,9 @@ void SymbolTable::reportRemainingUndefines() {
undefs.insert(sym);
}
- if (undefs.empty() && localImports.empty())
- return;
-
- for (Symbol *b : config->gcroot) {
- if (undefs.count(b))
- errorOrWarn("<root>: undefined symbol: " + toString(*b));
- if (config->warnLocallyDefinedImported)
- if (Symbol *imp = localImports.lookup(b))
- warn("<root>: locally defined symbol imported: " + toString(*imp) +
- " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
- }
-
- std::vector<UndefinedDiag> undefDiags;
- DenseMap<Symbol *, int> firstDiag;
-
- for (ObjFile *file : ObjFile::instances) {
- size_t symIndex = (size_t)-1;
- for (Symbol *sym : file->getSymbols()) {
- ++symIndex;
- if (!sym)
- continue;
- if (undefs.count(sym)) {
- auto it = firstDiag.find(sym);
- if (it == firstDiag.end()) {
- firstDiag[sym] = undefDiags.size();
- undefDiags.push_back({sym, {{file, symIndex}}});
- } else {
- undefDiags[it->second].files.push_back({file, symIndex});
- }
- }
- if (config->warnLocallyDefinedImported)
- if (Symbol *imp = localImports.lookup(sym))
- warn(toString(file) +
- ": locally defined symbol imported: " + toString(*imp) +
- " (defined in " + toString(imp->getFile()) + ") [LNK4217]");
- }
- }
-
- for (const UndefinedDiag& undefDiag : undefDiags)
- reportUndefinedSymbol(undefDiag);
+ reportProblemSymbols(
+ undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
+ ObjFile::instances, /* bitcode files no longer needed */ nullptr);
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
@@ -356,44 +478,109 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
Symbol *s;
bool wasInserted;
std::tie(s, wasInserted) = insert(name, f);
- if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) {
+ if (wasInserted || (s->isLazy() && isWeakAlias)) {
replaceSymbol<Undefined>(s, name);
return s;
}
- if (auto *l = dyn_cast<Lazy>(s)) {
- if (!s->pendingArchiveLoad) {
- s->pendingArchiveLoad = true;
- l->file->addMember(&l->sym);
- }
- }
+ if (s->isLazy())
+ forceLazy(s);
return s;
}
-void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol sym) {
+void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
StringRef name = sym.getName();
Symbol *s;
bool wasInserted;
std::tie(s, wasInserted) = insert(name);
if (wasInserted) {
- replaceSymbol<Lazy>(s, f, sym);
+ replaceSymbol<LazyArchive>(s, f, sym);
+ return;
+ }
+ auto *u = dyn_cast<Undefined>(s);
+ if (!u || u->weakAlias || s->pendingArchiveLoad)
+ return;
+ s->pendingArchiveLoad = true;
+ f->addMember(sym);
+}
+
+void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(n, f);
+ if (wasInserted) {
+ replaceSymbol<LazyObject>(s, f, n);
return;
}
auto *u = dyn_cast<Undefined>(s);
if (!u || u->weakAlias || s->pendingArchiveLoad)
return;
s->pendingArchiveLoad = true;
- f->addMember(&sym);
+ f->fetch();
}
-void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
- std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
- toString(existing->getFile()) + " and in " +
- toString(newFile);
+static std::string getSourceLocationBitcode(BitcodeFile *file) {
+ std::string res("\n>>> defined at ");
+ StringRef source = file->obj->getSourceFileName();
+ if (!source.empty())
+ res += source.str() + "\n>>> ";
+ res += toString(file);
+ return res;
+}
+
+static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc,
+ uint32_t offset, StringRef name) {
+ Optional<std::pair<StringRef, uint32_t>> fileLine;
+ if (sc)
+ fileLine = getFileLine(sc, offset);
+ if (!fileLine)
+ fileLine = file->getVariableLocation(name);
+
+ std::string res;
+ llvm::raw_string_ostream os(res);
+ os << "\n>>> defined at ";
+ if (fileLine)
+ os << fileLine->first << ":" << fileLine->second << "\n>>> ";
+ os << toString(file);
+ return os.str();
+}
+
+static std::string getSourceLocation(InputFile *file, SectionChunk *sc,
+ uint32_t offset, StringRef name) {
+ if (auto *o = dyn_cast<ObjFile>(file))
+ return getSourceLocationObj(o, sc, offset, name);
+ if (auto *b = dyn_cast<BitcodeFile>(file))
+ return getSourceLocationBitcode(b);
+ return "\n>>> defined at " + toString(file);
+}
+
+// Construct and print an error message in the form of:
+//
+// lld-link: error: duplicate symbol: foo
+// >>> defined at bar.c:30
+// >>> bar.o
+// >>> defined at baz.c:563
+// >>> baz.o
+void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
+ SectionChunk *newSc,
+ uint32_t newSectionOffset) {
+ std::string msg;
+ llvm::raw_string_ostream os(msg);
+ os << "duplicate symbol: " << toString(*existing);
+
+ DefinedRegular *d = cast<DefinedRegular>(existing);
+ if (d && isa<ObjFile>(d->getFile())) {
+ os << getSourceLocation(d->getFile(), d->getChunk(), d->getValue(),
+ existing->getName());
+ } else {
+ os << getSourceLocation(existing->getFile(), nullptr, 0, "");
+ }
+ os << getSourceLocation(newFile, newSc, newSectionOffset,
+ existing->getName());
if (config->forceMultiple)
- warn(msg);
+ warn(os.str());
else
- error(msg);
+ error(os.str());
}
Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
@@ -401,7 +588,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true;
- if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedAbsolute>(s, n, sym);
else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr);
@@ -413,7 +600,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true;
- if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedAbsolute>(s, n, va);
else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr);
@@ -425,7 +612,7 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true;
- if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
+ if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedSynthetic>(s, n, c);
else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr);
@@ -433,8 +620,8 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
}
Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
- const coff_symbol_generic *sym,
- SectionChunk *c) {
+ const coff_symbol_generic *sym, SectionChunk *c,
+ uint32_t sectionOffset) {
Symbol *s;
bool wasInserted;
std::tie(s, wasInserted) = insert(n, f);
@@ -442,7 +629,7 @@ Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
/*IsExternal*/ true, sym, c);
else
- reportDuplicate(s, f);
+ reportDuplicate(s, f, c, sectionOffset);
return s;
}
@@ -481,7 +668,7 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true;
- if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
+ if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
replaceSymbol<DefinedImportData>(s, n, f);
return s;
}
@@ -496,7 +683,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
bool wasInserted;
std::tie(s, wasInserted) = insert(name, nullptr);
s->isUsedInRegularObj = true;
- if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
+ if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
replaceSymbol<DefinedImportThunk>(s, name, id, machine);
return s;
}
@@ -505,6 +692,21 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
return nullptr;
}
+void SymbolTable::addLibcall(StringRef name) {
+ Symbol *sym = findUnderscore(name);
+ if (!sym)
+ return;
+
+ if (auto *l = dyn_cast<LazyArchive>(sym)) {
+ MemoryBufferRef mb = l->getMemberBuffer();
+ if (isBitcode(mb))
+ addUndefined(sym->getName());
+ } else if (LazyObject *o = dyn_cast<LazyObject>(sym)) {
+ if (isBitcode(o->file->mb))
+ addUndefined(sym->getName());
+ }
+}
+
std::vector<Chunk *> SymbolTable::getChunks() {
std::vector<Chunk *> res;
for (ObjFile *file : ObjFile::instances) {
diff --git a/COFF/SymbolTable.h b/COFF/SymbolTable.h
index 88f47cbe9e78..cd8a53dcecdc 100644
--- a/COFF/SymbolTable.h
+++ b/COFF/SymbolTable.h
@@ -29,7 +29,7 @@ class Defined;
class DefinedAbsolute;
class DefinedRegular;
class DefinedRelative;
-class Lazy;
+class LazyArchive;
class SectionChunk;
class Symbol;
@@ -49,10 +49,13 @@ class SymbolTable {
public:
void addFile(InputFile *file);
+ // Emit errors for symbols that cannot be resolved.
+ void reportUnresolvable();
+
// Try to resolve any undefined symbols and update the symbol table
// accordingly, then print an error message for any remaining undefined
- // symbols.
- void reportRemainingUndefines();
+ // symbols and warn about imported local symbols.
+ void resolveRemainingUndefines();
void loadMinGWAutomaticImports();
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
@@ -83,11 +86,12 @@ public:
Symbol *addAbsolute(StringRef n, uint64_t va);
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
- void addLazy(ArchiveFile *f, const Archive::Symbol sym);
+ void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym);
+ void addLazyObject(LazyObjFile *f, StringRef n);
Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
Symbol *addRegular(InputFile *f, StringRef n,
const llvm::object::coff_symbol_generic *s = nullptr,
- SectionChunk *c = nullptr);
+ SectionChunk *c = nullptr, uint32_t sectionOffset = 0);
std::pair<DefinedRegular *, bool>
addComdat(InputFile *f, StringRef n,
const llvm::object::coff_symbol_generic *s = nullptr);
@@ -97,8 +101,11 @@ public:
Symbol *addImportData(StringRef n, ImportFile *f);
Symbol *addImportThunk(StringRef name, DefinedImportData *s,
uint16_t machine);
+ void addLibcall(StringRef name);
- void reportDuplicate(Symbol *existing, InputFile *newFile);
+ void reportDuplicate(Symbol *existing, InputFile *newFile,
+ SectionChunk *newSc = nullptr,
+ uint32_t newSectionOffset = 0);
// A list of chunks which to be added to .rdata.
std::vector<Chunk *> localImportChunks;
@@ -110,6 +117,9 @@ public:
}
private:
+ /// Given a name without "__imp_" prefix, returns a defined symbol
+ /// with the "__imp_" prefix, if it exists.
+ Defined *impSymbol(StringRef name);
/// Inserts symbol if not already present.
std::pair<Symbol *, bool> insert(StringRef name);
/// Same as insert(Name), but also sets isUsedInRegularObj.
diff --git a/COFF/Symbols.cpp b/COFF/Symbols.cpp
index 3583d4cb28c1..938c9c527ffa 100644
--- a/COFF/Symbols.cpp
+++ b/COFF/Symbols.cpp
@@ -12,6 +12,7 @@
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@@ -20,18 +21,35 @@ using namespace llvm::object;
using namespace lld::coff;
+namespace lld {
+
static_assert(sizeof(SymbolUnion) <= 48,
"symbols should be optimized for memory usage");
// Returns a symbol name for an error message.
-std::string lld::toString(coff::Symbol &b) {
- if (config->demangle)
- if (Optional<std::string> s = lld::demangleMSVC(b.getName()))
- return *s;
- return b.getName();
+static std::string maybeDemangleSymbol(StringRef symName) {
+ if (config->demangle) {
+ std::string prefix;
+ StringRef prefixless = symName;
+ if (prefixless.consume_front("__imp_"))
+ prefix = "__declspec(dllimport) ";
+ StringRef demangleInput = prefixless;
+ if (config->machine == I386)
+ demangleInput.consume_front("_");
+ std::string demangled = demangle(demangleInput);
+ if (demangled != demangleInput)
+ return prefix + demangle(demangleInput);
+ return (prefix + prefixless).str();
+ }
+ return symName;
+}
+std::string toString(coff::Symbol &b) {
+ return maybeDemangleSymbol(b.getName());
+}
+std::string toCOFFString(const Archive::Symbol &b) {
+ return maybeDemangleSymbol(b.getName());
}
-namespace lld {
namespace coff {
StringRef Symbol::getName() {
@@ -56,7 +74,9 @@ StringRef Symbol::getName() {
InputFile *Symbol::getFile() {
if (auto *sym = dyn_cast<DefinedCOFF>(this))
return sym->file;
- if (auto *sym = dyn_cast<Lazy>(this))
+ if (auto *sym = dyn_cast<LazyArchive>(this))
+ return sym->file;
+ if (auto *sym = dyn_cast<LazyObject>(this))
return sym->file;
return nullptr;
}
@@ -113,5 +133,14 @@ Defined *Undefined::getWeakAlias() {
return d;
return nullptr;
}
+
+MemoryBufferRef LazyArchive::getMemberBuffer() {
+ Archive::Child c =
+ CHECK(sym.getMember(),
+ "could not get the member for symbol " + toCOFFString(sym));
+ return CHECK(c.getMemoryBufferRef(),
+ "could not get the buffer for the member defining symbol " +
+ toCOFFString(sym));
+}
} // namespace coff
} // namespace lld
diff --git a/COFF/Symbols.h b/COFF/Symbols.h
index 86cd4f585e50..fd79bd5065b1 100644
--- a/COFF/Symbols.h
+++ b/COFF/Symbols.h
@@ -21,6 +21,14 @@
#include <vector>
namespace lld {
+
+std::string toString(coff::Symbol &b);
+
+// There are two different ways to convert an Archive::Symbol to a string:
+// One for Microsoft name mangling and one for Itanium name mangling.
+// Call the functions toCOFFString and toELFString, not just toString.
+std::string toCOFFString(const coff::Archive::Symbol &b);
+
namespace coff {
using llvm::object::Archive;
@@ -51,7 +59,8 @@ public:
DefinedSyntheticKind,
UndefinedKind,
- LazyKind,
+ LazyArchiveKind,
+ LazyObjectKind,
LastDefinedCOFFKind = DefinedCommonKind,
LastDefinedKind = DefinedSyntheticKind,
@@ -71,6 +80,10 @@ public:
// after calling markLive.
bool isLive() const;
+ bool isLazy() const {
+ return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
+ }
+
protected:
friend SymbolTable;
explicit Symbol(Kind k, StringRef n = "")
@@ -248,24 +261,29 @@ private:
// This class represents a symbol defined in an archive file. It is
// created from an archive file header, and it knows how to load an
// object file from an archive to replace itself with a defined
-// symbol. If the resolver finds both Undefined and Lazy for
-// the same name, it will ask the Lazy to load a file.
-class Lazy : public Symbol {
+// symbol. If the resolver finds both Undefined and LazyArchive for
+// the same name, it will ask the LazyArchive to load a file.
+class LazyArchive : public Symbol {
public:
- Lazy(ArchiveFile *f, const Archive::Symbol s)
- : Symbol(LazyKind, s.getName()), file(f), sym(s) {}
+ LazyArchive(ArchiveFile *f, const Archive::Symbol s)
+ : Symbol(LazyArchiveKind, s.getName()), file(f), sym(s) {}
- static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
-
- ArchiveFile *file;
+ static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
-private:
- friend SymbolTable;
+ MemoryBufferRef getMemberBuffer();
-private:
+ ArchiveFile *file;
const Archive::Symbol sym;
};
+class LazyObject : public Symbol {
+public:
+ LazyObject(LazyObjFile *f, StringRef n)
+ : Symbol(LazyObjectKind, n), file(f) {}
+ static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
+ LazyObjFile *file;
+};
+
// Undefined symbols.
class Undefined : public Symbol {
public:
@@ -371,7 +389,8 @@ inline uint64_t Defined::getRVA() {
return cast<DefinedCommon>(this)->getRVA();
case DefinedRegularKind:
return cast<DefinedRegular>(this)->getRVA();
- case LazyKind:
+ case LazyArchiveKind:
+ case LazyObjectKind:
case UndefinedKind:
llvm_unreachable("Cannot get the address for an undefined symbol.");
}
@@ -394,7 +413,8 @@ inline Chunk *Defined::getChunk() {
return cast<DefinedLocalImport>(this)->getChunk();
case DefinedCommonKind:
return cast<DefinedCommon>(this)->getChunk();
- case LazyKind:
+ case LazyArchiveKind:
+ case LazyObjectKind:
case UndefinedKind:
llvm_unreachable("Cannot get the chunk of an undefined symbol.");
}
@@ -409,11 +429,12 @@ union SymbolUnion {
alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)];
- alignas(Lazy) char e[sizeof(Lazy)];
+ alignas(LazyArchive) char e[sizeof(LazyArchive)];
alignas(Undefined) char f[sizeof(Undefined)];
alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
+ alignas(LazyObject) char j[sizeof(LazyObject)];
};
template <typename T, typename... ArgT>
@@ -429,7 +450,6 @@ void replaceSymbol(Symbol *s, ArgT &&... arg) {
}
} // namespace coff
-std::string toString(coff::Symbol &b);
} // namespace lld
#endif
diff --git a/COFF/Writer.cpp b/COFF/Writer.cpp
index 36ef87de4263..9729c6938ec8 100644
--- a/COFF/Writer.cpp
+++ b/COFF/Writer.cpp
@@ -40,8 +40,9 @@ using namespace llvm::COFF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-using namespace lld;
-using namespace lld::coff;
+
+namespace lld {
+namespace coff {
/* To re-generate DOSProgram:
$ cat > /tmp/DOSProgram.asm
@@ -240,6 +241,8 @@ private:
IdataContents idata;
Chunk *importTableStart = nullptr;
uint64_t importTableSize = 0;
+ Chunk *edataStart = nullptr;
+ Chunk *edataEnd = nullptr;
Chunk *iatStart = nullptr;
uint64_t iatSize = 0;
DelayLoadContents delayIdata;
@@ -283,9 +286,6 @@ private:
};
} // anonymous namespace
-namespace lld {
-namespace coff {
-
static Timer codeLayoutTimer("Code Layout", Timer::root());
static Timer diskCommitTimer("Commit Output File", Timer::root());
@@ -331,9 +331,6 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
contribSections.push_back(sec);
}
-} // namespace coff
-} // namespace lld
-
// Check whether the target address S is in range from a relocation
// of type relType at address P.
static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
@@ -626,6 +623,9 @@ void Writer::run() {
writeMapFile(outputSections);
+ if (errorCount())
+ return;
+
ScopedTimer t2(diskCommitTimer);
if (auto e = buffer->commit())
fatal("failed to write the output file: " + toString(std::move(e)));
@@ -738,7 +738,8 @@ void Writer::addSyntheticIdata() {
add(".idata$2", idata.dirs);
add(".idata$4", idata.lookups);
add(".idata$5", idata.addresses);
- add(".idata$6", idata.hints);
+ if (!idata.hints.empty())
+ add(".idata$6", idata.hints);
add(".idata$7", idata.dllNames);
}
@@ -762,6 +763,28 @@ void Writer::locateImportTables() {
}
}
+// Return whether a SectionChunk's suffix (the dollar and any trailing
+// suffix) should be removed and sorted into the main suffixless
+// PartialSection.
+static bool shouldStripSectionSuffix(SectionChunk *sc, StringRef name) {
+ // On MinGW, comdat groups are formed by putting the comdat group name
+ // after the '$' in the section name. For .eh_frame$<symbol>, that must
+ // still be sorted before the .eh_frame trailer from crtend.o, thus just
+ // strip the section name trailer. For other sections, such as
+ // .tls$$<symbol> (where non-comdat .tls symbols are otherwise stored in
+ // ".tls$"), they must be strictly sorted after .tls. And for the
+ // hypothetical case of comdat .CRT$XCU, we definitely need to keep the
+ // suffix for sorting. Thus, to play it safe, only strip the suffix for
+ // the standard sections.
+ if (!config->mingw)
+ return false;
+ if (!sc || !sc->isCOMDAT())
+ return false;
+ return name.startswith(".text$") || name.startswith(".data$") ||
+ name.startswith(".rdata$") || name.startswith(".pdata$") ||
+ name.startswith(".xdata$") || name.startswith(".eh_frame$");
+}
+
// Create output section objects and add them to OutputSections.
void Writer::createSections() {
// First, create the builtin sections.
@@ -807,10 +830,7 @@ void Writer::createSections() {
continue;
}
StringRef name = c->getSectionName();
- // On MinGW, comdat groups are formed by putting the comdat group name
- // after the '$' in the section name. Such a section name suffix shouldn't
- // imply separate alphabetical sorting of those section chunks though.
- if (config->mingw && sc && sc->isCOMDAT())
+ if (shouldStripSectionSuffix(sc, name))
name = name.split('$').first;
PartialSection *pSec = createPartialSection(name,
c->getOutputCharacteristics());
@@ -818,6 +838,7 @@ void Writer::createSections() {
}
fixPartialSectionChars(".rsrc", data | r);
+ fixPartialSectionChars(".edata", data | r);
// Even in non MinGW cases, we might need to link against GNU import
// libraries.
bool hasIdata = fixGnuImportChunks();
@@ -992,10 +1013,19 @@ void Writer::appendImportThunks() {
}
void Writer::createExportTable() {
- if (config->exports.empty())
- return;
- for (Chunk *c : edata.chunks)
- edataSec->addChunk(c);
+ if (!edataSec->chunks.empty()) {
+ // Allow using a custom built export table from input object files, instead
+ // of having the linker synthesize the tables.
+ if (config->hadExplicitExports)
+ warn("literal .edata sections override exports");
+ } else if (!config->exports.empty()) {
+ for (Chunk *c : edata.chunks)
+ edataSec->addChunk(c);
+ }
+ if (!edataSec->chunks.empty()) {
+ edataStart = edataSec->chunks.front();
+ edataEnd = edataSec->chunks.back();
+ }
}
void Writer::removeUnusedSections() {
@@ -1076,6 +1106,13 @@ Optional<coff_symbol16> Writer::createSymbol(Defined *def) {
}
}
+ // Symbols that are runtime pseudo relocations don't point to the actual
+ // symbol data itself (as they are imported), but points to the IAT entry
+ // instead. Avoid emitting them to the symbol table, as they can confuse
+ // debuggers.
+ if (def->isRuntimePseudoReloc)
+ return None;
+
StringRef name = def->getName();
if (name.size() > COFF::NameSize) {
sym.Name.Offset.Zeroes = 0;
@@ -1179,9 +1216,11 @@ void Writer::assignAddresses() {
sizeOfHeaders +=
config->is64() ? sizeof(pe32plus_header) : sizeof(pe32_header);
sizeOfHeaders = alignTo(sizeOfHeaders, config->fileAlign);
- uint64_t rva = pageSize; // The first page is kept unmapped.
fileSize = sizeOfHeaders;
+ // The first page is kept unmapped.
+ uint64_t rva = alignTo(sizeOfHeaders, config->align);
+
for (OutputSection *sec : outputSections) {
if (sec == relocSec)
addBaserels();
@@ -1211,10 +1250,10 @@ void Writer::assignAddresses() {
sec->header.SizeOfRawData = rawSize;
if (rawSize != 0)
sec->header.PointerToRawData = fileSize;
- rva += alignTo(virtualSize, pageSize);
+ rva += alignTo(virtualSize, config->align);
fileSize += alignTo(rawSize, config->fileAlign);
}
- sizeOfImage = alignTo(rva, pageSize);
+ sizeOfImage = alignTo(rva, config->align);
// Assign addresses to sections in MergeChunks.
for (MergeChunk *mc : MergeChunk::instances)
@@ -1283,7 +1322,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
pe->MinorLinkerVersion = 0;
pe->ImageBase = config->imageBase;
- pe->SectionAlignment = pageSize;
+ pe->SectionAlignment = config->align;
pe->FileAlignment = config->fileAlign;
pe->MajorImageVersion = config->majorImageVersion;
pe->MinorImageVersion = config->minorImageVersion;
@@ -1335,9 +1374,10 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
// Write data directory
auto *dir = reinterpret_cast<data_directory *>(buf);
buf += sizeof(*dir) * numberOfDataDirectory;
- if (!config->exports.empty()) {
- dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA();
- dir[EXPORT_TABLE].Size = edata.getSize();
+ if (edataStart) {
+ dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA();
+ dir[EXPORT_TABLE].Size =
+ edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA();
}
if (importTableStart) {
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
@@ -1475,7 +1515,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
// Absolute is never code, synthetic generally isn't and usually isn't
// determinable.
break;
- case Symbol::LazyKind:
+ case Symbol::LazyArchiveKind:
+ case Symbol::LazyObjectKind:
case Symbol::UndefinedKind:
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy
// symbols shouldn't have relocations.
@@ -1899,3 +1940,6 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
return it->second;
return nullptr;
}
+
+} // namespace coff
+} // namespace lld