Merge ^/vendor/lld/dist up to its last change, and resolve conflicts.

This commit is contained in:
Dimitry Andric 2020-01-23 21:35:51 +00:00
commit 85868e8a1d
104 changed files with 3926 additions and 2254 deletions

View File

@ -28,8 +28,10 @@ add_lld_library(lldCOFF
BinaryFormat BinaryFormat
Core Core
DebugInfoCodeView DebugInfoCodeView
DebugInfoDWARF
DebugInfoMSF DebugInfoMSF
DebugInfoPDB DebugInfoPDB
Demangle
LibDriver LibDriver
LTO LTO
MC MC

View File

@ -122,6 +122,7 @@ struct Configuration {
bool dll = false; bool dll = false;
StringRef implib; StringRef implib;
std::vector<Export> exports; std::vector<Export> exports;
bool hadExplicitExports;
std::set<std::string> delayLoads; std::set<std::string> delayLoads;
std::map<std::string, int> dllOrder; std::map<std::string, int> dllOrder;
Symbol *delayLoadHelper = nullptr; Symbol *delayLoadHelper = nullptr;
@ -189,6 +190,9 @@ struct Configuration {
// Used for /thinlto-object-suffix-replace: // Used for /thinlto-object-suffix-replace:
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace; std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
// Used for /lto-obj-path:
llvm::StringRef ltoObjPath;
uint64_t align = 4096; uint64_t align = 4096;
uint64_t imageBase = -1; uint64_t imageBase = -1;
uint64_t fileAlign = 512; uint64_t fileAlign = 512;

View File

@ -135,7 +135,7 @@ class NullChunk : public NonSectionChunk {
static std::vector<std::vector<DefinedImportData *>> static std::vector<std::vector<DefinedImportData *>>
binImports(const std::vector<DefinedImportData *> &imports) { binImports(const std::vector<DefinedImportData *> &imports) {
// Group DLL-imported symbols by DLL name because that's how // 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) { auto less = [](const std::string &a, const std::string &b) {
return config->dllOrder[a] < config->dllOrder[b]; return config->dllOrder[a] < config->dllOrder[b];
}; };
@ -188,7 +188,7 @@ class DelayDirectoryChunk : public NonSectionChunk {
// Initial contents for delay-loaded functions. // Initial contents for delay-loaded functions.
// This code calls __delayLoadHelper2 function to resolve a symbol // 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. // for subsequent function calls.
static const uint8_t thunkX64[] = { static const uint8_t thunkX64[] = {
0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>] 0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]

View File

@ -17,11 +17,12 @@
#include "llvm/DebugInfo/PDB/Native/PDBFile.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
using namespace lld;
using namespace lld::coff;
using namespace llvm; using namespace llvm;
using namespace llvm::codeview; using namespace llvm::codeview;
namespace lld {
namespace coff {
namespace { namespace {
// The TypeServerSource class represents a PDB type server, a file referenced by // 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 // 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)); 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); return new TpiSource(TpiSource::Regular, f);
} }
TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f, TpiSource *makeUseTypeServerSource(const ObjFile *f,
const TypeServer2Record *ts) { const TypeServer2Record *ts) {
TypeServerSource::enqueue(f, *ts); TypeServerSource::enqueue(f, *ts);
return new UseTypeServerSource(f, ts); return new UseTypeServerSource(f, ts);
} }
TpiSource *lld::coff::makePrecompSource(const ObjFile *f) { TpiSource *makePrecompSource(const ObjFile *f) {
return new PrecompSource(f); return new PrecompSource(f);
} }
TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f, TpiSource *makeUsePrecompSource(const ObjFile *f,
const PrecompRecord *precomp) { const PrecompRecord *precomp) {
return new UsePrecompSource(f, precomp); return new UsePrecompSource(f, precomp);
} }
namespace lld {
namespace coff {
template <> template <>
const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) { const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
assert(source->kind == TpiSource::UsingPCH); assert(source->kind == TpiSource::UsingPCH);
@ -128,8 +127,6 @@ const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
assert(source->kind == TpiSource::UsingPDB); assert(source->kind == TpiSource::UsingPDB);
return ((const UseTypeServerSource *)source)->typeServerDependency; return ((const UseTypeServerSource *)source)->typeServerDependency;
} }
} // namespace coff
} // namespace lld
std::map<std::string, std::pair<std::string, TypeServerSource *>> std::map<std::string, std::pair<std::string, TypeServerSource *>>
TypeServerSource::instances; TypeServerSource::instances;
@ -210,8 +207,7 @@ TypeServerSource::findFromFile(const ObjFile *dependentFile) {
// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is // FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
// moved here. // moved here.
Expected<llvm::pdb::NativeSession *> Expected<llvm::pdb::NativeSession *> findTypeServerSource(const ObjFile *f) {
lld::coff::findTypeServerSource(const ObjFile *f) {
Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f); Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
if (!ts) if (!ts)
return ts.takeError(); return ts.takeError();
@ -231,7 +227,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
if (!it.second) if (!it.second)
return; // another OBJ already scheduled this PDB for load 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 // 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 // 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 // debug info will simply be missing from the final PDB - that is the default
// accepted behavior. // accepted behavior.
void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) { void loadTypeServerSource(llvm::MemoryBufferRef m) {
std::string path = normalizePdbPath(m.getBufferIdentifier()); std::string path = normalizePdbPath(m.getBufferIdentifier());
Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m); Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
@ -266,3 +262,6 @@ Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
return info.takeError(); return info.takeError();
return new TypeServerSource(m, session.release()); return new TypeServerSource(m, session.release());
} }
} // namespace coff
} // namespace lld

View File

@ -27,6 +27,7 @@
#include "llvm/ADT/Optional.h" #include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/StringSwitch.h"
#include "llvm/BinaryFormat/Magic.h" #include "llvm/BinaryFormat/Magic.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/COFFImportFile.h" #include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/COFFModuleDefinition.h" #include "llvm/Object/COFFModuleDefinition.h"
@ -63,16 +64,16 @@ LinkerDriver *driver;
bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) { bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) {
errorHandler().logName = args::getFilenameWithoutExe(args[0]); errorHandler().logName = args::getFilenameWithoutExe(args[0]);
errorHandler().errorOS = &diag; errorHandler().errorOS = &diag;
errorHandler().colorDiagnostics = diag.has_colors();
errorHandler().errorLimitExceededMsg = errorHandler().errorLimitExceededMsg =
"too many errors emitted, stopping now" "too many errors emitted, stopping now"
" (use /errorlimit:0 to see all errors)"; " (use /errorlimit:0 to see all errors)";
errorHandler().exitEarly = canExitEarly; errorHandler().exitEarly = canExitEarly;
enableColors(diag.has_colors());
config = make<Configuration>(); config = make<Configuration>();
symtab = make<SymbolTable>(); symtab = make<SymbolTable>();
driver = make<LinkerDriver>(); driver = make<LinkerDriver>();
driver->link(args); driver->link(args);
// Call exit() if we can to avoid calling destructors. // Call exit() if we can to avoid calling destructors.
@ -170,7 +171,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
} }
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb, void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
bool wholeArchive) { bool wholeArchive, bool lazy) {
StringRef filename = mb->getBufferIdentifier(); StringRef filename = mb->getBufferIdentifier();
MemoryBufferRef mbref = takeBuffer(std::move(mb)); MemoryBufferRef mbref = takeBuffer(std::move(mb));
@ -188,18 +189,25 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
Archive *archive = file.get(); Archive *archive = file.get();
make<std::unique_ptr<Archive>>(std::move(file)); // take ownership make<std::unique_ptr<Archive>>(std::move(file)); // take ownership
int memberIndex = 0;
for (MemoryBufferRef m : getArchiveMembers(archive)) for (MemoryBufferRef m : getArchiveMembers(archive))
addArchiveBuffer(m, "<whole-archive>", filename, 0); addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++);
return; return;
} }
symtab->addFile(make<ArchiveFile>(mbref)); symtab->addFile(make<ArchiveFile>(mbref));
break; break;
case file_magic::bitcode: 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; break;
case file_magic::coff_object: case file_magic::coff_object:
case file_magic::coff_import_library: 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; break;
case file_magic::pdb: case file_magic::pdb:
loadTypeServerSource(mbref); loadTypeServerSource(mbref);
@ -220,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 = auto future =
std::make_shared<std::future<MBErrPair>>(createFutureForFile(path)); std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
std::string pathStr = path; std::string pathStr = path;
@ -240,7 +248,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
else else
error(msg + "; did you mean '" + nearest + "'"); error(msg + "; did you mean '" + nearest + "'");
} else } else
driver->addBuffer(std::move(mbOrErr.first), wholeArchive); driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
}); });
} }
@ -303,9 +311,10 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
auto mbOrErr = future->get(); auto mbOrErr = future->get();
if (mbOrErr.second) if (mbOrErr.second)
reportBufferError(errorCodeToError(mbOrErr.second), childName); reportBufferError(errorCodeToError(mbOrErr.second), childName);
// Pass empty string as archive name so that the original filename is
// used as the buffer identifier.
driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)), driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
toCOFFString(sym), parentName, toCOFFString(sym), "", /*OffsetInArchive=*/0);
/*OffsetInArchive=*/0);
}); });
} }
@ -359,7 +368,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
break; break;
case OPT_defaultlib: case OPT_defaultlib:
if (Optional<StringRef> path = findLib(arg->getValue())) if (Optional<StringRef> path = findLib(arg->getValue()))
enqueuePath(*path, false); enqueuePath(*path, false, false);
break; break;
case OPT_entry: case OPT_entry:
config->entry = addUndefined(mangle(arg->getValue())); config->entry = addUndefined(mangle(arg->getValue()));
@ -594,6 +603,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
for (auto *arg : args) { for (auto *arg : args) {
switch (arg->getOption().getID()) { switch (arg->getOption().getID()) {
case OPT_linkrepro: case OPT_linkrepro:
case OPT_reproduce:
case OPT_INPUT: case OPT_INPUT:
case OPT_defaultlib: case OPT_defaultlib:
case OPT_libpath: case OPT_libpath:
@ -708,8 +718,7 @@ static std::string getImplibPath() {
return out.str(); return out.str();
} }
// // The import name is calculated as follows:
// The import name is caculated as the following:
// //
// | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY // | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY
// -----+----------------+---------------------+------------------ // -----+----------------+---------------------+------------------
@ -991,30 +1000,37 @@ static void parsePDBAltPath(StringRef altPath) {
config->pdbAltPath = buf; 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. /// Call after ObjFile::Instances is complete.
static void diagnoseMultipleResourceObjFiles() { void LinkerDriver::convertResources() {
// The .rsrc$01 section in a resource obj file contains a tree description std::vector<ObjFile *> resourceObjFiles;
// 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) { for (ObjFile *f : ObjFile::instances) {
if (!f->isResourceObjFile) if (f->isResourceObjFile())
continue; resourceObjFiles.push_back(f);
if (!resourceObjFile) {
resourceObjFile = f;
continue;
}
error(toString(f) +
": more than one resource obj file not allowed, already got " +
toString(resourceObjFile));
} }
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(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 // In MinGW, if no symbols are chosen to be exported, then all symbols are
@ -1055,11 +1071,25 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
}); });
} }
static const char *libcallRoutineNames[] = { // lld has a feature to create a tar file containing all input files as well as
#define HANDLE_LIBCALL(code, name) name, // all command line options, so that other people can run lld again with exactly
#include "llvm/IR/RuntimeLibcalls.def" // the same inputs. This feature is accessible via /linkrepro and /reproduce.
#undef HANDLE_LIBCALL //
}; // /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) { void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Needed for LTO. // Needed for LTO.
@ -1079,7 +1109,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Parse command line options. // Parse command line options.
ArgParser parser; ArgParser parser;
opt::InputArgList args = parser.parseLINK(argsArr); opt::InputArgList args = parser.parse(argsArr);
// Parse and evaluate -mllvm options. // Parse and evaluate -mllvm options.
std::vector<const char *> v; std::vector<const char *> v;
@ -1123,22 +1153,20 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// options are handled. // options are handled.
config->mingw = args.hasArg(OPT_lldmingw); config->mingw = args.hasArg(OPT_lldmingw);
if (auto *arg = args.getLastArg(OPT_linkrepro)) { // Handle /linkrepro and /reproduce.
SmallString<64> path = StringRef(arg->getValue()); if (Optional<std::string> path = getReproduceFile(args)) {
sys::path::append(path, "repro.tar");
Expected<std::unique_ptr<TarWriter>> errOrWriter = Expected<std::unique_ptr<TarWriter>> errOrWriter =
TarWriter::create(path, "repro"); TarWriter::create(*path, sys::path::stem(*path));
if (errOrWriter) { if (errOrWriter) {
tar = std::move(*errOrWriter); tar = std::move(*errOrWriter);
} else { } else {
error("/linkrepro: failed to open " + path + ": " + error("/linkrepro: failed to open " + *path + ": " +
toString(errOrWriter.takeError())); toString(errOrWriter.takeError()));
} }
} }
if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) { if (!args.hasArg(OPT_INPUT)) {
if (args.hasArg(OPT_deffile)) if (args.hasArg(OPT_deffile))
config->noEntry = true; config->noEntry = true;
else else
@ -1149,7 +1177,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
searchPaths.push_back(""); searchPaths.push_back("");
for (auto *arg : args.filtered(OPT_libpath)) for (auto *arg : args.filtered(OPT_libpath))
searchPaths.push_back(arg->getValue()); searchPaths.push_back(arg->getValue());
addLibSearchPaths(); if (!args.hasArg(OPT_lldignoreenv))
addLibSearchPaths();
// Handle /ignore // Handle /ignore
for (auto *arg : args.filtered(OPT_ignore)) { for (auto *arg : args.filtered(OPT_ignore)) {
@ -1481,6 +1510,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
getOldNewOptions(args, OPT_thinlto_prefix_replace); getOldNewOptions(args, OPT_thinlto_prefix_replace);
config->thinLTOObjectSuffixReplace = config->thinLTOObjectSuffixReplace =
getOldNewOptions(args, OPT_thinlto_object_suffix_replace); getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path);
// Handle miscellaneous boolean flags. // Handle miscellaneous boolean flags.
config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true); config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
config->allowIsolation = config->allowIsolation =
@ -1545,19 +1575,45 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
return false; return false;
}; };
// Create a list of input files. Files can be given as arguments // Create a list of input files. These can be given as OPT_INPUT options
// for /defaultlib option. // and OPT_wholearchive_file options, and we also need to track OPT_start_lib
for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file)) // and OPT_end_lib.
if (Optional<StringRef> path = findFile(arg->getValue())) bool inLib = false;
enqueuePath(*path, isWholeArchive(*path)); 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)) for (auto *arg : args.filtered(OPT_defaultlib))
if (Optional<StringRef> path = findLib(arg->getValue())) if (Optional<StringRef> path = findLib(arg->getValue()))
enqueuePath(*path, false); enqueuePath(*path, false, false);
// Windows specific -- Create a resource file containing a manifest file. // Windows specific -- Create a resource file containing a manifest file.
if (config->manifest == Configuration::Embed) if (config->manifest == Configuration::Embed)
addBuffer(createManifestRes(), false); addBuffer(createManifestRes(), false, false);
// Read all input files given via the command line. // Read all input files given via the command line.
run(); run();
@ -1582,12 +1638,6 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt)) for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
parseFunctionPadMin(arg, config->machine); 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) if (tar)
tar->append("response.txt", tar->append("response.txt",
createResponseFile(args, filePaths, createResponseFile(args, filePaths,
@ -1626,7 +1676,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
} }
// Handle generation of import library from a def file. // Handle generation of import library from a def file.
if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) { if (!args.hasArg(OPT_INPUT)) {
fixupExports(); fixupExports();
createImportLibrary(/*asLib=*/true); createImportLibrary(/*asLib=*/true);
return; return;
@ -1672,8 +1722,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// Set default image name if neither /out or /def set it. // Set default image name if neither /out or /def set it.
if (config->outputFile.empty()) { if (config->outputFile.empty()) {
config->outputFile = getOutputPath( config->outputFile =
(*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue()); getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue());
} }
// Fail early if an output file is not writable. // Fail early if an output file is not writable.
@ -1769,7 +1819,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
// bitcode file in an archive member, we need to arrange to use LTO to // 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. // compile those archive members by adding them to the link beforehand.
if (!BitcodeFile::instances.empty()) if (!BitcodeFile::instances.empty())
for (const char *s : libcallRoutineNames) for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
symtab->addLibcall(s); symtab->addLibcall(s);
// Windows specific -- if __load_config_used can be resolved, resolve it. // Windows specific -- if __load_config_used can be resolved, resolve it.
@ -1777,28 +1827,10 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
addUndefined(mangle("_load_config_used")); addUndefined(mangle("_load_config_used"));
} while (run()); } 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)) { if (args.hasArg(OPT_include_optional)) {
// Handle /includeoptional // Handle /includeoptional
for (auto *arg : args.filtered(OPT_include_optional)) 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()); addUndefined(arg->getValue());
while (run()); while (run());
} }
@ -1821,11 +1853,36 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
run(); run();
} }
// Make sure we have resolved all symbols. // At this point, we should not have any symbols that cannot be resolved.
symtab->reportRemainingUndefines(); // 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()) if (errorCount())
return; 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) { if (config->mingw) {
// In MinGW, all symbols are automatically exported if no symbols // In MinGW, all symbols are automatically exported if no symbols
// are chosen to be exported. // are chosen to be exported.
@ -1849,10 +1906,12 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
} }
// Windows specific -- when we are creating a .dll file, we also // 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) { if (!config->exports.empty() || config->dll) {
fixupExports(); fixupExports();
createImportLibrary(/*asLib=*/false); if (!config->mingw || !config->implib.empty())
createImportLibrary(/*asLib=*/false);
assignExportOrdinals(); assignExportOrdinals();
} }
@ -1896,7 +1955,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
markLive(symtab->getChunks()); markLive(symtab->getChunks());
// Needs to happen after the last call to addFile(). // Needs to happen after the last call to addFile().
diagnoseMultipleResourceObjFiles(); convertResources();
// Identify identical COMDAT sections to merge them. // Identify identical COMDAT sections to merge them.
if (config->doICF) { if (config->doICF) {

View File

@ -43,8 +43,8 @@ class COFFOptTable : public llvm::opt::OptTable {
class ArgParser { class ArgParser {
public: public:
// Concatenate LINK environment variable and given arguments and parse them. // Parses command line options.
llvm::opt::InputArgList parseLINK(std::vector<const char *> args); llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
// Tokenizes a given string and then parses as command line options. // Tokenizes a given string and then parses as command line options.
llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); } llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); }
@ -56,8 +56,8 @@ class ArgParser {
parseDirectives(StringRef s); parseDirectives(StringRef s);
private: private:
// Parses command line options. // Concatenate LINK environment variable.
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args); void addLINK(SmallVector<const char *, 256> &argv);
std::vector<const char *> tokenize(StringRef s); std::vector<const char *> tokenize(StringRef s);
@ -77,7 +77,7 @@ class LinkerDriver {
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb); MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
void enqueuePath(StringRef path, bool wholeArchive); void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
private: private:
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
@ -98,6 +98,10 @@ class LinkerDriver {
// Library search path. The first element is always "" (current directory). // Library search path. The first element is always "" (current directory).
std::vector<StringRef> searchPaths; 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); void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args);
// We don't want to add the same file more than once. // We don't want to add the same file more than once.
@ -120,7 +124,8 @@ class LinkerDriver {
StringRef findDefaultEntry(); StringRef findDefaultEntry();
WindowsSubsystem inferSubsystem(); 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, void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
StringRef parentName, uint64_t offsetInArchive); StringRef parentName, uint64_t offsetInArchive);
@ -184,7 +189,8 @@ void assignExportOrdinals();
void checkFailIfMismatch(StringRef arg, InputFile *source); void checkFailIfMismatch(StringRef arg, InputFile *source);
// Convert Windows resource files (.res files) to a .obj file. // 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); void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);

View File

@ -322,7 +322,7 @@ class TemporaryFile {
if (!contents.empty()) { if (!contents.empty()) {
std::error_code ec; 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) if (ec)
fatal("failed to open " + path + ": " + ec.message()); fatal("failed to open " + path + ": " + ec.message());
os << contents; os << contents;
@ -410,7 +410,7 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
// Create the default manifest file as a temporary file. // Create the default manifest file as a temporary file.
TemporaryFile Default("defaultxml", "manifest"); TemporaryFile Default("defaultxml", "manifest");
std::error_code ec; 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) if (ec)
fatal("failed to open " + Default.path + ": " + ec.message()); fatal("failed to open " + Default.path + ": " + ec.message());
os << defaultXml; os << defaultXml;
@ -511,7 +511,7 @@ void createSideBySideManifest() {
if (path == "") if (path == "")
path = config->outputFile + ".manifest"; path = config->outputFile + ".manifest";
std::error_code ec; 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) if (ec)
fatal("failed to create manifest: " + ec.message()); fatal("failed to create manifest: " + ec.message());
out << createManifestXml(); out << createManifestXml();
@ -700,26 +700,42 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) {
// Convert Windows resource files (.res files) to a .obj file. // Convert Windows resource files (.res files) to a .obj file.
// Does what cvtres.exe does, but in-process and cross-platform. // Does what cvtres.exe does, but in-process and cross-platform.
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs) { MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
object::WindowsResourceParser parser; ArrayRef<ObjFile *> objs) {
object::WindowsResourceParser parser(/* MinGW */ config->mingw);
std::vector<std::string> duplicates;
for (MemoryBufferRef mb : mbs) { for (MemoryBufferRef mb : mbs) {
std::unique_ptr<object::Binary> bin = check(object::createBinary(mb)); std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get()); object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
if (!rf) if (!rf)
fatal("cannot compile non-resource file as resource"); fatal("cannot compile non-resource file as resource");
std::vector<std::string> duplicates;
if (auto ec = parser.parse(rf, duplicates)) if (auto ec = parser.parse(rf, duplicates))
fatal(toString(std::move(ec))); fatal(toString(std::move(ec)));
for (const auto &dupeDiag : duplicates)
if (config->forceMultipleRes)
warn(dupeDiag);
else
error(dupeDiag);
} }
// 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)));
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 = Expected<std::unique_ptr<MemoryBuffer>> e =
llvm::object::writeWindowsResourceCOFF(config->machine, parser, llvm::object::writeWindowsResourceCOFF(config->machine, parser,
config->timestamp); config->timestamp);
@ -757,15 +773,15 @@ static void handleColorDiagnostics(opt::InputArgList &args) {
if (!arg) if (!arg)
return; return;
if (arg->getOption().getID() == OPT_color_diagnostics) { if (arg->getOption().getID() == OPT_color_diagnostics) {
errorHandler().colorDiagnostics = true; enableColors(true);
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) { } else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
errorHandler().colorDiagnostics = false; enableColors(false);
} else { } else {
StringRef s = arg->getValue(); StringRef s = arg->getValue();
if (s == "always") if (s == "always")
errorHandler().colorDiagnostics = true; enableColors(true);
else if (s == "never") else if (s == "never")
errorHandler().colorDiagnostics = false; enableColors(false);
else if (s != "auto") else if (s != "auto")
error("unknown option: --color-diagnostics=" + s); 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 // 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 // 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); 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(), SmallVector<const char *, 256> expandedArgv(argv.data(),
argv.data() + argv.size()); argv.data() + argv.size());
if (!args.hasArg(OPT_lldignoreenv))
addLINK(expandedArgv);
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv); cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex, args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
missingCount); missingCount);
@ -868,7 +888,7 @@ ArgParser::parseDirectives(StringRef s) {
// link.exe has an interesting feature. If LINK or _LINK_ environment // link.exe has an interesting feature. If LINK or _LINK_ environment
// variables exist, their contents are handled as command line strings. // variables exist, their contents are handled as command line strings.
// So you can pass extra arguments using them. // 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. // Concatenate LINK env and command line arguments, and then parse them.
if (Optional<std::string> s = Process::GetEnv("LINK")) { if (Optional<std::string> s = Process::GetEnv("LINK")) {
std::vector<const char *> v = tokenize(*s); 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); std::vector<const char *> v = tokenize(*s);
argv.insert(std::next(argv.begin()), v.begin(), v.end()); argv.insert(std::next(argv.begin()), v.begin(), v.end());
} }
return parse(argv);
} }
std::vector<const char *> ArgParser::tokenize(StringRef s) { std::vector<const char *> ArgParser::tokenize(StringRef s) {

View File

@ -13,7 +13,7 @@
// //
// On Windows, ICF is enabled by default. // 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 @@ class ICF {
// section is insignificant to the user program and the behaviour matches that // section is insignificant to the user program and the behaviour matches that
// of the Visual C++ linker. // of the Visual C++ linker.
bool ICF::isEligible(SectionChunk *c) { 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; bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
if (!c->isCOMDAT() || !c->live || writable) if (!c->isCOMDAT() || !c->live || writable)
return false; return false;
@ -274,7 +274,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
for (Symbol *b : sc->symbols()) for (Symbol *b : sc->symbols())
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b)) if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
hash += sym->getChunk()->eqClass[cnt % 2]; 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); sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
}); });
} }
@ -297,7 +297,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
log("ICF needed " + Twine(cnt) + " iterations"); 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) { forEachClass([&](size_t begin, size_t end) {
if (end - begin == 1) if (end - begin == 1)
return; return;

View File

@ -47,6 +47,24 @@ using llvm::Triple;
using llvm::support::ulittle32_t; using llvm::support::ulittle32_t;
namespace lld { 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 { namespace coff {
std::vector<ObjFile *> ObjFile::instances; 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) {} ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
void ArchiveFile::parse() { void ArchiveFile::parse() {
@ -81,7 +103,7 @@ void ArchiveFile::parse() {
// Read the symbol table to construct Lazy objects. // Read the symbol table to construct Lazy objects.
for (const Archive::Symbol &sym : file->symbols()) 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. // Returns a buffer pointing to a member file containing a given symbol.
@ -116,6 +138,49 @@ std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
return v; 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() { void ObjFile::parse() {
// Parse a memory buffer as a COFF file. // Parse a memory buffer as a COFF file.
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this); std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
@ -206,10 +271,6 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
if (def) if (def)
c->checksum = def->CheckSum; 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 // CodeView sections are stored to a different vector because they are not
// linked in the regular manner. // linked in the regular manner.
if (c->isCodeView()) if (c->isCodeView())
@ -226,12 +287,18 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
// relocations, in .rdata, leader symbol name matches the MSVC name mangling // relocations, in .rdata, leader symbol name matches the MSVC name mangling
// for string literals) are subject to string tail merging. // for string literals) are subject to string tail merging.
MergeChunk::addSection(c); MergeChunk::addSection(c);
else if (name == ".rsrc" || name.startswith(".rsrc$"))
resourceChunks.push_back(c);
else else
chunks.push_back(c); chunks.push_back(c);
return c; return c;
} }
void ObjFile::includeResourceChunks() {
chunks.insert(chunks.end(), resourceChunks.begin(), resourceChunks.end());
}
void ObjFile::readAssociativeDefinition( void ObjFile::readAssociativeDefinition(
COFFSymbolRef sym, const coff_aux_section_definition *def) { COFFSymbolRef sym, const coff_aux_section_definition *def) {
readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj())); readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
@ -315,7 +382,8 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
StringRef name; StringRef name;
coffObj->getSymbolName(sym, name); coffObj->getSymbolName(sym, name);
if (sc) 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, // For MinGW symbols named .weak.* that point to a discarded section,
// don't create an Undefined symbol. If nothing ever refers to the symbol, // don't create an Undefined symbol. If nothing ever refers to the symbol,
// everything should be fine. If something actually 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. // if the two comdat sections have e.g. different alignment.
// Match that. // Match that.
if (leaderChunk->getContents() != newChunk.getContents()) if (leaderChunk->getContents() != newChunk.getContents())
symtab->reportDuplicate(leader, this); symtab->reportDuplicate(leader, this, &newChunk, sym.getValue());
break; break;
} }
@ -524,13 +592,11 @@ Optional<Symbol *> ObjFile::createDefined(
if (sym.isAbsolute()) { if (sym.isAbsolute()) {
StringRef name = getName(); StringRef name = getName();
// Skip special symbols. if (name == "@feat.00")
if (name == "@comp.id")
return nullptr;
if (name == "@feat.00") {
feat00Flags = sym.getValue(); feat00Flags = sym.getValue();
// Skip special symbols.
if (ignoredSymbolName(name))
return nullptr; return nullptr;
}
if (sym.isExternal()) if (sym.isExternal())
return symtab->addAbsolute(name, sym); return symtab->addAbsolute(name, sym);
@ -552,7 +618,7 @@ Optional<Symbol *> ObjFile::createDefined(
// Comdat handling. // Comdat handling.
// A comdat symbol consists of two symbol table entries. // A comdat symbol consists of two symbol table entries.
// The first symbol entry has the name of the section (e.g. .text), fixed // 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 // The second symbol entry has the name of the comdat symbol, called the
// "comdat leader". // "comdat leader".
// When this function is called for the first symbol entry of a comdat, // 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 {}; 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 // 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 // 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 // PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is
@ -723,6 +789,37 @@ void ObjFile::initializeDependencies() {
debugTypesObj = makeTpiSource(this); 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) { StringRef ltrim1(StringRef s, const char *chars) {
if (!s.empty() && strchr(chars, s[0])) if (!s.empty() && strchr(chars, s[0]))
return s.substr(1); return s.substr(1);
@ -780,8 +877,9 @@ void ImportFile::parse() {
} }
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive) uint64_t offsetInArchive,
: InputFile(BitcodeKind, mb) { std::vector<Symbol *> &&symbols)
: InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
std::string path = mb.getBufferIdentifier().str(); std::string path = mb.getBufferIdentifier().str();
if (config->thinLTOIndexOnly) if (config->thinLTOIndexOnly)
path = replaceThinLTOSuffix(mb.getBufferIdentifier()); path = replaceThinLTOSuffix(mb.getBufferIdentifier());
@ -860,22 +958,6 @@ std::string replaceThinLTOSuffix(StringRef path) {
return (path + repl).str(); return (path + repl).str();
return path; return path;
} }
} // namespace coff } // namespace coff
} // namespace lld } // 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();
}

View File

@ -10,10 +10,12 @@
#define LLD_COFF_INPUT_FILES_H #define LLD_COFF_INPUT_FILES_H
#include "Config.h" #include "Config.h"
#include "lld/Common/DWARF.h"
#include "lld/Common/LLVM.h" #include "lld/Common/LLVM.h"
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseSet.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h"
#include "llvm/LTO/LTO.h" #include "llvm/LTO/LTO.h"
#include "llvm/Object/Archive.h" #include "llvm/Object/Archive.h"
@ -24,6 +26,7 @@
#include <vector> #include <vector>
namespace llvm { namespace llvm {
struct DILineInfo;
namespace pdb { namespace pdb {
class DbiModuleDescriptorBuilder; class DbiModuleDescriptorBuilder;
} }
@ -47,7 +50,6 @@ class Defined;
class DefinedImportData; class DefinedImportData;
class DefinedImportThunk; class DefinedImportThunk;
class DefinedRegular; class DefinedRegular;
class Lazy;
class SectionChunk; class SectionChunk;
class Symbol; class Symbol;
class Undefined; class Undefined;
@ -56,7 +58,13 @@ class TpiSource;
// The root class of input files. // The root class of input files.
class InputFile { class InputFile {
public: public:
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind }; enum Kind {
ArchiveKind,
ObjectKind,
LazyObjectKind,
ImportKind,
BitcodeKind
};
Kind kind() const { return fileKind; } Kind kind() const { return fileKind; }
virtual ~InputFile() {} virtual ~InputFile() {}
@ -103,10 +111,28 @@ class ArchiveFile : public InputFile {
llvm::DenseSet<uint64_t> seen; 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. // .obj or .o file. This may be a member of an archive file.
class ObjFile : public InputFile { class ObjFile : public InputFile {
public: public:
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {} 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; } static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
void parse() override; void parse() override;
MachineTypes getMachineType() override; MachineTypes getMachineType() override;
@ -135,6 +161,10 @@ class ObjFile : public InputFile {
return symbols.size() - 1; return symbols.size() - 1;
} }
void includeResourceChunks();
bool isResourceObjFile() const { return !resourceChunks.empty(); }
static std::vector<ObjFile *> instances; static std::vector<ObjFile *> instances;
// Flags in the absolute @feat.00 symbol if it is present. These usually // Flags in the absolute @feat.00 symbol if it is present. These usually
@ -162,9 +192,6 @@ class ObjFile : public InputFile {
// precompiled object. Any difference indicates out-of-date objects. // precompiled object. Any difference indicates out-of-date objects.
llvm::Optional<uint32_t> pchSignature; 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. // Whether this file was compiled with /hotpatch.
bool hotPatchable = false; bool hotPatchable = false;
@ -177,6 +204,12 @@ class ObjFile : public InputFile {
// The .debug$T stream if there's one. // The .debug$T stream if there's one.
llvm::Optional<llvm::codeview::CVTypeArray> debugTypes; 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: private:
const coff_section* getSection(uint32_t i); const coff_section* getSection(uint32_t i);
const coff_section *getSection(COFFSymbolRef sym) { const coff_section *getSection(COFFSymbolRef sym) {
@ -234,6 +267,8 @@ class ObjFile : public InputFile {
// chunks and non-section chunks for common symbols. // chunks and non-section chunks for common symbols.
std::vector<Chunk *> chunks; std::vector<Chunk *> chunks;
std::vector<SectionChunk *> resourceChunks;
// CodeView debug info sections. // CodeView debug info sections.
std::vector<SectionChunk *> debugChunks; std::vector<SectionChunk *> debugChunks;
@ -258,6 +293,8 @@ class ObjFile : public InputFile {
// index. Nonexistent indices (which are occupied by auxiliary // index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers. // symbols in the real symbol table) are filled with null pointers.
std::vector<Symbol *> symbols; std::vector<Symbol *> symbols;
DWARFCache *dwarf = nullptr;
}; };
// This type represents import library members that contain DLL names // This type represents import library members that contain DLL names
@ -299,7 +336,11 @@ class ImportFile : public InputFile {
class BitcodeFile : public InputFile { class BitcodeFile : public InputFile {
public: public:
BitcodeFile(MemoryBufferRef mb, StringRef archiveName, 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; } static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
ArrayRef<Symbol *> getSymbols() { return symbols; } ArrayRef<Symbol *> getSymbols() { return symbols; }
MachineTypes getMachineType() override; MachineTypes getMachineType() override;
@ -312,6 +353,10 @@ class BitcodeFile : public InputFile {
std::vector<Symbol *> symbols; std::vector<Symbol *> symbols;
}; };
inline bool isBitcode(MemoryBufferRef mb) {
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
}
std::string replaceThinLTOSuffix(StringRef path); std::string replaceThinLTOSuffix(StringRef path);
} // namespace coff } // namespace coff

View File

@ -39,14 +39,14 @@
using namespace llvm; using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace lld; namespace lld {
using namespace lld::coff; namespace coff {
// Creates an empty file to and returns a raw_fd_ostream to write to it. // 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) { static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
std::error_code ec; std::error_code ec;
auto ret = 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) { if (ec) {
error("cannot open " + file + ": " + ec.message()); error("cannot open " + file + ": " + ec.message());
return nullptr; return nullptr;
@ -105,7 +105,7 @@ BitcodeCompiler::BitcodeCompiler() {
backend = lto::createInProcessThinBackend(config->thinLTOJobs); backend = lto::createInProcessThinBackend(config->thinLTOJobs);
} }
ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend, ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
config->ltoPartitions); config->ltoPartitions);
} }
@ -160,8 +160,8 @@ std::vector<StringRef> BitcodeCompiler::compile() {
checkError(ltoObj->run( checkError(ltoObj->run(
[&](size_t task) { [&](size_t task) {
return llvm::make_unique<lto::NativeObjectStream>( return std::make_unique<lto::NativeObjectStream>(
llvm::make_unique<raw_svector_ostream>(buf[task])); std::make_unique<raw_svector_ostream>(buf[task]));
}, },
cache)); cache));
@ -177,6 +177,8 @@ std::vector<StringRef> BitcodeCompiler::compile() {
// files. After that, we exit from linker and ThinLTO backend runs in a // files. After that, we exit from linker and ThinLTO backend runs in a
// distributed environment. // distributed environment.
if (config->thinLTOIndexOnly) { if (config->thinLTOIndexOnly) {
if (!config->ltoObjPath.empty())
saveBuffer(buf[0], config->ltoObjPath);
if (indexFile) if (indexFile)
indexFile->close(); indexFile->close();
return {}; return {};
@ -204,3 +206,6 @@ std::vector<StringRef> BitcodeCompiler::compile() {
return ret; return ret;
} }
} // namespace coff
} // namespace lld

View File

@ -29,14 +29,14 @@
using namespace llvm; using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace lld; namespace lld {
using namespace lld::coff; namespace coff {
using SymbolMapTy = using SymbolMapTy =
DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>; DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
static const std::string indent8 = " "; // 8 spaces static constexpr char indent8[] = " "; // 8 spaces
static const std::string indent16 = " "; // 16 spaces static constexpr char indent16[] = " "; // 16 spaces
// Print out the first three columns of a line. // Print out the first three columns of a line.
static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size, static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
@ -87,12 +87,12 @@ getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
return ret; return ret;
} }
void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) { void writeMapFile(ArrayRef<OutputSection *> outputSections) {
if (config->mapFile.empty()) if (config->mapFile.empty())
return; return;
std::error_code ec; 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) if (ec)
fatal("cannot open " + config->mapFile + ": " + ec.message()); fatal("cannot open " + config->mapFile + ": " + ec.message());
@ -122,3 +122,6 @@ void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
} }
} }
} }
} // namespace coff
} // namespace lld

View File

@ -13,11 +13,12 @@
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
using namespace lld;
using namespace lld::coff;
using namespace llvm; using namespace llvm;
using namespace llvm::COFF; using namespace llvm::COFF;
namespace lld {
namespace coff {
AutoExporter::AutoExporter() { AutoExporter::AutoExporter() {
excludeLibs = { excludeLibs = {
"libgcc", "libgcc",
@ -55,7 +56,7 @@ AutoExporter::AutoExporter() {
// C++ symbols // C++ symbols
"__rtti_", "__rtti_",
"__builtin_", "__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); return !excludeObjects.count(fileName);
} }
void coff::writeDefFile(StringRef name) { void writeDefFile(StringRef name) {
std::error_code ec; 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) if (ec)
fatal("cannot open " + name + ": " + ec.message()); fatal("cannot open " + name + ": " + ec.message());
@ -164,3 +165,6 @@ void coff::writeDefFile(StringRef name) {
os << "\n"; os << "\n";
} }
} }
} // namespace coff
} // namespace lld

View File

@ -21,9 +21,9 @@ def aligncomm : P<"aligncomm", "Set common symbol alignment">;
def alternatename : P<"alternatename", "Define weak alias">; def alternatename : P<"alternatename", "Define weak alias">;
def base : P<"base", "Base address of the program">; def base : P<"base", "Base address of the program">;
def color_diagnostics: Flag<["--"], "color-diagnostics">, def color_diagnostics: Flag<["--"], "color-diagnostics">,
HelpText<"Use colors in diagnostics">; HelpText<"Use colors in diagnostics">;
def color_diagnostics_eq: Joined<["--"], "color-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 defaultlib : P<"defaultlib", "Add the library to the list of input files">;
def delayload : P<"delayload", "Delay loaded DLL name">; def delayload : P<"delayload", "Delay loaded DLL name">;
def entry : P<"entry", "Name of entry point symbol">; 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 failifmismatch : P<"failifmismatch", "">;
def filealign : P<"filealign", "Section alignment in the output file">; def filealign : P<"filealign", "Section alignment in the output file">;
def functionpadmin : F<"functionpadmin">; 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 guard : P<"guard", "Control flow guard">;
def heap : P<"heap", "Size of the heap">; def heap : P<"heap", "Size of the heap">;
def ignore : P<"ignore", "Specify warning codes to ignore">; def ignore : P<"ignore", "Specify warning codes to ignore">;
@ -42,9 +43,14 @@ def implib : P<"implib", "Import library name">;
def lib : F<"lib">, def lib : F<"lib">,
HelpText<"Act like lib.exe; must be first argument if present">; HelpText<"Act like lib.exe; must be first argument if present">;
def libpath : P<"libpath", "Additional library search path">; def libpath : P<"libpath", "Additional library search path">;
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">; def linkrepro : P<"linkrepro",
def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">; "Dump linker invocation and input files for debugging">;
def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">; 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">, def lldsavetemps : F<"lldsavetemps">,
HelpText<"Save temporary files instead of deleting them">; HelpText<"Save temporary files instead of deleting them">;
def machine : P<"machine", "Specify target platform">; 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 out : P<"out", "Path to file to write output">;
def natvis : P<"natvis", "Path to natvis file to embed in the PDB">; def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
def no_color_diagnostics: F<"no-color-diagnostics">, 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 pdb : P<"pdb", "PDB file path">;
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">; def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
def section : P<"section", "Specify section attributes">; 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 subsystem : P<"subsystem", "Specify subsystem">;
def timestamp : P<"timestamp", "Specify the PE header timestamp">; def timestamp : P<"timestamp", "Specify the PE header timestamp">;
def version : P<"version", "Specify a version number in the PE header">; 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:">, def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
Alias<nodefaultlib>; Alias<nodefaultlib>;
@ -105,6 +112,8 @@ def noentry : F<"noentry">,
def profile : F<"profile">; def profile : F<"profile">;
def repro : F<"Brepro">, def repro : F<"Brepro">,
HelpText<"Use a hash of the executable as the PE header timestamp">; 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", def swaprun : P<"swaprun",
"Comma-separated list of 'cd' or 'net'">; "Comma-separated list of 'cd' or 'net'">;
def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>, 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"]>, def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
HelpText<"Make loader run output binary from swap instead of from network">; HelpText<"Make loader run output binary from swap instead of from network">;
def verbose : F<"verbose">; 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">, 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">, def force_unresolved : F<"force:unresolved">,
HelpText<"Allow undefined symbols when creating executables">; HelpText<"Allow undefined symbols when creating executables">;
def force_multiple : F<"force:multiple">, def force_multiple : F<"force:multiple">,
@ -162,6 +172,8 @@ def help : F<"help">;
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>; def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// LLD extensions // 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 exclude_all_symbols : F<"exclude-all-symbols">;
def export_all_symbols : F<"export-all-symbols">; def export_all_symbols : F<"export-all-symbols">;
defm demangle : B<"demangle", defm demangle : B<"demangle",
@ -173,9 +185,11 @@ def kill_at : F<"kill-at">;
def lldmingw : F<"lldmingw">; def lldmingw : F<"lldmingw">;
def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">; def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
def pdb_source_path : P<"pdbsourcepath", 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=">, def rsp_quoting : Joined<["--"], "rsp-quoting=">,
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">; 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 : def thinlto_emit_imports_files :
F<"thinlto-emit-imports-files">, F<"thinlto-emit-imports-files">,
HelpText<"Emit .imports files with -thinlto-index-only">; HelpText<"Emit .imports files with -thinlto-index-only">;
@ -191,6 +205,9 @@ def thinlto_object_suffix_replace : P<
def thinlto_prefix_replace: P< def thinlto_prefix_replace: P<
"thinlto-prefix-replace", "thinlto-prefix-replace",
"'old;new' replace old prefix with new prefix in ThinLTO outputs">; "'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">, def dash_dash_version : Flag<["--"], "version">,
HelpText<"Print version information">; HelpText<"Print version information">;
defm threads: B<"threads", defm threads: B<"threads",

View File

@ -51,21 +51,22 @@
#include "llvm/Object/COFF.h" #include "llvm/Object/COFF.h"
#include "llvm/Object/CVDebugRecord.h" #include "llvm/Object/CVDebugRecord.h"
#include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/CRC.h"
#include "llvm/Support/Endian.h" #include "llvm/Support/Endian.h"
#include "llvm/Support/Errc.h" #include "llvm/Support/Errc.h"
#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/JamCRC.h"
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/ScopedPrinter.h"
#include <memory> #include <memory>
using namespace lld;
using namespace lld::coff;
using namespace llvm; using namespace llvm;
using namespace llvm::codeview; using namespace llvm::codeview;
using llvm::object::coff_section; using llvm::object::coff_section;
namespace lld {
namespace coff {
static ExitOnError exitOnErr; static ExitOnError exitOnErr;
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root()); static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
@ -513,16 +514,15 @@ static bool equals_path(StringRef path1, StringRef path2) {
return path1.equals(path2); return path1.equals(path2);
#endif #endif
} }
// Find by name an OBJ provided on the command line // Find by name an OBJ provided on the command line
static ObjFile *findObjByName(StringRef fileNameOnly) { static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly,
SmallString<128> currentPath; uint32_t precompSignature) {
for (ObjFile *f : ObjFile::instances) { for (ObjFile *f : ObjFile::instances) {
StringRef currentFileName = sys::path::filename(f->getName()); StringRef currentFileName = sys::path::filename(f->getName());
// Compare based solely on the file name (link.exe behavior) if (f->pchSignature.hasValue() &&
if (equals_path(currentFileName, fileNameOnly)) f->pchSignature.getValue() == precompSignature &&
equals_path(fileNameOnly, currentFileName))
return f; return f;
} }
return nullptr; 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 // link.exe requires that a precompiled headers object must always be provided
// on the command-line, even if that's not necessary. // on the command-line, even if that's not necessary.
auto precompFile = findObjByName(precompFileName); auto precompFile =
findObjWithPrecompSignature(precompFileName, precomp.Signature);
if (!precompFile) if (!precompFile)
return createFileError( return createFileError(
precompFileName.str(), precomp.getPrecompFilePath().str(),
make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref)); make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
addObjFile(precompFile, &indexMap); 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; return indexMap;
} }
@ -965,9 +958,7 @@ static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
sc.Imod = secChunk->file->moduleDBI->getModuleIndex(); sc.Imod = secChunk->file->moduleDBI->getModuleIndex();
ArrayRef<uint8_t> contents = secChunk->getContents(); ArrayRef<uint8_t> contents = secChunk->getContents();
JamCRC crc(0); JamCRC crc(0);
ArrayRef<char> charContents = makeArrayRef( crc.update(contents);
reinterpret_cast<const char *>(contents.data()), contents.size());
crc.update(charContents);
sc.DataCrc = crc.getCRC(); sc.DataCrc = crc.getCRC();
} else { } else {
sc.Characteristics = os ? os->header.Characteristics : 0; sc.Characteristics = os ? os->header.Characteristics : 0;
@ -1150,7 +1141,7 @@ void DebugSHandler::finish() {
// string table. Generally the string table subsection appears after the // string table. Generally the string table subsection appears after the
// checksum table, so we have to do this after looping over all the // checksum table, so we have to do this after looping over all the
// subsections. // subsections.
auto newChecksums = make_unique<DebugChecksumsSubsection>(linker.pdbStrTab); auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
for (FileChecksumEntry &fc : checksums) { for (FileChecksumEntry &fc : checksums) {
SmallString<128> filename = SmallString<128> filename =
exitOnErr(cVStrTab.getString(fc.FileNameOffset)); exitOnErr(cVStrTab.getString(fc.FileNameOffset));
@ -1599,7 +1590,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
} }
// Creates a PDB file. // Creates a PDB file.
void coff::createPDB(SymbolTable *symtab, void createPDB(SymbolTable *symtab,
ArrayRef<OutputSection *> outputSections, ArrayRef<OutputSection *> outputSections,
ArrayRef<uint8_t> sectionTable, ArrayRef<uint8_t> sectionTable,
llvm::codeview::DebugInfo *buildId) { llvm::codeview::DebugInfo *buildId) {
@ -1693,6 +1684,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
} }
void PDBLinker::commit(codeview::GUID *guid) { void PDBLinker::commit(codeview::GUID *guid) {
ExitOnError exitOnErr((config->pdbPath + ": ").str());
// Write to a file. // Write to a file.
exitOnErr(builder.commit(config->pdbPath, guid)); 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 // 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. // not found.
std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c, Optional<std::pair<StringRef, uint32_t>>
uint32_t addr) { getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
ExitOnError exitOnErr; ExitOnError exitOnErr;
DebugStringTableSubsectionRef cVStrTab; DebugStringTableSubsectionRef cVStrTab;
@ -1809,7 +1801,7 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
uint32_t offsetInLinetable; uint32_t offsetInLinetable;
if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable)) if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
return {"", 0}; return None;
Optional<uint32_t> nameIndex; Optional<uint32_t> nameIndex;
Optional<uint32_t> lineNumber; Optional<uint32_t> lineNumber;
@ -1823,14 +1815,17 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
} }
StringRef filename = StringRef filename =
exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
return {filename, *lineNumber}; return std::make_pair(filename, *lineNumber);
} }
nameIndex = entry.NameIndex; nameIndex = entry.NameIndex;
lineNumber = li.getStartLine(); lineNumber = li.getStartLine();
} }
} }
if (!nameIndex) if (!nameIndex)
return {"", 0}; return None;
StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex)); StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
return {filename, *lineNumber}; return std::make_pair(filename, *lineNumber);
} }
} // namespace coff
} // namespace lld

View File

@ -10,6 +10,7 @@
#define LLD_COFF_PDB_H #define LLD_COFF_PDB_H
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
namespace llvm { namespace llvm {
@ -29,9 +30,9 @@ void createPDB(SymbolTable *symtab,
llvm::ArrayRef<uint8_t> sectionTable, llvm::ArrayRef<uint8_t> sectionTable,
llvm::codeview::DebugInfo *buildId); llvm::codeview::DebugInfo *buildId);
std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c, llvm::Optional<std::pair<llvm::StringRef, uint32_t>>
uint32_t addr); getFileLineCodeView(const SectionChunk *c, uint32_t addr);
} } // namespace coff
} } // namespace lld
#endif #endif

View File

@ -15,6 +15,7 @@
#include "lld/Common/ErrorHandler.h" #include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h" #include "lld/Common/Memory.h"
#include "lld/Common/Timer.h" #include "lld/Common/Timer.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Object/WindowsMachineFlag.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
@ -61,6 +62,24 @@ static void errorOrWarn(const Twine &s) {
error(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. // 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 // This is generally the global variable or function whose definition contains
// Addr. // Addr.
@ -69,7 +88,8 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
for (Symbol *s : sc->file->getSymbols()) { for (Symbol *s : sc->file->getSymbols()) {
auto *d = dyn_cast_or_null<DefinedRegular>(s); 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())) (candidate && d->getValue() < candidate->getValue()))
continue; continue;
@ -79,6 +99,38 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
return candidate; 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 // 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 // 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 // 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()) { for (const coff_relocation &r : sc->getRelocs()) {
if (r.SymbolTableIndex != symIndex) if (r.SymbolTableIndex != symIndex)
continue; continue;
std::pair<StringRef, uint32_t> fileLine = Optional<std::pair<StringRef, uint32_t>> fileLine =
getFileLine(sc, r.VirtualAddress); getFileLine(sc, r.VirtualAddress);
Symbol *sym = getSymbol(sc, r.VirtualAddress); Symbol *sym = getSymbol(sc, r.VirtualAddress);
if (!fileLine.first.empty() || sym) if (fileLine)
locations.push_back({sym, 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; 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 // For an undefined symbol, stores all files referencing it and the index of
// the undefined symbol in each file. // the undefined symbol in each file.
struct UndefinedDiag { struct UndefinedDiag {
Symbol *sym; Symbol *sym;
struct File { struct File {
ObjFile *oFile; InputFile *file;
uint64_t symIndex; uint32_t symIndex;
}; };
std::vector<File> files; std::vector<File> files;
}; };
@ -143,7 +207,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
size_t i = 0, numRefs = 0; size_t i = 0, numRefs = 0;
for (const UndefinedDiag::File &ref : undefDiag.files) { for (const UndefinedDiag::File &ref : undefDiag.files) {
std::vector<std::string> symbolLocations = std::vector<std::string> symbolLocations =
getSymbolLocations(ref.oFile, ref.symIndex); getSymbolLocations(ref.file, ref.symIndex);
numRefs += symbolLocations.size(); numRefs += symbolLocations.size();
for (const std::string &s : symbolLocations) { for (const std::string &s : symbolLocations) {
if (i >= maxUndefReferences) if (i >= maxUndefReferences)
@ -165,28 +229,33 @@ void SymbolTable::loadMinGWAutomaticImports() {
continue; continue;
if (!sym->isUsedInRegularObj) if (!sym->isUsedInRegularObj)
continue; continue;
if (undef->getWeakAlias())
continue;
StringRef name = undef->getName(); StringRef name = undef->getName();
if (name.startswith("__imp_")) if (name.startswith("__imp_"))
continue; continue;
// If we have an undefined symbol, but we have a Lazy representing a // If we have an undefined symbol, but we have a lazy symbol we could
// symbol we could load from file, make sure to load that. // load, load it.
Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str())); Symbol *l = find(("__imp_" + name).str());
if (!l || l->pendingArchiveLoad) if (!l || l->pendingArchiveLoad || !l->isLazy())
continue; continue;
log("Loading lazy " + l->getName() + " from " + l->file->getName() + log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() +
" for automatic import"); " for automatic import");
l->pendingArchiveLoad = true; forceLazy(l);
l->file->addMember(l->sym);
} }
} }
bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) { Defined *SymbolTable::impSymbol(StringRef name) {
if (name.startswith("__imp_")) if (name.startswith("__imp_"))
return false; return nullptr;
Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str())); return dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
}
bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
Defined *imp = impSymbol(name);
if (!imp) if (!imp)
return false; return false;
@ -232,7 +301,97 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
return true; 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; SmallPtrSet<Symbol *, 8> undefs;
DenseMap<Symbol *, Symbol *> localImports; DenseMap<Symbol *, Symbol *> localImports;
@ -290,46 +449,9 @@ void SymbolTable::reportRemainingUndefines() {
undefs.insert(sym); undefs.insert(sym);
} }
if (undefs.empty() && localImports.empty()) reportProblemSymbols(
return; undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
ObjFile::instances, /* bitcode files no longer needed */ nullptr);
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);
} }
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) { std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
@ -356,26 +478,22 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
Symbol *s; Symbol *s;
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(name, f); std::tie(s, wasInserted) = insert(name, f);
if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) { if (wasInserted || (s->isLazy() && isWeakAlias)) {
replaceSymbol<Undefined>(s, name); replaceSymbol<Undefined>(s, name);
return s; return s;
} }
if (auto *l = dyn_cast<Lazy>(s)) { if (s->isLazy())
if (!s->pendingArchiveLoad) { forceLazy(s);
s->pendingArchiveLoad = true;
l->file->addMember(l->sym);
}
}
return 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(); StringRef name = sym.getName();
Symbol *s; Symbol *s;
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(name); std::tie(s, wasInserted) = insert(name);
if (wasInserted) { if (wasInserted) {
replaceSymbol<Lazy>(s, f, sym); replaceSymbol<LazyArchive>(s, f, sym);
return; return;
} }
auto *u = dyn_cast<Undefined>(s); auto *u = dyn_cast<Undefined>(s);
@ -385,15 +503,84 @@ void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) {
f->addMember(sym); f->addMember(sym);
} }
void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) { void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) {
std::string msg = "duplicate symbol: " + toString(*existing) + " in " + Symbol *s;
toString(existing->getFile()) + " and in " + bool wasInserted;
toString(newFile); 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->fetch();
}
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) if (config->forceMultiple)
warn(msg); warn(os.str());
else else
error(msg); error(os.str());
} }
Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) { Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
@ -401,7 +588,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr); std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedAbsolute>(s, n, sym); replaceSymbol<DefinedAbsolute>(s, n, sym);
else if (!isa<DefinedCOFF>(s)) else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr); reportDuplicate(s, nullptr);
@ -413,7 +600,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr); std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedAbsolute>(s, n, va); replaceSymbol<DefinedAbsolute>(s, n, va);
else if (!isa<DefinedCOFF>(s)) else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr); reportDuplicate(s, nullptr);
@ -425,7 +612,7 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr); std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) if (wasInserted || isa<Undefined>(s) || s->isLazy())
replaceSymbol<DefinedSynthetic>(s, n, c); replaceSymbol<DefinedSynthetic>(s, n, c);
else if (!isa<DefinedCOFF>(s)) else if (!isa<DefinedCOFF>(s))
reportDuplicate(s, nullptr); reportDuplicate(s, nullptr);
@ -433,8 +620,8 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
} }
Symbol *SymbolTable::addRegular(InputFile *f, StringRef n, Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
const coff_symbol_generic *sym, const coff_symbol_generic *sym, SectionChunk *c,
SectionChunk *c) { uint32_t sectionOffset) {
Symbol *s; Symbol *s;
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, f); 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, replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
/*IsExternal*/ true, sym, c); /*IsExternal*/ true, sym, c);
else else
reportDuplicate(s, f); reportDuplicate(s, f, c, sectionOffset);
return s; return s;
} }
@ -481,7 +668,7 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(n, nullptr); std::tie(s, wasInserted) = insert(n, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) { if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
replaceSymbol<DefinedImportData>(s, n, f); replaceSymbol<DefinedImportData>(s, n, f);
return s; return s;
} }
@ -496,7 +683,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
bool wasInserted; bool wasInserted;
std::tie(s, wasInserted) = insert(name, nullptr); std::tie(s, wasInserted) = insert(name, nullptr);
s->isUsedInRegularObj = true; s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) { if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
replaceSymbol<DefinedImportThunk>(s, name, id, machine); replaceSymbol<DefinedImportThunk>(s, name, id, machine);
return s; return s;
} }
@ -510,9 +697,12 @@ void SymbolTable::addLibcall(StringRef name) {
if (!sym) if (!sym)
return; return;
if (Lazy *l = dyn_cast<Lazy>(sym)) { if (auto *l = dyn_cast<LazyArchive>(sym)) {
MemoryBufferRef mb = l->getMemberBuffer(); MemoryBufferRef mb = l->getMemberBuffer();
if (identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode) if (isBitcode(mb))
addUndefined(sym->getName());
} else if (LazyObject *o = dyn_cast<LazyObject>(sym)) {
if (isBitcode(o->file->mb))
addUndefined(sym->getName()); addUndefined(sym->getName());
} }
} }

View File

@ -29,7 +29,7 @@ class Defined;
class DefinedAbsolute; class DefinedAbsolute;
class DefinedRegular; class DefinedRegular;
class DefinedRelative; class DefinedRelative;
class Lazy; class LazyArchive;
class SectionChunk; class SectionChunk;
class Symbol; class Symbol;
@ -49,10 +49,13 @@ class SymbolTable {
public: public:
void addFile(InputFile *file); 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 // Try to resolve any undefined symbols and update the symbol table
// accordingly, then print an error message for any remaining undefined // accordingly, then print an error message for any remaining undefined
// symbols. // symbols and warn about imported local symbols.
void reportRemainingUndefines(); void resolveRemainingUndefines();
void loadMinGWAutomaticImports(); void loadMinGWAutomaticImports();
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name); bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
@ -83,11 +86,12 @@ class SymbolTable {
Symbol *addAbsolute(StringRef n, uint64_t va); Symbol *addAbsolute(StringRef n, uint64_t va);
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias); 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 *addAbsolute(StringRef n, COFFSymbolRef s);
Symbol *addRegular(InputFile *f, StringRef n, Symbol *addRegular(InputFile *f, StringRef n,
const llvm::object::coff_symbol_generic *s = nullptr, const llvm::object::coff_symbol_generic *s = nullptr,
SectionChunk *c = nullptr); SectionChunk *c = nullptr, uint32_t sectionOffset = 0);
std::pair<DefinedRegular *, bool> std::pair<DefinedRegular *, bool>
addComdat(InputFile *f, StringRef n, addComdat(InputFile *f, StringRef n,
const llvm::object::coff_symbol_generic *s = nullptr); const llvm::object::coff_symbol_generic *s = nullptr);
@ -99,7 +103,9 @@ class SymbolTable {
uint16_t machine); uint16_t machine);
void addLibcall(StringRef name); 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. // A list of chunks which to be added to .rdata.
std::vector<Chunk *> localImportChunks; std::vector<Chunk *> localImportChunks;
@ -111,6 +117,9 @@ class SymbolTable {
} }
private: 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. /// Inserts symbol if not already present.
std::pair<Symbol *, bool> insert(StringRef name); std::pair<Symbol *, bool> insert(StringRef name);
/// Same as insert(Name), but also sets isUsedInRegularObj. /// Same as insert(Name), but also sets isUsedInRegularObj.

View File

@ -12,6 +12,7 @@
#include "lld/Common/Memory.h" #include "lld/Common/Memory.h"
#include "lld/Common/Strings.h" #include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/Debug.h" #include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
@ -26,15 +27,27 @@ static_assert(sizeof(SymbolUnion) <= 48,
"symbols should be optimized for memory usage"); "symbols should be optimized for memory usage");
// Returns a symbol name for an error message. // Returns a symbol name for an error message.
static std::string demangle(StringRef symName) { static std::string maybeDemangleSymbol(StringRef symName) {
if (config->demangle) if (config->demangle) {
if (Optional<std::string> s = demangleMSVC(symName)) std::string prefix;
return *s; 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; return symName;
} }
std::string toString(coff::Symbol &b) { return demangle(b.getName()); } std::string toString(coff::Symbol &b) {
return maybeDemangleSymbol(b.getName());
}
std::string toCOFFString(const Archive::Symbol &b) { std::string toCOFFString(const Archive::Symbol &b) {
return demangle(b.getName()); return maybeDemangleSymbol(b.getName());
} }
namespace coff { namespace coff {
@ -61,7 +74,9 @@ StringRef Symbol::getName() {
InputFile *Symbol::getFile() { InputFile *Symbol::getFile() {
if (auto *sym = dyn_cast<DefinedCOFF>(this)) if (auto *sym = dyn_cast<DefinedCOFF>(this))
return sym->file; 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 sym->file;
return nullptr; return nullptr;
} }
@ -119,7 +134,7 @@ Defined *Undefined::getWeakAlias() {
return nullptr; return nullptr;
} }
MemoryBufferRef Lazy::getMemberBuffer() { MemoryBufferRef LazyArchive::getMemberBuffer() {
Archive::Child c = Archive::Child c =
CHECK(sym.getMember(), CHECK(sym.getMember(),
"could not get the member for symbol " + toCOFFString(sym)); "could not get the member for symbol " + toCOFFString(sym));

View File

@ -59,7 +59,8 @@ class Symbol {
DefinedSyntheticKind, DefinedSyntheticKind,
UndefinedKind, UndefinedKind,
LazyKind, LazyArchiveKind,
LazyObjectKind,
LastDefinedCOFFKind = DefinedCommonKind, LastDefinedCOFFKind = DefinedCommonKind,
LastDefinedKind = DefinedSyntheticKind, LastDefinedKind = DefinedSyntheticKind,
@ -79,6 +80,10 @@ class Symbol {
// after calling markLive. // after calling markLive.
bool isLive() const; bool isLive() const;
bool isLazy() const {
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
protected: protected:
friend SymbolTable; friend SymbolTable;
explicit Symbol(Kind k, StringRef n = "") explicit Symbol(Kind k, StringRef n = "")
@ -256,26 +261,29 @@ class DefinedSynthetic : public Defined {
// This class represents a symbol defined in an archive file. It is // 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 // created from an archive file header, and it knows how to load an
// object file from an archive to replace itself with a defined // object file from an archive to replace itself with a defined
// symbol. If the resolver finds both Undefined and Lazy for // symbol. If the resolver finds both Undefined and LazyArchive for
// the same name, it will ask the Lazy to load a file. // the same name, it will ask the LazyArchive to load a file.
class Lazy : public Symbol { class LazyArchive : public Symbol {
public: public:
Lazy(ArchiveFile *f, const Archive::Symbol s) LazyArchive(ArchiveFile *f, const Archive::Symbol s)
: Symbol(LazyKind, s.getName()), file(f), sym(s) {} : Symbol(LazyArchiveKind, s.getName()), file(f), sym(s) {}
static bool classof(const Symbol *s) { return s->kind() == LazyKind; } static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
MemoryBufferRef getMemberBuffer(); MemoryBufferRef getMemberBuffer();
ArchiveFile *file; ArchiveFile *file;
private:
friend SymbolTable;
private:
const Archive::Symbol sym; 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. // Undefined symbols.
class Undefined : public Symbol { class Undefined : public Symbol {
public: public:
@ -381,7 +389,8 @@ inline uint64_t Defined::getRVA() {
return cast<DefinedCommon>(this)->getRVA(); return cast<DefinedCommon>(this)->getRVA();
case DefinedRegularKind: case DefinedRegularKind:
return cast<DefinedRegular>(this)->getRVA(); return cast<DefinedRegular>(this)->getRVA();
case LazyKind: case LazyArchiveKind:
case LazyObjectKind:
case UndefinedKind: case UndefinedKind:
llvm_unreachable("Cannot get the address for an undefined symbol."); llvm_unreachable("Cannot get the address for an undefined symbol.");
} }
@ -404,7 +413,8 @@ inline Chunk *Defined::getChunk() {
return cast<DefinedLocalImport>(this)->getChunk(); return cast<DefinedLocalImport>(this)->getChunk();
case DefinedCommonKind: case DefinedCommonKind:
return cast<DefinedCommon>(this)->getChunk(); return cast<DefinedCommon>(this)->getChunk();
case LazyKind: case LazyArchiveKind:
case LazyObjectKind:
case UndefinedKind: case UndefinedKind:
llvm_unreachable("Cannot get the chunk of an undefined symbol."); llvm_unreachable("Cannot get the chunk of an undefined symbol.");
} }
@ -419,11 +429,12 @@ union SymbolUnion {
alignas(DefinedCommon) char b[sizeof(DefinedCommon)]; alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)]; alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)]; 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(Undefined) char f[sizeof(Undefined)];
alignas(DefinedImportData) char g[sizeof(DefinedImportData)]; alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)]; alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)]; alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
alignas(LazyObject) char j[sizeof(LazyObject)];
}; };
template <typename T, typename... ArgT> template <typename T, typename... ArgT>

View File

@ -40,8 +40,9 @@ using namespace llvm::COFF;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support; using namespace llvm::support;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace lld;
using namespace lld::coff; namespace lld {
namespace coff {
/* To re-generate DOSProgram: /* To re-generate DOSProgram:
$ cat > /tmp/DOSProgram.asm $ cat > /tmp/DOSProgram.asm
@ -240,6 +241,8 @@ class Writer {
IdataContents idata; IdataContents idata;
Chunk *importTableStart = nullptr; Chunk *importTableStart = nullptr;
uint64_t importTableSize = 0; uint64_t importTableSize = 0;
Chunk *edataStart = nullptr;
Chunk *edataEnd = nullptr;
Chunk *iatStart = nullptr; Chunk *iatStart = nullptr;
uint64_t iatSize = 0; uint64_t iatSize = 0;
DelayLoadContents delayIdata; DelayLoadContents delayIdata;
@ -283,9 +286,6 @@ class Writer {
}; };
} // anonymous namespace } // anonymous namespace
namespace lld {
namespace coff {
static Timer codeLayoutTimer("Code Layout", Timer::root()); static Timer codeLayoutTimer("Code Layout", Timer::root());
static Timer diskCommitTimer("Commit Output File", Timer::root()); static Timer diskCommitTimer("Commit Output File", Timer::root());
@ -331,9 +331,6 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
contribSections.push_back(sec); contribSections.push_back(sec);
} }
} // namespace coff
} // namespace lld
// Check whether the target address S is in range from a relocation // Check whether the target address S is in range from a relocation
// of type relType at address P. // of type relType at address P.
static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) { static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
@ -741,7 +738,8 @@ void Writer::addSyntheticIdata() {
add(".idata$2", idata.dirs); add(".idata$2", idata.dirs);
add(".idata$4", idata.lookups); add(".idata$4", idata.lookups);
add(".idata$5", idata.addresses); add(".idata$5", idata.addresses);
add(".idata$6", idata.hints); if (!idata.hints.empty())
add(".idata$6", idata.hints);
add(".idata$7", idata.dllNames); add(".idata$7", idata.dllNames);
} }
@ -840,6 +838,7 @@ void Writer::createSections() {
} }
fixPartialSectionChars(".rsrc", data | r); fixPartialSectionChars(".rsrc", data | r);
fixPartialSectionChars(".edata", data | r);
// Even in non MinGW cases, we might need to link against GNU import // Even in non MinGW cases, we might need to link against GNU import
// libraries. // libraries.
bool hasIdata = fixGnuImportChunks(); bool hasIdata = fixGnuImportChunks();
@ -1014,10 +1013,19 @@ void Writer::appendImportThunks() {
} }
void Writer::createExportTable() { void Writer::createExportTable() {
if (config->exports.empty()) if (!edataSec->chunks.empty()) {
return; // Allow using a custom built export table from input object files, instead
for (Chunk *c : edata.chunks) // of having the linker synthesize the tables.
edataSec->addChunk(c); 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() { void Writer::removeUnusedSections() {
@ -1366,9 +1374,10 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
// Write data directory // Write data directory
auto *dir = reinterpret_cast<data_directory *>(buf); auto *dir = reinterpret_cast<data_directory *>(buf);
buf += sizeof(*dir) * numberOfDataDirectory; buf += sizeof(*dir) * numberOfDataDirectory;
if (!config->exports.empty()) { if (edataStart) {
dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA(); dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA();
dir[EXPORT_TABLE].Size = edata.getSize(); dir[EXPORT_TABLE].Size =
edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA();
} }
if (importTableStart) { if (importTableStart) {
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA(); dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
@ -1506,7 +1515,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
// Absolute is never code, synthetic generally isn't and usually isn't // Absolute is never code, synthetic generally isn't and usually isn't
// determinable. // determinable.
break; break;
case Symbol::LazyKind: case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
case Symbol::UndefinedKind: case Symbol::UndefinedKind:
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy // Undefined symbols resolve to zero, so they don't have an RVA. Lazy
// symbols shouldn't have relocations. // symbols shouldn't have relocations.
@ -1930,3 +1940,6 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
return it->second; return it->second;
return nullptr; return nullptr;
} }
} // namespace coff
} // namespace lld

View File

@ -29,6 +29,7 @@ set_property(SOURCE Version.cpp APPEND PROPERTY
add_lld_library(lldCommon add_lld_library(lldCommon
Args.cpp Args.cpp
DWARF.cpp
ErrorHandler.cpp ErrorHandler.cpp
Filesystem.cpp Filesystem.cpp
Memory.cpp Memory.cpp
@ -46,6 +47,7 @@ add_lld_library(lldCommon
LINK_COMPONENTS LINK_COMPONENTS
Codegen Codegen
Core Core
DebugInfoDWARF
Demangle Demangle
MC MC
Option Option

View File

@ -0,0 +1,103 @@
//===- DWARF.cpp ----------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lld/Common/DWARF.h"
#include "lld/Common/ErrorHandler.h"
using namespace llvm;
namespace lld {
DWARFCache::DWARFCache(std::unique_ptr<llvm::DWARFContext> d)
: dwarf(std::move(d)) {
for (std::unique_ptr<DWARFUnit> &cu : dwarf->compile_units()) {
auto report = [](Error err) {
handleAllErrors(std::move(err),
[](ErrorInfoBase &info) { warn(info.message()); });
};
Expected<const DWARFDebugLine::LineTable *> expectedLT =
dwarf->getLineTableForUnit(cu.get(), report);
const DWARFDebugLine::LineTable *lt = nullptr;
if (expectedLT)
lt = *expectedLT;
else
report(expectedLT.takeError());
if (!lt)
continue;
lineTables.push_back(lt);
// Loop over variable records and insert them to variableLoc.
for (const auto &entry : cu->dies()) {
DWARFDie die(cu.get(), &entry);
// Skip all tags that are not variables.
if (die.getTag() != dwarf::DW_TAG_variable)
continue;
// Skip if a local variable because we don't need them for generating
// error messages. In general, only non-local symbols can fail to be
// linked.
if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0))
continue;
// Get the source filename index for the variable.
unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0);
if (!lt->hasFileAtIndex(file))
continue;
// Get the line number on which the variable is declared.
unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0);
// Here we want to take the variable name to add it into variableLoc.
// Variable can have regular and linkage name associated. At first, we try
// to get linkage name as it can be different, for example when we have
// two variables in different namespaces of the same object. Use common
// name otherwise, but handle the case when it also absent in case if the
// input object file lacks some debug info.
StringRef name =
dwarf::toString(die.find(dwarf::DW_AT_linkage_name),
dwarf::toString(die.find(dwarf::DW_AT_name), ""));
if (!name.empty())
variableLoc.insert({name, {lt, file, line}});
}
}
}
// Returns the pair of file name and line number describing location of data
// object (variable, array, etc) definition.
Optional<std::pair<std::string, unsigned>>
DWARFCache::getVariableLoc(StringRef name) {
// Return if we have no debug information about data object.
auto it = variableLoc.find(name);
if (it == variableLoc.end())
return None;
// Take file name string from line table.
std::string fileName;
if (!it->second.lt->getFileNameByIndex(
it->second.file, {},
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName))
return None;
return std::make_pair(fileName, it->second.line);
}
// Returns source line information for a given offset
// using DWARF debug info.
Optional<DILineInfo> DWARFCache::getDILineInfo(uint64_t offset,
uint64_t sectionIndex) {
DILineInfo info;
for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) {
if (lt->getFileLineInfoForAddress(
{offset, sectionIndex}, nullptr,
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info))
return info;
}
return None;
}
} // namespace lld

View File

@ -29,16 +29,14 @@ using namespace lld;
// but outs() or errs() are not thread-safe. We protect them using a mutex. // but outs() or errs() are not thread-safe. We protect them using a mutex.
static std::mutex mu; static std::mutex mu;
// Prints "\n" or does nothing, depending on Msg contents of // We want to separate multi-line messages with a newline. `sep` is "\n"
// the previous call of this function. // if the last messages was multi-line. Otherwise "".
static void newline(raw_ostream *errorOS, const Twine &msg) { static StringRef sep;
// True if the previous error message contained "\n".
// We want to separate multi-line error messages with a newline.
static bool flag;
if (flag) static StringRef getSeparator(const Twine &msg) {
*errorOS << "\n"; if (StringRef(msg.str()).contains('\n'))
flag = StringRef(msg.str()).contains('\n'); return "\n";
return "";
} }
ErrorHandler &lld::errorHandler() { ErrorHandler &lld::errorHandler() {
@ -46,6 +44,10 @@ ErrorHandler &lld::errorHandler() {
return handler; return handler;
} }
void lld::enableColors(bool enable) {
errorHandler().errorOS->enable_colors(enable);
}
void lld::exitLld(int val) { void lld::exitLld(int val) {
// Delete any temporary file, while keeping the memory mapping open. // Delete any temporary file, while keeping the memory mapping open.
if (errorHandler().outputBuffer) if (errorHandler().outputBuffer)
@ -85,56 +87,69 @@ void lld::checkError(Error e) {
[&](ErrorInfoBase &eib) { error(eib.message()); }); [&](ErrorInfoBase &eib) { error(eib.message()); });
} }
static std::string getLocation(std::string msg, std::string defaultMsg) { // This is for --vs-diagnostics.
static std::vector<std::regex> Regexes{ //
std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"), // Normally, lld's error message starts with argv[0]. Therefore, it usually
// looks like this:
//
// ld.lld: error: ...
//
// This error message style is unfortunately unfriendly to Visual Studio
// IDE. VS interprets the first word of the first line as an error location
// and make it clickable, thus "ld.lld" in the above message would become a
// clickable text. When you click it, VS opens "ld.lld" executable file with
// a binary editor.
//
// As a workaround, we print out an error location instead of "ld.lld" if
// lld is running in VS diagnostics mode. As a result, error message will
// look like this:
//
// src/foo.c(35): error: ...
//
// This function returns an error location string. An error location is
// extracted from an error message using regexps.
std::string ErrorHandler::getLocation(const Twine &msg) {
if (!vsDiagnostics)
return logName;
static std::regex regexes[] = {
std::regex(
R"(^undefined (?:\S+ )?symbol:.*\n)"
R"(>>> referenced by .+\((\S+):(\d+)\))"),
std::regex(
R"(^undefined (?:\S+ )?symbol:.*\n>>> referenced by (\S+):(\d+))"),
std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"), std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"),
std::regex( std::regex(
R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"), R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"),
std::regex( std::regex(
R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+).*)"), R"(^duplicate symbol: .*\n>>> defined at .+\((\S+):(\d+)\))"),
std::regex(R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+))"),
std::regex( std::regex(
R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"), R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))"),
std::regex( std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),
R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"),
std::regex(R"((\S+):(\d+): unclosed quote)"), std::regex(R"((\S+):(\d+): unclosed quote)"),
}; };
std::smatch Match; std::string str = msg.str();
for (std::regex &Re : Regexes) { for (std::regex &re : regexes) {
if (std::regex_search(msg, Match, Re)) { std::smatch m;
return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")" if (!std::regex_search(str, m, re))
: Match.str(1); continue;
}
}
return defaultMsg;
}
void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c, assert(m.size() == 2 || m.size() == 3);
const Twine &msg) { if (m.size() == 2)
return m.str(1);
if (vsDiagnostics) { return m.str(1) + "(" + m.str(2) + ")";
// A Visual Studio-style error message starts with an error location.
// If a location cannot be extracted then we default to LogName.
*errorOS << getLocation(msg.str(), logName) << ": ";
} else {
*errorOS << logName << ": ";
} }
if (colorDiagnostics) { return logName;
errorOS->changeColor(c, true);
*errorOS << s;
errorOS->resetColor();
} else {
*errorOS << s;
}
} }
void ErrorHandler::log(const Twine &msg) { void ErrorHandler::log(const Twine &msg) {
if (verbose) { if (!verbose)
std::lock_guard<std::mutex> lock(mu); return;
*errorOS << logName << ": " << msg << "\n"; std::lock_guard<std::mutex> lock(mu);
} *errorOS << logName << ": " << msg << "\n";
} }
void ErrorHandler::message(const Twine &msg) { void ErrorHandler::message(const Twine &msg) {
@ -150,25 +165,41 @@ void ErrorHandler::warn(const Twine &msg) {
} }
std::lock_guard<std::mutex> lock(mu); std::lock_guard<std::mutex> lock(mu);
newline(errorOS, msg); *errorOS << sep << getLocation(msg) << ": " << Colors::MAGENTA
printHeader("warning: ", raw_ostream::MAGENTA, msg); << "warning: " << Colors::RESET << msg << "\n";
*errorOS << msg << "\n"; sep = getSeparator(msg);
} }
void ErrorHandler::error(const Twine &msg) { void ErrorHandler::error(const Twine &msg) {
// If Visual Studio-style error message mode is enabled,
// this particular error is printed out as two errors.
if (vsDiagnostics) {
static std::regex re(R"(^(duplicate symbol: .*))"
R"((\n>>> defined at \S+:\d+.*\n>>>.*))"
R"((\n>>> defined at \S+:\d+.*\n>>>.*))");
std::string str = msg.str();
std::smatch m;
if (std::regex_match(str, m, re)) {
error(m.str(1) + m.str(2));
error(m.str(1) + m.str(3));
return;
}
}
std::lock_guard<std::mutex> lock(mu); std::lock_guard<std::mutex> lock(mu);
newline(errorOS, msg);
if (errorLimit == 0 || errorCount < errorLimit) { if (errorLimit == 0 || errorCount < errorLimit) {
printHeader("error: ", raw_ostream::RED, msg); *errorOS << sep << getLocation(msg) << ": " << Colors::RED
*errorOS << msg << "\n"; << "error: " << Colors::RESET << msg << "\n";
} else if (errorCount == errorLimit) { } else if (errorCount == errorLimit) {
printHeader("error: ", raw_ostream::RED, msg); *errorOS << sep << getLocation(msg) << ": " << Colors::RED
*errorOS << errorLimitExceededMsg << "\n"; << "error: " << Colors::RESET << errorLimitExceededMsg << "\n";
if (exitEarly) if (exitEarly)
exitLld(1); exitLld(1);
} }
sep = getSeparator(msg);
++errorCount; ++errorCount;
} }

View File

@ -18,39 +18,17 @@
using namespace llvm; using namespace llvm;
using namespace lld; using namespace lld;
// Returns the demangled C++ symbol name for Name. // Returns the demangled C++ symbol name for name.
Optional<std::string> lld::demangleItanium(StringRef name) { std::string lld::demangleItanium(StringRef name) {
// itaniumDemangle can be used to demangle strings other than symbol // itaniumDemangle can be used to demangle strings other than symbol
// names which do not necessarily start with "_Z". Name can be // names which do not necessarily start with "_Z". Name can be
// either a C or C++ symbol. Don't call itaniumDemangle if the name // either a C or C++ symbol. Don't call demangle if the name
// does not look like a C++ symbol name to avoid getting unexpected // does not look like a C++ symbol name to avoid getting unexpected
// result for a C symbol that happens to match a mangled type name. // result for a C symbol that happens to match a mangled type name.
if (!name.startswith("_Z")) if (!name.startswith("_Z"))
return None; return name;
char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr); return demangle(name);
if (!buf)
return None;
std::string s(buf);
free(buf);
return s;
}
Optional<std::string> lld::demangleMSVC(StringRef name) {
std::string prefix;
if (name.consume_front("__imp_"))
prefix = "__declspec(dllimport) ";
// Demangle only C++ names.
if (!name.startswith("?"))
return None;
char *buf = microsoftDemangle(name.str().c_str(), nullptr, nullptr, nullptr);
if (!buf)
return None;
std::string s(buf);
free(buf);
return prefix + s;
} }
StringMatcher::StringMatcher(ArrayRef<StringRef> pat) { StringMatcher::StringMatcher(ArrayRef<StringRef> pat) {
@ -96,7 +74,7 @@ bool lld::isValidCIdentifier(StringRef s) {
// Write the contents of the a buffer to a file // Write the contents of the a buffer to a file
void lld::saveBuffer(StringRef buffer, const Twine &path) { void lld::saveBuffer(StringRef buffer, const Twine &path) {
std::error_code ec; std::error_code ec;
raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::F_None); raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::OF_None);
if (ec) if (ec)
error("cannot create " + path + ": " + ec.message()); error("cannot create " + path + ": " + ec.message());
os << buffer; os << buffer;

View File

@ -26,6 +26,10 @@ llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
return ::InitTargetOptionsFromCodeGenFlags(); return ::InitTargetOptionsFromCodeGenFlags();
} }
llvm::Optional<llvm::Reloc::Model> lld::getRelocModelFromCMModel() {
return getRelocModel();
}
llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() { llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
return getCodeModel(); return getCodeModel();
} }

View File

@ -6,7 +6,10 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// This file implements Section Patching for the purpose of working around // This file implements Section Patching for the purpose of working around
// errata in CPUs. The general principle is that an erratum sequence of one or // the AArch64 Cortex-53 errata 843419 that affects r0p0, r0p1, r0p2 and r0p4
// versions of the core.
//
// The general principle is that an erratum sequence of one or
// more instructions is detected in the instruction stream, one of the // more instructions is detected in the instruction stream, one of the
// instructions in the sequence is replaced with a branch to a patch sequence // instructions in the sequence is replaced with a branch to a patch sequence
// of replacement instructions. At the end of the replacement sequence the // of replacement instructions. At the end of the replacement sequence the
@ -20,12 +23,6 @@
// - We can overwrite an instruction in the erratum sequence with a branch to // - We can overwrite an instruction in the erratum sequence with a branch to
// the replacement sequence. // the replacement sequence.
// - We can place the replacement sequence within range of the branch. // - We can place the replacement sequence within range of the branch.
// FIXME:
// - The implementation here only supports one patch, the AArch64 Cortex-53
// errata 843419 that affects r0p0, r0p1, r0p2 and r0p4 versions of the core.
// To keep the initial version simple there is no support for multiple
// architectures or selection of different patches.
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "AArch64ErrataFix.h" #include "AArch64ErrataFix.h"
@ -48,8 +45,8 @@ using namespace llvm::object;
using namespace llvm::support; using namespace llvm::support;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
// Helper functions to identify instructions and conditions needed to trigger // Helper functions to identify instructions and conditions needed to trigger
// the Cortex-A53-843419 erratum. // the Cortex-A53-843419 erratum.
@ -333,16 +330,16 @@ static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2,
} }
// Scan the instruction sequence starting at Offset Off from the base of // Scan the instruction sequence starting at Offset Off from the base of
// InputSection IS. We update Off in this function rather than in the caller as // InputSection isec. We update Off in this function rather than in the caller
// we can skip ahead much further into the section when we know how many // as we can skip ahead much further into the section when we know how many
// instructions we've scanned. // instructions we've scanned.
// Return the offset of the load or store instruction in IS that we want to // Return the offset of the load or store instruction in isec that we want to
// patch or 0 if no patch required. // patch or 0 if no patch required.
static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off, static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
uint64_t limit) { uint64_t limit) {
uint64_t isecAddr = isec->getVA(0); uint64_t isecAddr = isec->getVA(0);
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8. // Advance Off so that (isecAddr + Off) modulo 0x1000 is at least 0xff8.
uint64_t initialPageOff = (isecAddr + off) & 0xfff; uint64_t initialPageOff = (isecAddr + off) & 0xfff;
if (initialPageOff < 0xff8) if (initialPageOff < 0xff8)
off += 0xff8 - initialPageOff; off += 0xff8 - initialPageOff;
@ -374,7 +371,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
return patchOff; return patchOff;
} }
class lld::elf::Patch843419Section : public SyntheticSection { class Patch843419Section : public SyntheticSection {
public: public:
Patch843419Section(InputSection *p, uint64_t off); Patch843419Section(InputSection *p, uint64_t off);
@ -386,13 +383,13 @@ class lld::elf::Patch843419Section : public SyntheticSection {
// The Section we are patching. // The Section we are patching.
const InputSection *patchee; const InputSection *patchee;
// The offset of the instruction in the Patchee section we are patching. // The offset of the instruction in the patchee section we are patching.
uint64_t patcheeOffset; uint64_t patcheeOffset;
// A label for the start of the Patch that we can use as a relocation target. // A label for the start of the Patch that we can use as a relocation target.
Symbol *patchSym; Symbol *patchSym;
}; };
lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off) Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4, : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
".text.patch"), ".text.patch"),
patchee(p), patcheeOffset(off) { patchee(p), patcheeOffset(off) {
@ -403,16 +400,16 @@ lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this); addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this);
} }
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const { uint64_t Patch843419Section::getLDSTAddr() const {
return patchee->getVA(patcheeOffset); return patchee->getVA(patcheeOffset);
} }
void lld::elf::Patch843419Section::writeTo(uint8_t *buf) { void Patch843419Section::writeTo(uint8_t *buf) {
// Copy the instruction that we will be replacing with a branch in the // Copy the instruction that we will be replacing with a branch in the
// Patchee Section. // patchee Section.
write32le(buf, read32le(patchee->data().begin() + patcheeOffset)); write32le(buf, read32le(patchee->data().begin() + patcheeOffset));
// Apply any relocation transferred from the original PatcheeSection. // Apply any relocation transferred from the original patchee section.
// For a SyntheticSection Buf already has outSecOff added, but relocateAlloc // For a SyntheticSection Buf already has outSecOff added, but relocateAlloc
// also adds outSecOff so we need to subtract to avoid double counting. // also adds outSecOff so we need to subtract to avoid double counting.
this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize()); this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
@ -461,18 +458,18 @@ void AArch64Err843419Patcher::init() {
// $d.0 $d.1 $x.1. // $d.0 $d.1 $x.1.
for (auto &kv : sectionMap) { for (auto &kv : sectionMap) {
std::vector<const Defined *> &mapSyms = kv.second; std::vector<const Defined *> &mapSyms = kv.second;
if (mapSyms.size() <= 1)
continue;
llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) { llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) {
return a->value < b->value; return a->value < b->value;
}); });
mapSyms.erase( mapSyms.erase(
std::unique(mapSyms.begin(), mapSyms.end(), std::unique(mapSyms.begin(), mapSyms.end(),
[=](const Defined *a, const Defined *b) { [=](const Defined *a, const Defined *b) {
return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) || return isCodeMapSymbol(a) == isCodeMapSymbol(b);
(isDataMapSymbol(a) && isDataMapSymbol(b));
}), }),
mapSyms.end()); mapSyms.end());
// Always start with a Code Mapping Symbol.
if (!mapSyms.empty() && !isCodeMapSymbol(mapSyms.front()))
mapSyms.erase(mapSyms.begin());
} }
initialized = true; initialized = true;
} }
@ -511,19 +508,16 @@ void AArch64Err843419Patcher::insertPatches(
(*patchIt)->outSecOff = isecLimit; (*patchIt)->outSecOff = isecLimit;
} }
// merge all patch sections. We use the outSecOff assigned above to // Merge all patch sections. We use the outSecOff assigned above to
// determine the insertion point. This is ok as we only merge into an // determine the insertion point. This is ok as we only merge into an
// InputSectionDescription once per pass, and at the end of the pass // InputSectionDescription once per pass, and at the end of the pass
// assignAddresses() will recalculate all the outSecOff values. // assignAddresses() will recalculate all the outSecOff values.
std::vector<InputSection *> tmp; std::vector<InputSection *> tmp;
tmp.reserve(isd.sections.size() + patches.size()); tmp.reserve(isd.sections.size() + patches.size());
auto mergeCmp = [](const InputSection *a, const InputSection *b) { auto mergeCmp = [](const InputSection *a, const InputSection *b) {
if (a->outSecOff < b->outSecOff) if (a->outSecOff != b->outSecOff)
return true; return a->outSecOff < b->outSecOff;
if (a->outSecOff == b->outSecOff && isa<Patch843419Section>(a) && return isa<Patch843419Section>(a) && !isa<Patch843419Section>(b);
!isa<Patch843419Section>(b))
return true;
return false;
}; };
std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(), std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
patches.end(), std::back_inserter(tmp), mergeCmp); patches.end(), std::back_inserter(tmp), mergeCmp);
@ -532,7 +526,7 @@ void AArch64Err843419Patcher::insertPatches(
// Given an erratum sequence that starts at address adrpAddr, with an // Given an erratum sequence that starts at address adrpAddr, with an
// instruction that we need to patch at patcheeOffset from the start of // instruction that we need to patch at patcheeOffset from the start of
// InputSection IS, create a Patch843419 Section and add it to the // InputSection isec, create a Patch843419 Section and add it to the
// Patches that we need to insert. // Patches that we need to insert.
static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset, static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
InputSection *isec, InputSection *isec,
@ -578,7 +572,7 @@ static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
// Scan all the instructions in InputSectionDescription, for each instance of // Scan all the instructions in InputSectionDescription, for each instance of
// the erratum sequence create a Patch843419Section. We return the list of // the erratum sequence create a Patch843419Section. We return the list of
// Patch843419Sections that need to be applied to ISD. // Patch843419Sections that need to be applied to the InputSectionDescription.
std::vector<Patch843419Section *> std::vector<Patch843419Section *>
AArch64Err843419Patcher::patchInputSectionDescription( AArch64Err843419Patcher::patchInputSectionDescription(
InputSectionDescription &isd) { InputSectionDescription &isd) {
@ -594,10 +588,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
// section size). // section size).
std::vector<const Defined *> &mapSyms = sectionMap[isec]; std::vector<const Defined *> &mapSyms = sectionMap[isec];
auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) { auto codeSym = mapSyms.begin();
return ms->getName().startswith("$x");
});
while (codeSym != mapSyms.end()) { while (codeSym != mapSyms.end()) {
auto dataSym = std::next(codeSym); auto dataSym = std::next(codeSym);
uint64_t off = (*codeSym)->value; uint64_t off = (*codeSym)->value;
@ -606,7 +597,8 @@ AArch64Err843419Patcher::patchInputSectionDescription(
while (off < limit) { while (off < limit) {
uint64_t startAddr = isec->getVA(off); uint64_t startAddr = isec->getVA(off);
if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit)) if (uint64_t patcheeOffset =
scanCortexA53Errata843419(isec, off, limit))
implementPatch(startAddr, patcheeOffset, isec, patches); implementPatch(startAddr, patcheeOffset, isec, patches);
} }
if (dataSym == mapSyms.end()) if (dataSym == mapSyms.end())
@ -630,7 +622,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
// Ouptut and Input Sections may have been changed. // Ouptut and Input Sections may have been changed.
// Returns false if no patches were required and no changes were made. // Returns false if no patches were required and no changes were made.
bool AArch64Err843419Patcher::createFixes() { bool AArch64Err843419Patcher::createFixes() {
if (initialized == false) if (!initialized)
init(); init();
bool addressesChanged = false; bool addressesChanged = false;
@ -649,3 +641,5 @@ bool AArch64Err843419Patcher::createFixes() {
} }
return addressesChanged; return addressesChanged;
} }
} // namespace elf
} // namespace lld

View File

@ -0,0 +1,528 @@
//===- ARMErrataFix.cpp ---------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file implements Section Patching for the purpose of working around the
// Cortex-a8 erratum 657417 "A 32bit branch instruction that spans 2 4K regions
// can result in an incorrect instruction fetch or processor deadlock." The
// erratum affects all but r1p7, r2p5, r2p6, r3p1 and r3p2 revisions of the
// Cortex-A8. A high level description of the patching technique is given in
// the opening comment of AArch64ErrataFix.cpp.
//===----------------------------------------------------------------------===//
#include "ARMErrataFix.h"
#include "Config.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "Relocations.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
namespace lld {
namespace elf {
// The documented title for Erratum 657417 is:
// "A 32bit branch instruction that spans two 4K regions can result in an
// incorrect instruction fetch or processor deadlock". Graphically using a
// 32-bit B.w instruction encoded as a pair of halfwords 0xf7fe 0xbfff
// xxxxxx000 // Memory region 1 start
// target:
// ...
// xxxxxxffe f7fe // First halfword of branch to target:
// xxxxxx000 // Memory region 2 start
// xxxxxx002 bfff // Second halfword of branch to target:
//
// The specific trigger conditions that can be detected at link time are:
// - There is a 32-bit Thumb-2 branch instruction with an address of the form
// xxxxxxFFE. The first 2 bytes of the instruction are in 4KiB region 1, the
// second 2 bytes are in region 2.
// - The branch instruction is one of BLX, BL, B.w BCC.w
// - The instruction preceding the branch is a 32-bit non-branch instruction.
// - The target of the branch is in region 1.
//
// The linker mitigation for the fix is to redirect any branch that meets the
// erratum conditions to a patch section containing a branch to the target.
//
// As adding patch sections may move branches onto region boundaries the patch
// must iterate until no more patches are added.
//
// Example, before:
// 00000FFA func: NOP.w // 32-bit Thumb function
// 00000FFE B.W func // 32-bit branch spanning 2 regions, dest in 1st.
// Example, after:
// 00000FFA func: NOP.w // 32-bit Thumb function
// 00000FFE B.w __CortexA8657417_00000FFE
// 00001002 2 - bytes padding
// 00001004 __CortexA8657417_00000FFE: B.w func
class Patch657417Section : public SyntheticSection {
public:
Patch657417Section(InputSection *p, uint64_t off, uint32_t instr, bool isARM);
void writeTo(uint8_t *buf) override;
size_t getSize() const override { return 4; }
// Get the virtual address of the branch instruction at patcheeOffset.
uint64_t getBranchAddr() const;
// The Section we are patching.
const InputSection *patchee;
// The offset of the instruction in the Patchee section we are patching.
uint64_t patcheeOffset;
// A label for the start of the Patch that we can use as a relocation target.
Symbol *patchSym;
// A decoding of the branch instruction at patcheeOffset.
uint32_t instr;
// True If the patch is to be written in ARM state, otherwise the patch will
// be written in Thumb state.
bool isARM;
};
// Return true if the half-word, when taken as the first of a pair of halfwords
// is the first half of a 32-bit instruction.
// Reference from ARM Architecure Reference Manual ARMv7-A and ARMv7-R edition
// section A6.3: 32-bit Thumb instruction encoding
// | HW1 | HW2 |
// | 1 1 1 | op1 (2) | op2 (7) | x (4) |op| x (15) |
// With op1 == 0b00, a 16-bit instruction is encoded.
//
// We test only the first halfword, looking for op != 0b00.
static bool is32bitInstruction(uint16_t hw) {
return (hw & 0xe000) == 0xe000 && (hw & 0x1800) != 0x0000;
}
// Reference from ARM Architecure Reference Manual ARMv7-A and ARMv7-R edition
// section A6.3.4 Branches and miscellaneous control.
// | HW1 | HW2 |
// | 1 1 1 | 1 0 | op (7) | x (4) | 1 | op1 (3) | op2 (4) | imm8 (8) |
// op1 == 0x0 op != x111xxx | Conditional branch (Bcc.W)
// op1 == 0x1 | Branch (B.W)
// op1 == 1x0 | Branch with Link and Exchange (BLX.w)
// op1 == 1x1 | Branch with Link (BL.W)
static bool isBcc(uint32_t instr) {
return (instr & 0xf800d000) == 0xf0008000 &&
(instr & 0x03800000) != 0x03800000;
}
static bool isB(uint32_t instr) { return (instr & 0xf800d000) == 0xf0009000; }
static bool isBLX(uint32_t instr) { return (instr & 0xf800d000) == 0xf000c000; }
static bool isBL(uint32_t instr) { return (instr & 0xf800d000) == 0xf000d000; }
static bool is32bitBranch(uint32_t instr) {
return isBcc(instr) || isB(instr) || isBL(instr) || isBLX(instr);
}
Patch657417Section::Patch657417Section(InputSection *p, uint64_t off,
uint32_t instr, bool isARM)
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
".text.patch"),
patchee(p), patcheeOffset(off), instr(instr), isARM(isARM) {
parent = p->getParent();
patchSym = addSyntheticLocal(
saver.save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC,
isARM ? 0 : 1, getSize(), *this);
addSyntheticLocal(saver.save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this);
}
uint64_t Patch657417Section::getBranchAddr() const {
return patchee->getVA(patcheeOffset);
}
// Given a branch instruction instr at sourceAddr work out its destination
// address. This is only used when the branch instruction has no relocation.
static uint64_t getThumbDestAddr(uint64_t sourceAddr, uint32_t instr) {
uint8_t buf[4];
write16le(buf, instr >> 16);
write16le(buf + 2, instr & 0x0000ffff);
int64_t offset;
if (isBcc(instr))
offset = target->getImplicitAddend(buf, R_ARM_THM_JUMP19);
else if (isB(instr))
offset = target->getImplicitAddend(buf, R_ARM_THM_JUMP24);
else
offset = target->getImplicitAddend(buf, R_ARM_THM_CALL);
return sourceAddr + offset + 4;
}
void Patch657417Section::writeTo(uint8_t *buf) {
// The base instruction of the patch is always a 32-bit unconditional branch.
if (isARM)
write32le(buf, 0xea000000);
else
write32le(buf, 0x9000f000);
// If we have a relocation then apply it. For a SyntheticSection buf already
// has outSecOff added, but relocateAlloc also adds outSecOff so we need to
// subtract to avoid double counting.
if (!relocations.empty()) {
relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
return;
}
// If we don't have a relocation then we must calculate and write the offset
// ourselves.
// Get the destination offset from the addend in the branch instruction.
// We cannot use the instruction in the patchee section as this will have
// been altered to point to us!
uint64_t s = getThumbDestAddr(getBranchAddr(), instr);
uint64_t p = getVA(4);
target->relocateOne(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p);
}
// Given a branch instruction spanning two 4KiB regions, at offset off from the
// start of isec, return true if the destination of the branch is within the
// first of the two 4Kib regions.
static bool branchDestInFirstRegion(const InputSection *isec, uint64_t off,
uint32_t instr, const Relocation *r) {
uint64_t sourceAddr = isec->getVA(0) + off;
assert((sourceAddr & 0xfff) == 0xffe);
uint64_t destAddr = sourceAddr;
// If there is a branch relocation at the same offset we must use this to
// find the destination address as the branch could be indirected via a thunk
// or the PLT.
if (r) {
uint64_t dst = (r->expr == R_PLT_PC) ? r->sym->getPltVA() : r->sym->getVA();
// Account for Thumb PC bias, usually cancelled to 0 by addend of -4.
destAddr = dst + r->addend + 4;
} else {
// If there is no relocation, we must have an intra-section branch
// We must extract the offset from the addend manually.
destAddr = getThumbDestAddr(sourceAddr, instr);
}
return (destAddr & 0xfffff000) == (sourceAddr & 0xfffff000);
}
// Return true if a branch can reach a patch section placed after isec.
// The Bcc.w instruction has a range of 1 MiB, all others have 16 MiB.
static bool patchInRange(const InputSection *isec, uint64_t off,
uint32_t instr) {
// We need the branch at source to reach a patch section placed immediately
// after isec. As there can be more than one patch in the patch section we
// add 0x100 as contingency to account for worst case of 1 branch every 4KiB
// for a 1 MiB range.
return target->inBranchRange(
isBcc(instr) ? R_ARM_THM_JUMP19 : R_ARM_THM_JUMP24, isec->getVA(off),
isec->getVA() + isec->getSize() + 0x100);
}
struct ScanResult {
// Offset of branch within its InputSection.
uint64_t off;
// Cached decoding of the branch instruction.
uint32_t instr;
// Branch relocation at off. Will be nullptr if no relocation exists.
Relocation *rel;
};
// Detect the erratum sequence, returning the offset of the branch instruction
// and a decoding of the branch. If the erratum sequence is not found then
// return an offset of 0 for the branch. 0 is a safe value to use for no patch
// as there must be at least one 32-bit non-branch instruction before the
// branch so the minimum offset for a patch is 4.
static ScanResult scanCortexA8Errata657417(InputSection *isec, uint64_t &off,
uint64_t limit) {
uint64_t isecAddr = isec->getVA(0);
// Advance Off so that (isecAddr + off) modulo 0x1000 is at least 0xffa. We
// need to check for a 32-bit instruction immediately before a 32-bit branch
// at 0xffe modulo 0x1000.
off = alignTo(isecAddr + off, 0x1000, 0xffa) - isecAddr;
if (off >= limit || limit - off < 8) {
// Need at least 2 4-byte sized instructions to trigger erratum.
off = limit;
return {0, 0, nullptr};
}
ScanResult scanRes = {0, 0, nullptr};
const uint8_t *buf = isec->data().begin();
// ARMv7-A Thumb 32-bit instructions are encoded 2 consecutive
// little-endian halfwords.
const ulittle16_t *instBuf = reinterpret_cast<const ulittle16_t *>(buf + off);
uint16_t hw11 = *instBuf++;
uint16_t hw12 = *instBuf++;
uint16_t hw21 = *instBuf++;
uint16_t hw22 = *instBuf++;
if (is32bitInstruction(hw11) && is32bitInstruction(hw21)) {
uint32_t instr1 = (hw11 << 16) | hw12;
uint32_t instr2 = (hw21 << 16) | hw22;
if (!is32bitBranch(instr1) && is32bitBranch(instr2)) {
// Find a relocation for the branch if it exists. This will be used
// to determine the target.
uint64_t branchOff = off + 4;
auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) {
return r.offset == branchOff &&
(r.type == R_ARM_THM_JUMP19 || r.type == R_ARM_THM_JUMP24 ||
r.type == R_ARM_THM_CALL);
});
if (relIt != isec->relocations.end())
scanRes.rel = &(*relIt);
if (branchDestInFirstRegion(isec, branchOff, instr2, scanRes.rel)) {
if (patchInRange(isec, branchOff, instr2)) {
scanRes.off = branchOff;
scanRes.instr = instr2;
} else {
warn(toString(isec->file) +
": skipping cortex-a8 657417 erratum sequence, section " +
isec->name + " is too large to patch");
}
}
}
}
off += 0x1000;
return scanRes;
}
void ARMErr657417Patcher::init() {
// The Arm ABI permits a mix of ARM, Thumb and Data in the same
// InputSection. We must only scan Thumb instructions to avoid false
// matches. We use the mapping symbols in the InputObjects to identify this
// data, caching the results in sectionMap so we don't have to recalculate
// it each pass.
// The ABI Section 4.5.5 Mapping symbols; defines local symbols that describe
// half open intervals [Symbol Value, Next Symbol Value) of code and data
// within sections. If there is no next symbol then the half open interval is
// [Symbol Value, End of section). The type, code or data, is determined by
// the mapping symbol name, $a for Arm code, $t for Thumb code, $d for data.
auto isArmMapSymbol = [](const Symbol *s) {
return s->getName() == "$a" || s->getName().startswith("$a.");
};
auto isThumbMapSymbol = [](const Symbol *s) {
return s->getName() == "$t" || s->getName().startswith("$t.");
};
auto isDataMapSymbol = [](const Symbol *s) {
return s->getName() == "$d" || s->getName().startswith("$d.");
};
// Collect mapping symbols for every executable InputSection.
for (InputFile *file : objectFiles) {
auto *f = cast<ObjFile<ELF32LE>>(file);
for (Symbol *s : f->getLocalSymbols()) {
auto *def = dyn_cast<Defined>(s);
if (!def)
continue;
if (!isArmMapSymbol(def) && !isThumbMapSymbol(def) &&
!isDataMapSymbol(def))
continue;
if (auto *sec = dyn_cast_or_null<InputSection>(def->section))
if (sec->flags & SHF_EXECINSTR)
sectionMap[sec].push_back(def);
}
}
// For each InputSection make sure the mapping symbols are in sorted in
// ascending order and are in alternating Thumb, non-Thumb order.
for (auto &kv : sectionMap) {
std::vector<const Defined *> &mapSyms = kv.second;
llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) {
return a->value < b->value;
});
mapSyms.erase(std::unique(mapSyms.begin(), mapSyms.end(),
[=](const Defined *a, const Defined *b) {
return (isThumbMapSymbol(a) ==
isThumbMapSymbol(b));
}),
mapSyms.end());
// Always start with a Thumb Mapping Symbol
if (!mapSyms.empty() && !isThumbMapSymbol(mapSyms.front()))
mapSyms.erase(mapSyms.begin());
}
initialized = true;
}
void ARMErr657417Patcher::insertPatches(
InputSectionDescription &isd, std::vector<Patch657417Section *> &patches) {
uint64_t spacing = 0x100000 - 0x7500;
uint64_t isecLimit;
uint64_t prevIsecLimit = isd.sections.front()->outSecOff;
uint64_t patchUpperBound = prevIsecLimit + spacing;
uint64_t outSecAddr = isd.sections.front()->getParent()->addr;
// Set the outSecOff of patches to the place where we want to insert them.
// We use a similar strategy to initial thunk placement, using 1 MiB as the
// range of the Thumb-2 conditional branch with a contingency accounting for
// thunk generation.
auto patchIt = patches.begin();
auto patchEnd = patches.end();
for (const InputSection *isec : isd.sections) {
isecLimit = isec->outSecOff + isec->getSize();
if (isecLimit > patchUpperBound) {
for (; patchIt != patchEnd; ++patchIt) {
if ((*patchIt)->getBranchAddr() - outSecAddr >= prevIsecLimit)
break;
(*patchIt)->outSecOff = prevIsecLimit;
}
patchUpperBound = prevIsecLimit + spacing;
}
prevIsecLimit = isecLimit;
}
for (; patchIt != patchEnd; ++patchIt)
(*patchIt)->outSecOff = isecLimit;
// Merge all patch sections. We use the outSecOff assigned above to
// determine the insertion point. This is ok as we only merge into an
// InputSectionDescription once per pass, and at the end of the pass
// assignAddresses() will recalculate all the outSecOff values.
std::vector<InputSection *> tmp;
tmp.reserve(isd.sections.size() + patches.size());
auto mergeCmp = [](const InputSection *a, const InputSection *b) {
if (a->outSecOff != b->outSecOff)
return a->outSecOff < b->outSecOff;
return isa<Patch657417Section>(a) && !isa<Patch657417Section>(b);
};
std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
patches.end(), std::back_inserter(tmp), mergeCmp);
isd.sections = std::move(tmp);
}
// Given a branch instruction described by ScanRes redirect it to a patch
// section containing an unconditional branch instruction to the target.
// Ensure that this patch section is 4-byte aligned so that the branch cannot
// span two 4 KiB regions. Place the patch section so that it is always after
// isec so the branch we are patching always goes forwards.
static void implementPatch(ScanResult sr, InputSection *isec,
std::vector<Patch657417Section *> &patches) {
log("detected cortex-a8-657419 erratum sequence starting at " +
utohexstr(isec->getVA(sr.off)) + " in unpatched output.");
Patch657417Section *psec;
// We have two cases to deal with.
// Case 1. There is a relocation at patcheeOffset to a symbol. The
// unconditional branch in the patch must have a relocation so that any
// further redirection via the PLT or a Thunk happens as normal. At
// patcheeOffset we redirect the existing relocation to a Symbol defined at
// the start of the patch section.
//
// Case 2. There is no relocation at patcheeOffset. We are unlikely to have
// a symbol that we can use as a target for a relocation in the patch section.
// Luckily we know that the destination cannot be indirected via the PLT or
// a Thunk so we can just write the destination directly.
if (sr.rel) {
// Case 1. We have an existing relocation to redirect to patch and a
// Symbol target.
// Create a branch relocation for the unconditional branch in the patch.
// This can be redirected via the PLT or Thunks.
RelType patchRelType = R_ARM_THM_JUMP24;
int64_t patchRelAddend = sr.rel->addend;
bool destIsARM = false;
if (isBL(sr.instr) || isBLX(sr.instr)) {
// The final target of the branch may be ARM or Thumb, if the target
// is ARM then we write the patch in ARM state to avoid a state change
// Thunk from the patch to the target.
uint64_t dstSymAddr = (sr.rel->expr == R_PLT_PC) ? sr.rel->sym->getPltVA()
: sr.rel->sym->getVA();
destIsARM = (dstSymAddr & 1) == 0;
}
psec = make<Patch657417Section>(isec, sr.off, sr.instr, destIsARM);
if (destIsARM) {
// The patch will be in ARM state. Use an ARM relocation and account for
// the larger ARM PC-bias of 8 rather than Thumb's 4.
patchRelType = R_ARM_JUMP24;
patchRelAddend -= 4;
}
psec->relocations.push_back(
Relocation{sr.rel->expr, patchRelType, 0, patchRelAddend, sr.rel->sym});
// Redirect the existing branch relocation to the patch.
sr.rel->expr = R_PC;
sr.rel->addend = -4;
sr.rel->sym = psec->patchSym;
} else {
// Case 2. We do not have a relocation to the patch. Add a relocation of the
// appropriate type to the patch at patcheeOffset.
// The destination is ARM if we have a BLX.
psec = make<Patch657417Section>(isec, sr.off, sr.instr, isBLX(sr.instr));
RelType type;
if (isBcc(sr.instr))
type = R_ARM_THM_JUMP19;
else if (isB(sr.instr))
type = R_ARM_THM_JUMP24;
else
type = R_ARM_THM_CALL;
isec->relocations.push_back(
Relocation{R_PC, type, sr.off, -4, psec->patchSym});
}
patches.push_back(psec);
}
// Scan all the instructions in InputSectionDescription, for each instance of
// the erratum sequence create a Patch657417Section. We return the list of
// Patch657417Sections that need to be applied to the InputSectionDescription.
std::vector<Patch657417Section *>
ARMErr657417Patcher::patchInputSectionDescription(
InputSectionDescription &isd) {
std::vector<Patch657417Section *> patches;
for (InputSection *isec : isd.sections) {
// LLD doesn't use the erratum sequence in SyntheticSections.
if (isa<SyntheticSection>(isec))
continue;
// Use sectionMap to make sure we only scan Thumb code and not Arm or inline
// data. We have already sorted mapSyms in ascending order and removed
// consecutive mapping symbols of the same type. Our range of executable
// instructions to scan is therefore [thumbSym->value, nonThumbSym->value)
// or [thumbSym->value, section size).
std::vector<const Defined *> &mapSyms = sectionMap[isec];
auto thumbSym = mapSyms.begin();
while (thumbSym != mapSyms.end()) {
auto nonThumbSym = std::next(thumbSym);
uint64_t off = (*thumbSym)->value;
uint64_t limit = (nonThumbSym == mapSyms.end()) ? isec->data().size()
: (*nonThumbSym)->value;
while (off < limit) {
ScanResult sr = scanCortexA8Errata657417(isec, off, limit);
if (sr.off)
implementPatch(sr, isec, patches);
}
if (nonThumbSym == mapSyms.end())
break;
thumbSym = std::next(nonThumbSym);
}
}
return patches;
}
bool ARMErr657417Patcher::createFixes() {
if (!initialized)
init();
bool addressesChanged = false;
for (OutputSection *os : outputSections) {
if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR))
continue;
for (BaseCommand *bc : os->sectionCommands)
if (auto *isd = dyn_cast<InputSectionDescription>(bc)) {
std::vector<Patch657417Section *> patches =
patchInputSectionDescription(*isd);
if (!patches.empty()) {
insertPatches(*isd, patches);
addressesChanged = true;
}
}
}
return addressesChanged;
}
} // namespace elf
} // namespace lld

View File

@ -0,0 +1,51 @@
//===- ARMErrataFix.h -------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLD_ELF_ARMA8ERRATAFIX_H
#define LLD_ELF_ARMA8ERRATAFIX_H
#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include <map>
#include <vector>
namespace lld {
namespace elf {
class Defined;
class InputSection;
struct InputSectionDescription;
class OutputSection;
class Patch657417Section;
class ARMErr657417Patcher {
public:
// Return true if Patches have been added to the OutputSections.
bool createFixes();
private:
std::vector<Patch657417Section *>
patchInputSectionDescription(InputSectionDescription &isd);
void insertPatches(InputSectionDescription &isd,
std::vector<Patch657417Section *> &patches);
void init();
// A cache of the mapping symbols defined by the InputSection sorted in order
// of ascending value with redundant symbols removed. These describe
// the ranges of code and data in an executable InputSection.
llvm::DenseMap<InputSection *, std::vector<const Defined *>> sectionMap;
bool initialized = false;
};
} // namespace elf
} // namespace lld
#endif

View File

@ -17,13 +17,14 @@
using namespace llvm; using namespace llvm;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
// Page(Expr) is the page address of the expression Expr, defined // Page(Expr) is the page address of the expression Expr, defined
// as (Expr & ~0xFFF). (This applies even if the machine page size // as (Expr & ~0xFFF). (This applies even if the machine page size
// supported by the platform has a different value.) // supported by the platform has a different value.)
uint64_t elf::getAArch64Page(uint64_t expr) { uint64_t getAArch64Page(uint64_t expr) {
return expr & ~static_cast<uint64_t>(0xFFF); return expr & ~static_cast<uint64_t>(0xFFF);
} }
@ -76,6 +77,26 @@ AArch64::AArch64() {
RelExpr AArch64::getRelExpr(RelType type, const Symbol &s, RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const { const uint8_t *loc) const {
switch (type) { switch (type) {
case R_AARCH64_ABS16:
case R_AARCH64_ABS32:
case R_AARCH64_ABS64:
case R_AARCH64_ADD_ABS_LO12_NC:
case R_AARCH64_LDST128_ABS_LO12_NC:
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_LDST64_ABS_LO12_NC:
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_MOVW_SABS_G0:
case R_AARCH64_MOVW_SABS_G1:
case R_AARCH64_MOVW_SABS_G2:
case R_AARCH64_MOVW_UABS_G0:
case R_AARCH64_MOVW_UABS_G0_NC:
case R_AARCH64_MOVW_UABS_G1:
case R_AARCH64_MOVW_UABS_G1_NC:
case R_AARCH64_MOVW_UABS_G2:
case R_AARCH64_MOVW_UABS_G2_NC:
case R_AARCH64_MOVW_UABS_G3:
return R_ABS;
case R_AARCH64_TLSDESC_ADR_PAGE21: case R_AARCH64_TLSDESC_ADR_PAGE21:
return R_AARCH64_TLSDESC_PAGE; return R_AARCH64_TLSDESC_PAGE;
case R_AARCH64_TLSDESC_LD64_LO12: case R_AARCH64_TLSDESC_LD64_LO12:
@ -90,6 +111,11 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC: case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
return R_TLS; return R_TLS;
case R_AARCH64_CALL26: case R_AARCH64_CALL26:
case R_AARCH64_CONDBR19: case R_AARCH64_CONDBR19:
@ -101,6 +127,13 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
case R_AARCH64_PREL64: case R_AARCH64_PREL64:
case R_AARCH64_ADR_PREL_LO21: case R_AARCH64_ADR_PREL_LO21:
case R_AARCH64_LD_PREL_LO19: case R_AARCH64_LD_PREL_LO19:
case R_AARCH64_MOVW_PREL_G0:
case R_AARCH64_MOVW_PREL_G0_NC:
case R_AARCH64_MOVW_PREL_G1:
case R_AARCH64_MOVW_PREL_G1_NC:
case R_AARCH64_MOVW_PREL_G2:
case R_AARCH64_MOVW_PREL_G2_NC:
case R_AARCH64_MOVW_PREL_G3:
return R_PC; return R_PC;
case R_AARCH64_ADR_PREL_PG_HI21: case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_ADR_PREL_PG_HI21_NC: case R_AARCH64_ADR_PREL_PG_HI21_NC:
@ -114,7 +147,9 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
case R_AARCH64_NONE: case R_AARCH64_NONE:
return R_NONE; return R_NONE;
default: default:
return R_ABS; error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
") against symbol " + toString(s));
return R_NONE;
} }
} }
@ -247,6 +282,26 @@ static void or32AArch64Imm(uint8_t *l, uint64_t imm) {
or32le(l, (imm & 0xFFF) << 10); or32le(l, (imm & 0xFFF) << 10);
} }
// Update the immediate field in an AArch64 movk, movn or movz instruction
// for a signed relocation, and update the opcode of a movn or movz instruction
// to match the sign of the operand.
static void writeSMovWImm(uint8_t *loc, uint32_t imm) {
uint32_t inst = read32le(loc);
// Opcode field is bits 30, 29, with 10 = movz, 00 = movn and 11 = movk.
if (!(inst & (1 << 29))) {
// movn or movz.
if (imm & 0x10000) {
// Change opcode to movn, which takes an inverted operand.
imm ^= 0xFFFF;
inst &= ~(1 << 30);
} else {
// Change opcode to movz.
inst |= 1 << 30;
}
}
write32le(loc, inst | ((imm & 0xFFFF) << 5));
}
void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const { void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
switch (type) { switch (type) {
case R_AARCH64_ABS16: case R_AARCH64_ABS16:
@ -326,18 +381,56 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
checkAlignment(loc, val, 16, type); checkAlignment(loc, val, 16, type);
or32AArch64Imm(loc, getBits(val, 4, 11)); or32AArch64Imm(loc, getBits(val, 4, 11));
break; break;
case R_AARCH64_MOVW_UABS_G0:
checkUInt(loc, val, 16, type);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G0_NC: case R_AARCH64_MOVW_UABS_G0_NC:
or32le(loc, (val & 0xFFFF) << 5); or32le(loc, (val & 0xFFFF) << 5);
break; break;
case R_AARCH64_MOVW_UABS_G1:
checkUInt(loc, val, 32, type);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G1_NC: case R_AARCH64_MOVW_UABS_G1_NC:
or32le(loc, (val & 0xFFFF0000) >> 11); or32le(loc, (val & 0xFFFF0000) >> 11);
break; break;
case R_AARCH64_MOVW_UABS_G2:
checkUInt(loc, val, 48, type);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G2_NC: case R_AARCH64_MOVW_UABS_G2_NC:
or32le(loc, (val & 0xFFFF00000000) >> 27); or32le(loc, (val & 0xFFFF00000000) >> 27);
break; break;
case R_AARCH64_MOVW_UABS_G3: case R_AARCH64_MOVW_UABS_G3:
or32le(loc, (val & 0xFFFF000000000000) >> 43); or32le(loc, (val & 0xFFFF000000000000) >> 43);
break; break;
case R_AARCH64_MOVW_PREL_G0:
case R_AARCH64_MOVW_SABS_G0:
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
checkInt(loc, val, 17, type);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G0_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
writeSMovWImm(loc, val);
break;
case R_AARCH64_MOVW_PREL_G1:
case R_AARCH64_MOVW_SABS_G1:
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
checkInt(loc, val, 33, type);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G1_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
writeSMovWImm(loc, val >> 16);
break;
case R_AARCH64_MOVW_PREL_G2:
case R_AARCH64_MOVW_SABS_G2:
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
checkInt(loc, val, 49, type);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G2_NC:
writeSMovWImm(loc, val >> 32);
break;
case R_AARCH64_MOVW_PREL_G3:
writeSMovWImm(loc, val >> 48);
break;
case R_AARCH64_TSTBR14: case R_AARCH64_TSTBR14:
checkInt(loc, val, 16, type); checkInt(loc, val, 16, type);
or32le(loc, (val & 0xFFFC) << 3); or32le(loc, (val & 0xFFFC) << 3);
@ -351,7 +444,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
or32AArch64Imm(loc, val); or32AArch64Imm(loc, val);
break; break;
default: default:
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); llvm_unreachable("unknown relocation");
} }
} }
@ -587,4 +680,7 @@ static TargetInfo *getTargetInfo() {
return &t; return &t;
} }
TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); } TargetInfo *getAArch64TargetInfo() { return getTargetInfo(); }
} // namespace elf
} // namespace lld

View File

@ -17,8 +17,9 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class AMDGPU final : public TargetInfo { class AMDGPU final : public TargetInfo {
@ -107,7 +108,10 @@ RelType AMDGPU::getDynRel(RelType type) const {
return R_AMDGPU_NONE; return R_AMDGPU_NONE;
} }
TargetInfo *elf::getAMDGPUTargetInfo() { TargetInfo *getAMDGPUTargetInfo() {
static AMDGPU target; static AMDGPU target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -18,8 +18,9 @@
using namespace llvm; using namespace llvm;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class ARM final : public TargetInfo { class ARM final : public TargetInfo {
@ -600,7 +601,10 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
} }
} }
TargetInfo *elf::getARMTargetInfo() { TargetInfo *getARMTargetInfo() {
static ARM target; static ARM target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -36,8 +36,9 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class AVR final : public TargetInfo { class AVR final : public TargetInfo {
@ -70,7 +71,10 @@ void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
} }
} }
TargetInfo *elf::getAVRTargetInfo() { TargetInfo *getAVRTargetInfo() {
static AVR target; static AVR target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -19,8 +19,9 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class Hexagon final : public TargetInfo { class Hexagon final : public TargetInfo {
@ -29,6 +30,7 @@ class Hexagon final : public TargetInfo {
uint32_t calcEFlags() const override; uint32_t calcEFlags() const override;
RelExpr getRelExpr(RelType type, const Symbol &s, RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override; const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override; void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
void writePltHeader(uint8_t *buf) const override; void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
@ -86,25 +88,47 @@ static uint32_t applyMask(uint32_t mask, uint32_t data) {
RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s, RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const { const uint8_t *loc) const {
switch (type) { switch (type) {
case R_HEX_NONE:
return R_NONE;
case R_HEX_6_X:
case R_HEX_8_X:
case R_HEX_9_X:
case R_HEX_10_X:
case R_HEX_11_X:
case R_HEX_12_X:
case R_HEX_16_X:
case R_HEX_32:
case R_HEX_32_6_X:
case R_HEX_HI16:
case R_HEX_LO16:
return R_ABS;
case R_HEX_B9_PCREL: case R_HEX_B9_PCREL:
case R_HEX_B9_PCREL_X:
case R_HEX_B13_PCREL: case R_HEX_B13_PCREL:
case R_HEX_B15_PCREL: case R_HEX_B15_PCREL:
case R_HEX_B15_PCREL_X:
case R_HEX_6_PCREL_X: case R_HEX_6_PCREL_X:
case R_HEX_32_PCREL: case R_HEX_32_PCREL:
return R_PC; return R_PC;
case R_HEX_B9_PCREL_X:
case R_HEX_B15_PCREL_X:
case R_HEX_B22_PCREL: case R_HEX_B22_PCREL:
case R_HEX_PLT_B22_PCREL: case R_HEX_PLT_B22_PCREL:
case R_HEX_B22_PCREL_X: case R_HEX_B22_PCREL_X:
case R_HEX_B32_PCREL_X: case R_HEX_B32_PCREL_X:
return R_PLT_PC; return R_PLT_PC;
case R_HEX_GOTREL_11_X:
case R_HEX_GOTREL_16_X:
case R_HEX_GOTREL_32_6_X:
case R_HEX_GOTREL_HI16:
case R_HEX_GOTREL_LO16:
return R_GOTPLTREL;
case R_HEX_GOT_11_X: case R_HEX_GOT_11_X:
case R_HEX_GOT_16_X: case R_HEX_GOT_16_X:
case R_HEX_GOT_32_6_X: case R_HEX_GOT_32_6_X:
return R_HEXAGON_GOT; return R_GOTPLT;
default: default:
return R_ABS; error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
") against symbol " + toString(s));
return R_NONE;
} }
} }
@ -197,6 +221,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break; break;
case R_HEX_11_X: case R_HEX_11_X:
case R_HEX_GOT_11_X: case R_HEX_GOT_11_X:
case R_HEX_GOTREL_11_X:
or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f)); or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f));
break; break;
case R_HEX_12_X: case R_HEX_12_X:
@ -204,6 +229,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break; break;
case R_HEX_16_X: // These relocs only have 6 effective bits. case R_HEX_16_X: // These relocs only have 6 effective bits.
case R_HEX_GOT_16_X: case R_HEX_GOT_16_X:
case R_HEX_GOTREL_16_X:
or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f)); or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f));
break; break;
case R_HEX_32: case R_HEX_32:
@ -212,18 +238,22 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break; break;
case R_HEX_32_6_X: case R_HEX_32_6_X:
case R_HEX_GOT_32_6_X: case R_HEX_GOT_32_6_X:
case R_HEX_GOTREL_32_6_X:
or32le(loc, applyMask(0x0fff3fff, val >> 6)); or32le(loc, applyMask(0x0fff3fff, val >> 6));
break; break;
case R_HEX_B9_PCREL: case R_HEX_B9_PCREL:
checkInt(loc, val, 11, type);
or32le(loc, applyMask(0x003000fe, val >> 2)); or32le(loc, applyMask(0x003000fe, val >> 2));
break; break;
case R_HEX_B9_PCREL_X: case R_HEX_B9_PCREL_X:
or32le(loc, applyMask(0x003000fe, val & 0x3f)); or32le(loc, applyMask(0x003000fe, val & 0x3f));
break; break;
case R_HEX_B13_PCREL: case R_HEX_B13_PCREL:
checkInt(loc, val, 15, type);
or32le(loc, applyMask(0x00202ffe, val >> 2)); or32le(loc, applyMask(0x00202ffe, val >> 2));
break; break;
case R_HEX_B15_PCREL: case R_HEX_B15_PCREL:
checkInt(loc, val, 17, type);
or32le(loc, applyMask(0x00df20fe, val >> 2)); or32le(loc, applyMask(0x00df20fe, val >> 2));
break; break;
case R_HEX_B15_PCREL_X: case R_HEX_B15_PCREL_X:
@ -231,6 +261,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
break; break;
case R_HEX_B22_PCREL: case R_HEX_B22_PCREL:
case R_HEX_PLT_B22_PCREL: case R_HEX_PLT_B22_PCREL:
checkInt(loc, val, 22, type);
or32le(loc, applyMask(0x1ff3ffe, val >> 2)); or32le(loc, applyMask(0x1ff3ffe, val >> 2));
break; break;
case R_HEX_B22_PCREL_X: case R_HEX_B22_PCREL_X:
@ -239,15 +270,16 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_HEX_B32_PCREL_X: case R_HEX_B32_PCREL_X:
or32le(loc, applyMask(0x0fff3fff, val >> 6)); or32le(loc, applyMask(0x0fff3fff, val >> 6));
break; break;
case R_HEX_GOTREL_HI16:
case R_HEX_HI16: case R_HEX_HI16:
or32le(loc, applyMask(0x00c03fff, val >> 16)); or32le(loc, applyMask(0x00c03fff, val >> 16));
break; break;
case R_HEX_GOTREL_LO16:
case R_HEX_LO16: case R_HEX_LO16:
or32le(loc, applyMask(0x00c03fff, val)); or32le(loc, applyMask(0x00c03fff, val));
break; break;
default: default:
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type)); llvm_unreachable("unknown relocation");
break;
} }
} }
@ -285,7 +317,16 @@ void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr); relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
} }
TargetInfo *elf::getHexagonTargetInfo() { RelType Hexagon::getDynRel(RelType type) const {
if (type == R_HEX_32)
return type;
return R_HEX_NONE;
}
TargetInfo *getHexagonTargetInfo() {
static Hexagon target; static Hexagon target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -26,8 +26,9 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class MSP430 final : public TargetInfo { class MSP430 final : public TargetInfo {
@ -87,7 +88,10 @@ void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
} }
} }
TargetInfo *elf::getMSP430TargetInfo() { TargetInfo *getMSP430TargetInfo() {
static MSP430 target; static MSP430 target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -14,15 +14,13 @@
#include "Thunks.h" #include "Thunks.h"
#include "lld/Common/ErrorHandler.h" #include "lld/Common/ErrorHandler.h"
#include "llvm/Object/ELF.h" #include "llvm/Object/ELF.h"
#include "llvm/Support/Endian.h"
using namespace llvm; using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
namespace lld {
namespace elf {
namespace { namespace {
template <class ELFT> class MIPS final : public TargetInfo { template <class ELFT> class MIPS final : public TargetInfo {
public: public:
@ -85,8 +83,14 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
switch (type) { switch (type) {
case R_MIPS_JALR: case R_MIPS_JALR:
// If the target symbol is not preemptible and is not microMIPS,
// it might be possible to replace jalr/jr instruction by bal/b.
// It depends on the target symbol's offset.
if (!s.isPreemptible && !(s.getVA() & 0x1))
return R_PC;
return R_NONE;
case R_MICROMIPS_JALR: case R_MICROMIPS_JALR:
return R_HINT; return R_NONE;
case R_MIPS_GPREL16: case R_MIPS_GPREL16:
case R_MIPS_GPREL32: case R_MIPS_GPREL32:
case R_MICROMIPS_GPREL16: case R_MICROMIPS_GPREL16:
@ -120,15 +124,16 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_DTPREL64: case R_MIPS_TLS_DTPREL64:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_DTPREL_LO16:
return R_ABS;
case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16: case R_MIPS_TLS_TPREL_LO16:
case R_MIPS_TLS_TPREL32: case R_MIPS_TLS_TPREL32:
case R_MIPS_TLS_TPREL64: case R_MIPS_TLS_TPREL64:
case R_MICROMIPS_TLS_DTPREL_HI16:
case R_MICROMIPS_TLS_DTPREL_LO16:
case R_MICROMIPS_TLS_TPREL_HI16: case R_MICROMIPS_TLS_TPREL_HI16:
case R_MICROMIPS_TLS_TPREL_LO16: case R_MICROMIPS_TLS_TPREL_LO16:
return R_ABS; return R_TLS;
case R_MIPS_PC32: case R_MIPS_PC32:
case R_MIPS_PC16: case R_MIPS_PC16:
case R_MIPS_PC19_S2: case R_MIPS_PC19_S2:
@ -192,7 +197,7 @@ void MIPS<ELFT>::writeGotPlt(uint8_t *buf, const Symbol &) const {
uint64_t va = in.plt->getVA(); uint64_t va = in.plt->getVA();
if (isMicroMips()) if (isMicroMips())
va |= 1; va |= 1;
write32<ELFT::TargetEndianness>(buf, va); write32(buf, va);
} }
template <endianness E> static uint32_t readShuffle(const uint8_t *loc) { template <endianness E> static uint32_t readShuffle(const uint8_t *loc) {
@ -202,19 +207,18 @@ template <endianness E> static uint32_t readShuffle(const uint8_t *loc) {
// as early as possible. To do so, little-endian binaries keep 16-bit // as early as possible. To do so, little-endian binaries keep 16-bit
// words in a big-endian order. That is why we have to swap these // words in a big-endian order. That is why we have to swap these
// words to get a correct value. // words to get a correct value.
uint32_t v = read32<E>(loc); uint32_t v = read32(loc);
if (E == support::little) if (E == support::little)
return (v << 16) | (v >> 16); return (v << 16) | (v >> 16);
return v; return v;
} }
template <endianness E>
static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize, static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
uint8_t shift) { uint8_t shift) {
uint32_t instr = read32<E>(loc); uint32_t instr = read32(loc);
uint32_t mask = 0xffffffff >> (32 - bitsSize); uint32_t mask = 0xffffffff >> (32 - bitsSize);
uint32_t data = (instr & ~mask) | ((v >> shift) & mask); uint32_t data = (instr & ~mask) | ((v >> shift) & mask);
write32<E>(loc, data); write32(loc, data);
} }
template <endianness E> template <endianness E>
@ -225,7 +229,7 @@ static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
if (E == support::little) if (E == support::little)
std::swap(words[0], words[1]); std::swap(words[0], words[1]);
writeValue<E>(loc, v, bitsSize, shift); writeValue(loc, v, bitsSize, shift);
if (E == support::little) if (E == support::little)
std::swap(words[0], words[1]); std::swap(words[0], words[1]);
@ -234,94 +238,92 @@ static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
template <endianness E> template <endianness E>
static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize, static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize,
uint8_t shift) { uint8_t shift) {
uint16_t instr = read16<E>(loc); uint16_t instr = read16(loc);
uint16_t mask = 0xffff >> (16 - bitsSize); uint16_t mask = 0xffff >> (16 - bitsSize);
uint16_t data = (instr & ~mask) | ((v >> shift) & mask); uint16_t data = (instr & ~mask) | ((v >> shift) & mask);
write16<E>(loc, data); write16(loc, data);
} }
template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const { template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const {
const endianness e = ELFT::TargetEndianness;
if (isMicroMips()) { if (isMicroMips()) {
uint64_t gotPlt = in.gotPlt->getVA(); uint64_t gotPlt = in.gotPlt->getVA();
uint64_t plt = in.plt->getVA(); uint64_t plt = in.plt->getVA();
// Overwrite trap instructions written by Writer::writeTrapInstr. // Overwrite trap instructions written by Writer::writeTrapInstr.
memset(buf, 0, pltHeaderSize); memset(buf, 0, pltHeaderSize);
write16<e>(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . write16(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
write16<e>(buf + 4, 0xff23); // lw $25, 0($3) write16(buf + 4, 0xff23); // lw $25, 0($3)
write16<e>(buf + 8, 0x0535); // subu16 $2, $2, $3 write16(buf + 8, 0x0535); // subu16 $2, $2, $3
write16<e>(buf + 10, 0x2525); // srl16 $2, $2, 2 write16(buf + 10, 0x2525); // srl16 $2, $2, 2
write16<e>(buf + 12, 0x3302); // addiu $24, $2, -2 write16(buf + 12, 0x3302); // addiu $24, $2, -2
write16<e>(buf + 14, 0xfffe); write16(buf + 14, 0xfffe);
write16<e>(buf + 16, 0x0dff); // move $15, $31 write16(buf + 16, 0x0dff); // move $15, $31
if (isMipsR6()) { if (isMipsR6()) {
write16<e>(buf + 18, 0x0f83); // move $28, $3 write16(buf + 18, 0x0f83); // move $28, $3
write16<e>(buf + 20, 0x472b); // jalrc $25 write16(buf + 20, 0x472b); // jalrc $25
write16<e>(buf + 22, 0x0c00); // nop write16(buf + 22, 0x0c00); // nop
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt); relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
} else { } else {
write16<e>(buf + 18, 0x45f9); // jalrc $25 write16(buf + 18, 0x45f9); // jalrc $25
write16<e>(buf + 20, 0x0f83); // move $28, $3 write16(buf + 20, 0x0f83); // move $28, $3
write16<e>(buf + 22, 0x0c00); // nop write16(buf + 22, 0x0c00); // nop
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt); relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
} }
return; return;
} }
if (config->mipsN32Abi) { if (config->mipsN32Abi) {
write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
write32<e>(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) write32(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14 write32(buf + 12, 0x030ec023); // subu $24, $24, $14
write32<e>(buf + 16, 0x03e07825); // move $15, $31 write32(buf + 16, 0x03e07825); // move $15, $31
write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2 write32(buf + 20, 0x0018c082); // srl $24, $24, 2
} else if (ELFT::Is64Bits) { } else if (ELFT::Is64Bits) {
write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
write32<e>(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) write32(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14 write32(buf + 12, 0x030ec023); // subu $24, $24, $14
write32<e>(buf + 16, 0x03e07825); // move $15, $31 write32(buf + 16, 0x03e07825); // move $15, $31
write32<e>(buf + 20, 0x0018c0c2); // srl $24, $24, 3 write32(buf + 20, 0x0018c0c2); // srl $24, $24, 3
} else { } else {
write32<e>(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) write32(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
write32<e>(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) write32(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
write32<e>(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) write32(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
write32<e>(buf + 12, 0x031cc023); // subu $24, $24, $28 write32(buf + 12, 0x031cc023); // subu $24, $24, $28
write32<e>(buf + 16, 0x03e07825); // move $15, $31 write32(buf + 16, 0x03e07825); // move $15, $31
write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2 write32(buf + 20, 0x0018c082); // srl $24, $24, 2
} }
uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809; uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809;
write32<e>(buf + 24, jalrInst); // jalr.hb $25 or jalr $25 write32(buf + 24, jalrInst); // jalr.hb $25 or jalr $25
write32<e>(buf + 28, 0x2718fffe); // subu $24, $24, 2 write32(buf + 28, 0x2718fffe); // subu $24, $24, 2
uint64_t gotPlt = in.gotPlt->getVA(); uint64_t gotPlt = in.gotPlt->getVA();
writeValue<e>(buf, gotPlt + 0x8000, 16, 16); writeValue(buf, gotPlt + 0x8000, 16, 16);
writeValue<e>(buf + 4, gotPlt, 16, 0); writeValue(buf + 4, gotPlt, 16, 0);
writeValue<e>(buf + 8, gotPlt, 16, 0); writeValue(buf + 8, gotPlt, 16, 0);
} }
template <class ELFT> template <class ELFT>
void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
uint64_t pltEntryAddr, int32_t index, uint64_t pltEntryAddr, int32_t index,
unsigned relOff) const { unsigned relOff) const {
const endianness e = ELFT::TargetEndianness;
if (isMicroMips()) { if (isMicroMips()) {
// Overwrite trap instructions written by Writer::writeTrapInstr. // Overwrite trap instructions written by Writer::writeTrapInstr.
memset(buf, 0, pltEntrySize); memset(buf, 0, pltEntrySize);
if (isMipsR6()) { if (isMipsR6()) {
write16<e>(buf, 0x7840); // addiupc $2, (GOTPLT) - . write16(buf, 0x7840); // addiupc $2, (GOTPLT) - .
write16<e>(buf + 4, 0xff22); // lw $25, 0($2) write16(buf + 4, 0xff22); // lw $25, 0($2)
write16<e>(buf + 8, 0x0f02); // move $24, $2 write16(buf + 8, 0x0f02); // move $24, $2
write16<e>(buf + 10, 0x4723); // jrc $25 / jr16 $25 write16(buf + 10, 0x4723); // jrc $25 / jr16 $25
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr); relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
} else { } else {
write16<e>(buf, 0x7900); // addiupc $2, (GOTPLT) - . write16(buf, 0x7900); // addiupc $2, (GOTPLT) - .
write16<e>(buf + 4, 0xff22); // lw $25, 0($2) write16(buf + 4, 0xff22); // lw $25, 0($2)
write16<e>(buf + 8, 0x4599); // jrc $25 / jr16 $25 write16(buf + 8, 0x4599); // jrc $25 / jr16 $25
write16<e>(buf + 10, 0x0f02); // move $24, $2 write16(buf + 10, 0x0f02); // move $24, $2
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr); relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
} }
return; return;
@ -332,13 +334,13 @@ void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
: (config->zHazardplt ? 0x03200408 : 0x03200008); : (config->zHazardplt ? 0x03200408 : 0x03200008);
uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000; uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000;
write32<e>(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) write32(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
write32<e>(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15) write32(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15)
write32<e>(buf + 8, jrInst); // jr $25 / jr.hb $25 write32(buf + 8, jrInst); // jr $25 / jr.hb $25
write32<e>(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry) write32(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry)
writeValue<e>(buf, gotPltEntryAddr + 0x8000, 16, 16); writeValue(buf, gotPltEntryAddr + 0x8000, 16, 16);
writeValue<e>(buf + 4, gotPltEntryAddr, 16, 0); writeValue(buf + 4, gotPltEntryAddr, 16, 0);
writeValue<e>(buf + 12, gotPltEntryAddr, 16, 0); writeValue(buf + 12, gotPltEntryAddr, 16, 0);
} }
template <class ELFT> template <class ELFT>
@ -372,16 +374,16 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
case R_MIPS_GPREL32: case R_MIPS_GPREL32:
case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_TPREL32: case R_MIPS_TLS_TPREL32:
return SignExtend64<32>(read32<e>(buf)); return SignExtend64<32>(read32(buf));
case R_MIPS_26: case R_MIPS_26:
// FIXME (simon): If the relocation target symbol is not a PLT entry // FIXME (simon): If the relocation target symbol is not a PLT entry
// we should use another expression for calculation: // we should use another expression for calculation:
// ((A << 2) | (P & 0xf0000000)) >> 2 // ((A << 2) | (P & 0xf0000000)) >> 2
return SignExtend64<28>(read32<e>(buf) << 2); return SignExtend64<28>(read32(buf) << 2);
case R_MIPS_GOT16: case R_MIPS_GOT16:
case R_MIPS_HI16: case R_MIPS_HI16:
case R_MIPS_PCHI16: case R_MIPS_PCHI16:
return SignExtend64<16>(read32<e>(buf)) << 16; return SignExtend64<16>(read32(buf)) << 16;
case R_MIPS_GPREL16: case R_MIPS_GPREL16:
case R_MIPS_LO16: case R_MIPS_LO16:
case R_MIPS_PCLO16: case R_MIPS_PCLO16:
@ -389,7 +391,7 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16: case R_MIPS_TLS_TPREL_LO16:
return SignExtend64<16>(read32<e>(buf)); return SignExtend64<16>(read32(buf));
case R_MICROMIPS_GOT16: case R_MICROMIPS_GOT16:
case R_MICROMIPS_HI16: case R_MICROMIPS_HI16:
return SignExtend64<16>(readShuffle<e>(buf)) << 16; return SignExtend64<16>(readShuffle<e>(buf)) << 16;
@ -403,21 +405,21 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
case R_MICROMIPS_GPREL7_S2: case R_MICROMIPS_GPREL7_S2:
return SignExtend64<9>(readShuffle<e>(buf) << 2); return SignExtend64<9>(readShuffle<e>(buf) << 2);
case R_MIPS_PC16: case R_MIPS_PC16:
return SignExtend64<18>(read32<e>(buf) << 2); return SignExtend64<18>(read32(buf) << 2);
case R_MIPS_PC19_S2: case R_MIPS_PC19_S2:
return SignExtend64<21>(read32<e>(buf) << 2); return SignExtend64<21>(read32(buf) << 2);
case R_MIPS_PC21_S2: case R_MIPS_PC21_S2:
return SignExtend64<23>(read32<e>(buf) << 2); return SignExtend64<23>(read32(buf) << 2);
case R_MIPS_PC26_S2: case R_MIPS_PC26_S2:
return SignExtend64<28>(read32<e>(buf) << 2); return SignExtend64<28>(read32(buf) << 2);
case R_MIPS_PC32: case R_MIPS_PC32:
return SignExtend64<32>(read32<e>(buf)); return SignExtend64<32>(read32(buf));
case R_MICROMIPS_26_S1: case R_MICROMIPS_26_S1:
return SignExtend64<27>(readShuffle<e>(buf) << 1); return SignExtend64<27>(readShuffle<e>(buf) << 1);
case R_MICROMIPS_PC7_S1: case R_MICROMIPS_PC7_S1:
return SignExtend64<8>(read16<e>(buf) << 1); return SignExtend64<8>(read16(buf) << 1);
case R_MICROMIPS_PC10_S1: case R_MICROMIPS_PC10_S1:
return SignExtend64<11>(read16<e>(buf) << 1); return SignExtend64<11>(read16(buf) << 1);
case R_MICROMIPS_PC16_S1: case R_MICROMIPS_PC16_S1:
return SignExtend64<17>(readShuffle<e>(buf) << 1); return SignExtend64<17>(readShuffle<e>(buf) << 1);
case R_MICROMIPS_PC18_S3: case R_MICROMIPS_PC18_S3:
@ -487,9 +489,9 @@ static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) {
switch (type) { switch (type) {
case R_MIPS_26: { case R_MIPS_26: {
uint32_t inst = read32<e>(loc) >> 26; uint32_t inst = read32(loc) >> 26;
if (inst == 0x3 || inst == 0x1d) { // JAL or JALX if (inst == 0x3 || inst == 0x1d) { // JAL or JALX
writeValue<e>(loc, 0x1d << 26, 32, 0); writeValue(loc, 0x1d << 26, 32, 0);
return val; return val;
} }
break; break;
@ -538,11 +540,6 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
type == R_MICROMIPS_TLS_DTPREL_HI16 || type == R_MICROMIPS_TLS_DTPREL_HI16 ||
type == R_MICROMIPS_TLS_DTPREL_LO16) { type == R_MICROMIPS_TLS_DTPREL_LO16) {
val -= 0x8000; val -= 0x8000;
} else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 ||
type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 ||
type == R_MICROMIPS_TLS_TPREL_HI16 ||
type == R_MICROMIPS_TLS_TPREL_LO16) {
val -= 0x7000;
} }
switch (type) { switch (type) {
@ -550,25 +547,25 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_MIPS_GPREL32: case R_MIPS_GPREL32:
case R_MIPS_TLS_DTPREL32: case R_MIPS_TLS_DTPREL32:
case R_MIPS_TLS_TPREL32: case R_MIPS_TLS_TPREL32:
write32<e>(loc, val); write32(loc, val);
break; break;
case R_MIPS_64: case R_MIPS_64:
case R_MIPS_TLS_DTPREL64: case R_MIPS_TLS_DTPREL64:
case R_MIPS_TLS_TPREL64: case R_MIPS_TLS_TPREL64:
write64<e>(loc, val); write64(loc, val);
break; break;
case R_MIPS_26: case R_MIPS_26:
writeValue<e>(loc, val, 26, 2); writeValue(loc, val, 26, 2);
break; break;
case R_MIPS_GOT16: case R_MIPS_GOT16:
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode // The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
// is updated addend (not a GOT index). In that case write high 16 bits // is updated addend (not a GOT index). In that case write high 16 bits
// to store a correct addend value. // to store a correct addend value.
if (config->relocatable) { if (config->relocatable) {
writeValue<e>(loc, val + 0x8000, 16, 16); writeValue(loc, val + 0x8000, 16, 16);
} else { } else {
checkInt(loc, val, 16, type); checkInt(loc, val, 16, type);
writeValue<e>(loc, val, 16, 0); writeValue(loc, val, 16, 0);
} }
break; break;
case R_MICROMIPS_GOT16: case R_MICROMIPS_GOT16:
@ -595,7 +592,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_MIPS_PCLO16: case R_MIPS_PCLO16:
case R_MIPS_TLS_DTPREL_LO16: case R_MIPS_TLS_DTPREL_LO16:
case R_MIPS_TLS_TPREL_LO16: case R_MIPS_TLS_TPREL_LO16:
writeValue<e>(loc, val, 16, 0); writeValue(loc, val, 16, 0);
break; break;
case R_MICROMIPS_GPREL16: case R_MICROMIPS_GPREL16:
case R_MICROMIPS_TLS_GD: case R_MICROMIPS_TLS_GD:
@ -621,7 +618,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
case R_MIPS_PCHI16: case R_MIPS_PCHI16:
case R_MIPS_TLS_DTPREL_HI16: case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_TPREL_HI16: case R_MIPS_TLS_TPREL_HI16:
writeValue<e>(loc, val + 0x8000, 16, 16); writeValue(loc, val + 0x8000, 16, 16);
break; break;
case R_MICROMIPS_CALL_HI16: case R_MICROMIPS_CALL_HI16:
case R_MICROMIPS_GOT_HI16: case R_MICROMIPS_GOT_HI16:
@ -631,37 +628,51 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
writeShuffleValue<e>(loc, val + 0x8000, 16, 16); writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
break; break;
case R_MIPS_HIGHER: case R_MIPS_HIGHER:
writeValue<e>(loc, val + 0x80008000, 16, 32); writeValue(loc, val + 0x80008000, 16, 32);
break; break;
case R_MIPS_HIGHEST: case R_MIPS_HIGHEST:
writeValue<e>(loc, val + 0x800080008000, 16, 48); writeValue(loc, val + 0x800080008000, 16, 48);
break; break;
case R_MIPS_JALR: case R_MIPS_JALR:
val -= 4;
// Replace jalr/jr instructions by bal/b if the target
// offset fits into the 18-bit range.
if (isInt<18>(val)) {
switch (read32(loc)) {
case 0x0320f809: // jalr $25 => bal sym
write32(loc, 0x04110000 | ((val >> 2) & 0xffff));
break;
case 0x03200008: // jr $25 => b sym
write32(loc, 0x10000000 | ((val >> 2) & 0xffff));
break;
}
}
break;
case R_MICROMIPS_JALR: case R_MICROMIPS_JALR:
// Ignore this optimization relocation for now // Ignore this optimization relocation for now
break; break;
case R_MIPS_PC16: case R_MIPS_PC16:
checkAlignment(loc, val, 4, type); checkAlignment(loc, val, 4, type);
checkInt(loc, val, 18, type); checkInt(loc, val, 18, type);
writeValue<e>(loc, val, 16, 2); writeValue(loc, val, 16, 2);
break; break;
case R_MIPS_PC19_S2: case R_MIPS_PC19_S2:
checkAlignment(loc, val, 4, type); checkAlignment(loc, val, 4, type);
checkInt(loc, val, 21, type); checkInt(loc, val, 21, type);
writeValue<e>(loc, val, 19, 2); writeValue(loc, val, 19, 2);
break; break;
case R_MIPS_PC21_S2: case R_MIPS_PC21_S2:
checkAlignment(loc, val, 4, type); checkAlignment(loc, val, 4, type);
checkInt(loc, val, 23, type); checkInt(loc, val, 23, type);
writeValue<e>(loc, val, 21, 2); writeValue(loc, val, 21, 2);
break; break;
case R_MIPS_PC26_S2: case R_MIPS_PC26_S2:
checkAlignment(loc, val, 4, type); checkAlignment(loc, val, 4, type);
checkInt(loc, val, 28, type); checkInt(loc, val, 28, type);
writeValue<e>(loc, val, 26, 2); writeValue(loc, val, 26, 2);
break; break;
case R_MIPS_PC32: case R_MIPS_PC32:
writeValue<e>(loc, val, 32, 0); writeValue(loc, val, 32, 0);
break; break;
case R_MICROMIPS_26_S1: case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC26_S1: case R_MICROMIPS_PC26_S1:
@ -707,7 +718,7 @@ template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType type) const {
} }
// Return true if the symbol is a PIC function. // Return true if the symbol is a PIC function.
template <class ELFT> bool elf::isMipsPIC(const Defined *sym) { template <class ELFT> bool isMipsPIC(const Defined *sym) {
if (!sym->isFunc()) if (!sym->isFunc())
return false; return false;
@ -725,17 +736,20 @@ template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
return file->getObj().getHeader()->e_flags & EF_MIPS_PIC; return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
} }
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() { template <class ELFT> TargetInfo *getMipsTargetInfo() {
static MIPS<ELFT> target; static MIPS<ELFT> target;
return &target; return &target;
} }
template TargetInfo *elf::getMipsTargetInfo<ELF32LE>(); template TargetInfo *getMipsTargetInfo<ELF32LE>();
template TargetInfo *elf::getMipsTargetInfo<ELF32BE>(); template TargetInfo *getMipsTargetInfo<ELF32BE>();
template TargetInfo *elf::getMipsTargetInfo<ELF64LE>(); template TargetInfo *getMipsTargetInfo<ELF64LE>();
template TargetInfo *elf::getMipsTargetInfo<ELF64BE>(); template TargetInfo *getMipsTargetInfo<ELF64BE>();
template bool elf::isMipsPIC<ELF32LE>(const Defined *); template bool isMipsPIC<ELF32LE>(const Defined *);
template bool elf::isMipsPIC<ELF32BE>(const Defined *); template bool isMipsPIC<ELF32BE>(const Defined *);
template bool elf::isMipsPIC<ELF64LE>(const Defined *); template bool isMipsPIC<ELF64LE>(const Defined *);
template bool elf::isMipsPIC<ELF64BE>(const Defined *); template bool isMipsPIC<ELF64BE>(const Defined *);
} // namespace elf
} // namespace lld

View File

@ -23,8 +23,8 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
namespace { namespace {
struct ArchTreeEdge { struct ArchTreeEdge {
@ -166,17 +166,17 @@ static ArchTreeEdge archTree[] = {
{EF_MIPS_ARCH_2, EF_MIPS_ARCH_1}, {EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
}; };
static bool isArchMatched(uint32_t New, uint32_t res) { static bool isArchMatched(uint32_t newFlags, uint32_t res) {
if (New == res) if (newFlags == res)
return true; return true;
if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res)) if (newFlags == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res))
return true; return true;
if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res)) if (newFlags == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res))
return true; return true;
for (const auto &edge : archTree) { for (const auto &edge : archTree) {
if (res == edge.child) { if (res == edge.child) {
res = edge.parent; res = edge.parent;
if (res == New) if (res == newFlags)
return true; return true;
} }
} }
@ -278,46 +278,34 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH); uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
for (const FileFlags &f : files.slice(1)) { for (const FileFlags &f : files.slice(1)) {
uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH); uint32_t newFlags = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
// Check ISA compatibility. // Check ISA compatibility.
if (isArchMatched(New, ret)) if (isArchMatched(newFlags, ret))
continue; continue;
if (!isArchMatched(ret, New)) { if (!isArchMatched(ret, newFlags)) {
error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " + error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " + getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
getFullArchName(New)); getFullArchName(newFlags));
return 0; return 0;
} }
ret = New; ret = newFlags;
} }
return ret; return ret;
} }
// If we don't have any input files, we'll have to rely on the information we template <class ELFT> uint32_t calcMipsEFlags() {
// can derive from emulation information, since this at least gets us ABI.
static uint32_t getFlagsFromEmulation() {
uint32_t ret = 0;
if (config->emulation.empty())
return 0;
if (config->ekind == ELF32BEKind || config->ekind == ELF32LEKind) {
if (config->mipsN32Abi)
ret |= EF_MIPS_ABI2;
else
ret |= EF_MIPS_ABI_O32;
}
return ret;
}
template <class ELFT> uint32_t elf::calcMipsEFlags() {
std::vector<FileFlags> v; std::vector<FileFlags> v;
for (InputFile *f : objectFiles) for (InputFile *f : objectFiles)
v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags}); v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
if (v.empty()) if (v.empty()) {
return getFlagsFromEmulation(); // If we don't have any input files, we'll have to rely on the information
// we can derive from emulation information, since this at least gets us
// ABI.
if (config->emulation.empty() || config->is64)
return 0;
return config->mipsN32Abi ? EF_MIPS_ABI2 : EF_MIPS_ABI_O32;
}
checkFlags(v); checkFlags(v);
return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v); return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v);
} }
@ -362,8 +350,7 @@ static StringRef getMipsFpAbiName(uint8_t fpAbi) {
} }
} }
uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, StringRef fileName) {
StringRef fileName) {
if (compareMipsFpAbi(newFlag, oldFlag) >= 0) if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
return newFlag; return newFlag;
if (compareMipsFpAbi(oldFlag, newFlag) < 0) if (compareMipsFpAbi(oldFlag, newFlag) < 0)
@ -379,7 +366,7 @@ template <class ELFT> static bool isN32Abi(const InputFile *f) {
return false; return false;
} }
bool elf::isMipsN32Abi(const InputFile *f) { bool isMipsN32Abi(const InputFile *f) {
switch (config->ekind) { switch (config->ekind) {
case ELF32LEKind: case ELF32LEKind:
return isN32Abi<ELF32LE>(f); return isN32Abi<ELF32LE>(f);
@ -394,14 +381,17 @@ bool elf::isMipsN32Abi(const InputFile *f) {
} }
} }
bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; } bool isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
bool elf::isMipsR6() { bool isMipsR6() {
uint32_t arch = config->eflags & EF_MIPS_ARCH; uint32_t arch = config->eflags & EF_MIPS_ARCH;
return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6; return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
} }
template uint32_t elf::calcMipsEFlags<ELF32LE>(); template uint32_t calcMipsEFlags<ELF32LE>();
template uint32_t elf::calcMipsEFlags<ELF32BE>(); template uint32_t calcMipsEFlags<ELF32BE>();
template uint32_t elf::calcMipsEFlags<ELF64LE>(); template uint32_t calcMipsEFlags<ELF64LE>();
template uint32_t elf::calcMipsEFlags<ELF64BE>(); template uint32_t calcMipsEFlags<ELF64BE>();
} // namespace elf
} // namespace lld

View File

@ -16,8 +16,9 @@
using namespace llvm; using namespace llvm;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class PPC final : public TargetInfo { class PPC final : public TargetInfo {
@ -61,7 +62,7 @@ static void writeFromHalf16(uint8_t *loc, uint32_t insn) {
write32(config->isLE ? loc : loc - 2, insn); write32(config->isLE ? loc : loc - 2, insn);
} }
void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) { void writePPC32GlinkSection(uint8_t *buf, size_t numEntries) {
// On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an
// absolute address from a specific .plt slot (usually called .got.plt on // absolute address from a specific .plt slot (usually called .got.plt on
// other targets) and jumps there. // other targets) and jumps there.
@ -435,7 +436,10 @@ void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
} }
} }
TargetInfo *elf::getPPCTargetInfo() { TargetInfo *getPPCTargetInfo() {
static PPC target; static PPC target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -16,8 +16,9 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
static uint64_t ppc64TocOffset = 0x8000; static uint64_t ppc64TocOffset = 0x8000;
static uint64_t dynamicThreadPointerOffset = 0x8000; static uint64_t dynamicThreadPointerOffset = 0x8000;
@ -59,7 +60,7 @@ enum DFormOpcd {
ADDI = 14 ADDI = 14
}; };
uint64_t elf::getPPC64TocBase() { uint64_t getPPC64TocBase() {
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
// TOC starts where the first of these sections starts. We always create a // TOC starts where the first of these sections starts. We always create a
// .got when we see a relocation that uses it, so for us the start is always // .got when we see a relocation that uses it, so for us the start is always
@ -73,7 +74,7 @@ uint64_t elf::getPPC64TocBase() {
return tocVA + ppc64TocOffset; return tocVA + ppc64TocOffset;
} }
unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) { unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
// The offset is encoded into the 3 most significant bits of the st_other // The offset is encoded into the 3 most significant bits of the st_other
// field, with some special values described in section 3.4.1 of the ABI: // field, with some special values described in section 3.4.1 of the ABI:
// 0 --> Zero offset between the GEP and LEP, and the function does NOT use // 0 --> Zero offset between the GEP and LEP, and the function does NOT use
@ -98,7 +99,7 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
return 0; return 0;
} }
bool elf::isPPC64SmallCodeModelTocReloc(RelType type) { bool isPPC64SmallCodeModelTocReloc(RelType type) {
// The only small code model relocations that access the .toc section. // The only small code model relocations that access the .toc section.
return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS; return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
} }
@ -153,8 +154,8 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) {
// ld/lwa 3, 0(3) # load the value from the address // ld/lwa 3, 0(3) # load the value from the address
// //
// Returns true if the relaxation is performed. // Returns true if the relaxation is performed.
bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
uint8_t *bufLoc) { uint8_t *bufLoc) {
assert(config->tocOptimize); assert(config->tocOptimize);
if (rel.addend < 0) if (rel.addend < 0)
return false; return false;
@ -175,6 +176,10 @@ bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
if (!d || d->isPreemptible) if (!d || d->isPreemptible)
return false; return false;
// R_PPC64_ADDR64 should have created a canonical PLT for the non-preemptable
// ifunc and changed its type to STT_FUNC.
assert(!d->isGnuIFunc());
// Two instructions can materialize a 32-bit signed offset from the toc base. // Two instructions can materialize a 32-bit signed offset from the toc base.
uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase(); uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase();
if (!isInt<32>(tocRelative)) if (!isInt<32>(tocRelative))
@ -454,7 +459,7 @@ void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
} }
} }
unsigned elf::getPPCDFormOp(unsigned secondaryOp) { unsigned getPPCDFormOp(unsigned secondaryOp) {
switch (secondaryOp) { switch (secondaryOp) {
case LBZX: case LBZX:
return LBZ; return LBZ;
@ -1089,7 +1094,10 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
return true; return true;
} }
TargetInfo *elf::getPPC64TargetInfo() { TargetInfo *getPPC64TargetInfo() {
static PPC64 target; static PPC64 target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -14,8 +14,9 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
@ -439,7 +440,10 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
} }
} }
TargetInfo *elf::getRISCVTargetInfo() { TargetInfo *getRISCVTargetInfo() {
static RISCV target; static RISCV target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -16,8 +16,9 @@
using namespace llvm; using namespace llvm;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class SPARCV9 final : public TargetInfo { class SPARCV9 final : public TargetInfo {
@ -143,7 +144,10 @@ void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr,
relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize)); relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
} }
TargetInfo *elf::getSPARCV9TargetInfo() { TargetInfo *getSPARCV9TargetInfo() {
static SPARCV9 target; static SPARCV9 target;
return &target; return &target;
} }
} // namespace elf
} // namespace lld

View File

@ -16,8 +16,9 @@
using namespace llvm; using namespace llvm;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class X86 : public TargetInfo { class X86 : public TargetInfo {
@ -539,7 +540,7 @@ void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
write32le(buf + 22, -off - 26); write32le(buf + 22, -off - 26);
} }
TargetInfo *elf::getX86TargetInfo() { TargetInfo *getX86TargetInfo() {
if (config->zRetpolineplt) { if (config->zRetpolineplt) {
if (config->isPic) { if (config->isPic) {
static RetpolinePic t; static RetpolinePic t;
@ -552,3 +553,6 @@ TargetInfo *elf::getX86TargetInfo() {
static X86 t; static X86 t;
return &t; return &t;
} }
} // namespace elf
} // namespace lld

View File

@ -18,8 +18,9 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
class X86_64 : public TargetInfo { class X86_64 : public TargetInfo {
@ -698,4 +699,7 @@ static TargetInfo *getTargetInfo() {
return &t; return &t;
} }
TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); } TargetInfo *getX86_64TargetInfo() { return getTargetInfo(); }
} // namespace elf
} // namespace lld

View File

@ -22,6 +22,7 @@ add_lld_library(lldELF
Arch/SPARCV9.cpp Arch/SPARCV9.cpp
Arch/X86.cpp Arch/X86.cpp
Arch/X86_64.cpp Arch/X86_64.cpp
ARMErrataFix.cpp
CallGraphSort.cpp CallGraphSort.cpp
DWARF.cpp DWARF.cpp
Driver.cpp Driver.cpp

View File

@ -45,9 +45,12 @@
#include "SymbolTable.h" #include "SymbolTable.h"
#include "Symbols.h" #include "Symbols.h"
#include <numeric>
using namespace llvm; using namespace llvm;
using namespace lld;
using namespace lld::elf; namespace lld {
namespace elf {
namespace { namespace {
struct Edge { struct Edge {
@ -56,7 +59,7 @@ struct Edge {
}; };
struct Cluster { struct Cluster {
Cluster(int sec, size_t s) : sections{sec}, size(s) {} Cluster(int sec, size_t s) : next(sec), prev(sec), size(s) {}
double getDensity() const { double getDensity() const {
if (size == 0) if (size == 0)
@ -64,7 +67,8 @@ struct Cluster {
return double(weight) / double(size); return double(weight) / double(size);
} }
std::vector<int> sections; int next;
int prev;
size_t size = 0; size_t size = 0;
uint64_t weight = 0; uint64_t weight = 0;
uint64_t initialWeight = 0; uint64_t initialWeight = 0;
@ -80,8 +84,6 @@ class CallGraphSort {
private: private:
std::vector<Cluster> clusters; std::vector<Cluster> clusters;
std::vector<const InputSectionBase *> sections; std::vector<const InputSectionBase *> sections;
void groupClusters();
}; };
// Maximum ammount the combined cluster density can be worse than the original // Maximum ammount the combined cluster density can be worse than the original
@ -103,7 +105,7 @@ CallGraphSort::CallGraphSort() {
DenseMap<const InputSectionBase *, int> secToCluster; DenseMap<const InputSectionBase *, int> secToCluster;
auto getOrCreateNode = [&](const InputSectionBase *isec) -> int { auto getOrCreateNode = [&](const InputSectionBase *isec) -> int {
auto res = secToCluster.insert(std::make_pair(isec, clusters.size())); auto res = secToCluster.try_emplace(isec, clusters.size());
if (res.second) { if (res.second) {
sections.push_back(isec); sections.push_back(isec);
clusters.emplace_back(clusters.size(), isec->getSize()); clusters.emplace_back(clusters.size(), isec->getSize());
@ -151,83 +153,88 @@ static bool isNewDensityBad(Cluster &a, Cluster &b) {
return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION; return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
} }
static void mergeClusters(Cluster &into, Cluster &from) { // Find the leader of V's belonged cluster (represented as an equivalence
into.sections.insert(into.sections.end(), from.sections.begin(), // class). We apply union-find path-halving technique (simple to implement) in
from.sections.end()); // the meantime as it decreases depths and the time complexity.
static int getLeader(std::vector<int> &leaders, int v) {
while (leaders[v] != v) {
leaders[v] = leaders[leaders[v]];
v = leaders[v];
}
return v;
}
static void mergeClusters(std::vector<Cluster> &cs, Cluster &into, int intoIdx,
Cluster &from, int fromIdx) {
int tail1 = into.prev, tail2 = from.prev;
into.prev = tail2;
cs[tail2].next = intoIdx;
from.prev = tail1;
cs[tail1].next = fromIdx;
into.size += from.size; into.size += from.size;
into.weight += from.weight; into.weight += from.weight;
from.sections.clear();
from.size = 0; from.size = 0;
from.weight = 0; from.weight = 0;
} }
// Group InputSections into clusters using the Call-Chain Clustering heuristic // Group InputSections into clusters using the Call-Chain Clustering heuristic
// then sort the clusters by density. // then sort the clusters by density.
void CallGraphSort::groupClusters() { DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
std::vector<int> sortedSecs(clusters.size()); std::vector<int> sorted(clusters.size());
std::vector<Cluster *> secToCluster(clusters.size()); std::vector<int> leaders(clusters.size());
for (size_t i = 0; i < clusters.size(); ++i) { std::iota(leaders.begin(), leaders.end(), 0);
sortedSecs[i] = i; std::iota(sorted.begin(), sorted.end(), 0);
secToCluster[i] = &clusters[i]; llvm::stable_sort(sorted, [&](int a, int b) {
}
llvm::stable_sort(sortedSecs, [&](int a, int b) {
return clusters[a].getDensity() > clusters[b].getDensity(); return clusters[a].getDensity() > clusters[b].getDensity();
}); });
for (int si : sortedSecs) { for (int l : sorted) {
// clusters[si] is the same as secToClusters[si] here because it has not // The cluster index is the same as the index of its leader here because
// been merged into another cluster yet. // clusters[L] has not been merged into another cluster yet.
Cluster &c = clusters[si]; Cluster &c = clusters[l];
// Don't consider merging if the edge is unlikely. // Don't consider merging if the edge is unlikely.
if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight) if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
continue; continue;
Cluster *predC = secToCluster[c.bestPred.from]; int predL = getLeader(leaders, c.bestPred.from);
if (predC == &c) if (l == predL)
continue; continue;
Cluster *predC = &clusters[predL];
if (c.size + predC->size > MAX_CLUSTER_SIZE) if (c.size + predC->size > MAX_CLUSTER_SIZE)
continue; continue;
if (isNewDensityBad(*predC, c)) if (isNewDensityBad(*predC, c))
continue; continue;
// NOTE: Consider using a disjoint-set to track section -> cluster mapping leaders[l] = predL;
// if this is ever slow. mergeClusters(clusters, *predC, predL, c, l);
for (int si : c.sections)
secToCluster[si] = predC;
mergeClusters(*predC, c);
} }
// Remove empty or dead nodes. Invalidates all cluster indices. // Sort remaining non-empty clusters by density.
llvm::erase_if(clusters, [](const Cluster &c) { sorted.clear();
return c.size == 0 || c.sections.empty(); for (int i = 0, e = (int)clusters.size(); i != e; ++i)
if (clusters[i].size > 0)
sorted.push_back(i);
llvm::stable_sort(sorted, [&](int a, int b) {
return clusters[a].getDensity() > clusters[b].getDensity();
}); });
// Sort by density.
llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) {
return a.getDensity() > b.getDensity();
});
}
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
groupClusters();
// Generate order.
DenseMap<const InputSectionBase *, int> orderMap; DenseMap<const InputSectionBase *, int> orderMap;
ssize_t curOrder = 1; int curOrder = 1;
for (int leader : sorted)
for (const Cluster &c : clusters) for (int i = leader;;) {
for (int secIndex : c.sections) orderMap[sections[i]] = curOrder++;
orderMap[sections[secIndex]] = curOrder++; i = clusters[i].next;
if (i == leader)
break;
}
if (!config->printSymbolOrder.empty()) { if (!config->printSymbolOrder.empty()) {
std::error_code ec; std::error_code ec;
raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None); raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
if (ec) { if (ec) {
error("cannot open " + config->printSymbolOrder + ": " + ec.message()); error("cannot open " + config->printSymbolOrder + ": " + ec.message());
return orderMap; return orderMap;
@ -235,15 +242,19 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
// Print the symbols ordered by C3, in the order of increasing curOrder // Print the symbols ordered by C3, in the order of increasing curOrder
// Instead of sorting all the orderMap, just repeat the loops above. // Instead of sorting all the orderMap, just repeat the loops above.
for (const Cluster &c : clusters) for (int leader : sorted)
for (int secIndex : c.sections) for (int i = leader;;) {
// Search all the symbols in the file of the section // Search all the symbols in the file of the section
// and find out a Defined symbol with name that is within the section. // and find out a Defined symbol with name that is within the section.
for (Symbol *sym: sections[secIndex]->file->getSymbols()) for (Symbol *sym : sections[i]->file->getSymbols())
if (!sym->isSection()) // Filter out section-type symbols here. if (!sym->isSection()) // Filter out section-type symbols here.
if (auto *d = dyn_cast<Defined>(sym)) if (auto *d = dyn_cast<Defined>(sym))
if (sections[secIndex] == d->section) if (sections[i] == d->section)
os << sym->getName() << "\n"; os << sym->getName() << "\n";
i = clusters[i].next;
if (i == leader)
break;
}
} }
return orderMap; return orderMap;
@ -254,6 +265,9 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
// This first builds a call graph based on the profile data then merges sections // This first builds a call graph based on the profile data then merges sections
// according to the C³ huristic. All clusters are then sorted by a density // according to the C³ huristic. All clusters are then sorted by a density
// metric to further improve locality. // metric to further improve locality.
DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() { DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder() {
return CallGraphSort().run(); return CallGraphSort().run();
} }
} // namespace elf
} // namespace lld

View File

@ -61,6 +61,9 @@ enum class Target2Policy { Abs, Rel, GotRel };
// For tracking ARM Float Argument PCS // For tracking ARM Float Argument PCS
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain }; enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
// For -z noseparate-code, -z separate-code and -z separate-loadable-segments.
enum class SeparateSegmentKind { None, Code, Loadable };
struct SymbolVersion { struct SymbolVersion {
llvm::StringRef name; llvm::StringRef name;
bool isExternCpp; bool isExternCpp;
@ -71,8 +74,8 @@ struct SymbolVersion {
// can be found in version script if it is used for link. // can be found in version script if it is used for link.
struct VersionDefinition { struct VersionDefinition {
llvm::StringRef name; llvm::StringRef name;
uint16_t id = 0; uint16_t id;
std::vector<SymbolVersion> globals; std::vector<SymbolVersion> patterns;
}; };
// This struct contains the global configuration for the linker. // This struct contains the global configuration for the linker.
@ -117,8 +120,6 @@ struct Configuration {
std::vector<llvm::StringRef> symbolOrderingFile; std::vector<llvm::StringRef> symbolOrderingFile;
std::vector<llvm::StringRef> undefined; std::vector<llvm::StringRef> undefined;
std::vector<SymbolVersion> dynamicList; std::vector<SymbolVersion> dynamicList;
std::vector<SymbolVersion> versionScriptGlobals;
std::vector<SymbolVersion> versionScriptLocals;
std::vector<uint8_t> buildIdVector; std::vector<uint8_t> buildIdVector;
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>, llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
uint64_t> uint64_t>
@ -147,6 +148,7 @@ struct Configuration {
bool executeOnly; bool executeOnly;
bool exportDynamic; bool exportDynamic;
bool fixCortexA53Errata843419; bool fixCortexA53Errata843419;
bool fixCortexA8;
bool forceBTI; bool forceBTI;
bool formatBinary = false; bool formatBinary = false;
bool requireCET; bool requireCET;
@ -222,8 +224,8 @@ struct Configuration {
Target2Policy target2; Target2Policy target2;
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default; ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
BuildIdKind buildId = BuildIdKind::None; BuildIdKind buildId = BuildIdKind::None;
SeparateSegmentKind zSeparate;
ELFKind ekind = ELFNoneKind; ELFKind ekind = ELFNoneKind;
uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
uint16_t emachine = llvm::ELF::EM_NONE; uint16_t emachine = llvm::ELF::EM_NONE;
llvm::Optional<uint64_t> imageBase; llvm::Optional<uint64_t> imageBase;
uint64_t commonPageSize; uint64_t commonPageSize;
@ -309,6 +311,12 @@ struct Configuration {
// The only instance of Configuration struct. // The only instance of Configuration struct.
extern Configuration *config; extern Configuration *config;
// The first two elements of versionDefinitions represent VER_NDX_LOCAL and
// VER_NDX_GLOBAL. This helper returns other elements.
static inline ArrayRef<VersionDefinition> namedVersionDefs() {
return llvm::makeArrayRef(config->versionDefinitions).slice(2);
}
static inline void errorOrWarn(const Twine &msg) { static inline void errorOrWarn(const Twine &msg) {
if (!config->noinhibitExec) if (!config->noinhibitExec)
error(msg); error(msg);

View File

@ -22,9 +22,9 @@
using namespace llvm; using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace lld;
using namespace lld::elf;
namespace lld {
namespace elf {
template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) { template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
for (InputSectionBase *sec : obj->getSections()) { for (InputSectionBase *sec : obj->getSections()) {
if (!sec) if (!sec)
@ -33,11 +33,12 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
if (LLDDWARFSection *m = if (LLDDWARFSection *m =
StringSwitch<LLDDWARFSection *>(sec->name) StringSwitch<LLDDWARFSection *>(sec->name)
.Case(".debug_addr", &addrSection) .Case(".debug_addr", &addrSection)
.Case(".debug_gnu_pubnames", &gnuPubNamesSection) .Case(".debug_gnu_pubnames", &gnuPubnamesSection)
.Case(".debug_gnu_pubtypes", &gnuPubTypesSection) .Case(".debug_gnu_pubtypes", &gnuPubtypesSection)
.Case(".debug_info", &infoSection) .Case(".debug_info", &infoSection)
.Case(".debug_ranges", &rangeSection) .Case(".debug_ranges", &rangesSection)
.Case(".debug_rnglists", &rngListsSection) .Case(".debug_rnglists", &rnglistsSection)
.Case(".debug_str_offsets", &strOffsetsSection)
.Case(".debug_line", &lineSection) .Case(".debug_line", &lineSection)
.Default(nullptr)) { .Default(nullptr)) {
m->Data = toStringRef(sec->data()); m->Data = toStringRef(sec->data());
@ -50,7 +51,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
else if (sec->name == ".debug_str") else if (sec->name == ".debug_str")
strSection = toStringRef(sec->data()); strSection = toStringRef(sec->data());
else if (sec->name == ".debug_line_str") else if (sec->name == ".debug_line_str")
lineStringSection = toStringRef(sec->data()); lineStrSection = toStringRef(sec->data());
} }
} }
@ -123,7 +124,10 @@ Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s,
return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>()); return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>());
} }
template class elf::LLDDwarfObj<ELF32LE>; template class LLDDwarfObj<ELF32LE>;
template class elf::LLDDwarfObj<ELF32BE>; template class LLDDwarfObj<ELF32BE>;
template class elf::LLDDwarfObj<ELF64LE>; template class LLDDwarfObj<ELF64LE>;
template class elf::LLDDwarfObj<ELF64BE>; template class LLDDwarfObj<ELF64BE>;
} // namespace elf
} // namespace lld

View File

@ -32,12 +32,16 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
f(infoSection); f(infoSection);
} }
const llvm::DWARFSection &getRangeSection() const override { const llvm::DWARFSection &getRangesSection() const override {
return rangeSection; return rangesSection;
} }
const llvm::DWARFSection &getRnglistsSection() const override { const llvm::DWARFSection &getRnglistsSection() const override {
return rngListsSection; return rnglistsSection;
}
const llvm::DWARFSection &getStrOffsetsSection() const override {
return strOffsetsSection;
} }
const llvm::DWARFSection &getLineSection() const override { const llvm::DWARFSection &getLineSection() const override {
@ -48,18 +52,18 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
return addrSection; return addrSection;
} }
const llvm::DWARFSection &getGnuPubNamesSection() const override { const llvm::DWARFSection &getGnuPubnamesSection() const override {
return gnuPubNamesSection; return gnuPubnamesSection;
} }
const llvm::DWARFSection &getGnuPubTypesSection() const override { const llvm::DWARFSection &getGnuPubtypesSection() const override {
return gnuPubTypesSection; return gnuPubtypesSection;
} }
StringRef getFileName() const override { return ""; } StringRef getFileName() const override { return ""; }
StringRef getAbbrevSection() const override { return abbrevSection; } StringRef getAbbrevSection() const override { return abbrevSection; }
StringRef getStringSection() const override { return strSection; } StringRef getStrSection() const override { return strSection; }
StringRef getLineStringSection() const override { return lineStringSection; } StringRef getLineStrSection() const override { return lineStrSection; }
bool isLittleEndian() const override { bool isLittleEndian() const override {
return ELFT::TargetEndianness == llvm::support::little; return ELFT::TargetEndianness == llvm::support::little;
@ -74,16 +78,17 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
uint64_t pos, uint64_t pos,
ArrayRef<RelTy> rels) const; ArrayRef<RelTy> rels) const;
LLDDWARFSection gnuPubNamesSection; LLDDWARFSection gnuPubnamesSection;
LLDDWARFSection gnuPubTypesSection; LLDDWARFSection gnuPubtypesSection;
LLDDWARFSection infoSection; LLDDWARFSection infoSection;
LLDDWARFSection rangeSection; LLDDWARFSection rangesSection;
LLDDWARFSection rngListsSection; LLDDWARFSection rnglistsSection;
LLDDWARFSection strOffsetsSection;
LLDDWARFSection lineSection; LLDDWARFSection lineSection;
LLDDWARFSection addrSection; LLDDWARFSection addrSection;
StringRef abbrevSection; StringRef abbrevSection;
StringRef strSection; StringRef strSection;
StringRef lineStringSection; StringRef lineStrSection;
}; };
} // namespace elf } // namespace elf

View File

@ -48,6 +48,7 @@
#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/StringSwitch.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h" #include "llvm/Support/Compression.h"
#include "llvm/Support/GlobPattern.h" #include "llvm/Support/GlobPattern.h"
@ -65,24 +66,23 @@ using namespace llvm::object;
using namespace llvm::sys; using namespace llvm::sys;
using namespace llvm::support; using namespace llvm::support;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
Configuration *elf::config; Configuration *config;
LinkerDriver *elf::driver; LinkerDriver *driver;
static void setConfigs(opt::InputArgList &args); static void setConfigs(opt::InputArgList &args);
static void readConfigs(opt::InputArgList &args); static void readConfigs(opt::InputArgList &args);
bool elf::link(ArrayRef<const char *> args, bool canExitEarly, bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &error) {
raw_ostream &error) {
errorHandler().logName = args::getFilenameWithoutExe(args[0]); errorHandler().logName = args::getFilenameWithoutExe(args[0]);
errorHandler().errorLimitExceededMsg = errorHandler().errorLimitExceededMsg =
"too many errors emitted, stopping now (use " "too many errors emitted, stopping now (use "
"-error-limit=0 to see all errors)"; "-error-limit=0 to see all errors)";
errorHandler().errorOS = &error; errorHandler().errorOS = &error;
errorHandler().exitEarly = canExitEarly; errorHandler().exitEarly = canExitEarly;
errorHandler().colorDiagnostics = error.has_colors(); enableColors(error.has_colors());
inputSections.clear(); inputSections.clear();
outputSections.clear(); outputSections.clear();
@ -299,6 +299,9 @@ static void checkOptions() {
if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64) if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64)
error("--fix-cortex-a53-843419 is only supported on AArch64 targets"); error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
if (config->fixCortexA8 && config->emachine != EM_ARM)
error("--fix-cortex-a8 is only supported on ARM targets");
if (config->tocOptimize && config->emachine != EM_PPC64) if (config->tocOptimize && config->emachine != EM_PPC64)
error("--toc-optimize is only supported on the PowerPC64 target"); error("--toc-optimize is only supported on the PowerPC64 target");
@ -314,6 +317,9 @@ static void checkOptions() {
if (!config->relocatable && !config->defineCommon) if (!config->relocatable && !config->defineCommon)
error("-no-define-common not supported in non relocatable output"); error("-no-define-common not supported in non relocatable output");
if (config->strip == StripPolicy::All && config->emitRelocs)
error("--strip-all and --emit-relocs may not be used together");
if (config->zText && config->zIfuncNoplt) if (config->zText && config->zIfuncNoplt)
error("-z text and -z ifunc-noplt may not be used together"); error("-z text and -z ifunc-noplt may not be used together");
@ -328,6 +334,8 @@ static void checkOptions() {
error("-r and --icf may not be used together"); error("-r and --icf may not be used together");
if (config->pie) if (config->pie)
error("-r and -pie may not be used together"); error("-r and -pie may not be used together");
if (config->exportDynamic)
error("-r and --export-dynamic may not be used together");
} }
if (config->executeOnly) { if (config->executeOnly) {
@ -373,17 +381,32 @@ static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2,
return Default; return Default;
} }
static SeparateSegmentKind getZSeparate(opt::InputArgList &args) {
for (auto *arg : args.filtered_reverse(OPT_z)) {
StringRef v = arg->getValue();
if (v == "noseparate-code")
return SeparateSegmentKind::None;
if (v == "separate-code")
return SeparateSegmentKind::Code;
if (v == "separate-loadable-segments")
return SeparateSegmentKind::Loadable;
}
return SeparateSegmentKind::None;
}
static bool isKnownZFlag(StringRef s) { static bool isKnownZFlag(StringRef s) {
return s == "combreloc" || s == "copyreloc" || s == "defs" || return s == "combreloc" || s == "copyreloc" || s == "defs" ||
s == "execstack" || s == "global" || s == "hazardplt" || s == "execstack" || s == "global" || s == "hazardplt" ||
s == "ifunc-noplt" || s == "initfirst" || s == "interpose" || s == "ifunc-noplt" || s == "initfirst" || s == "interpose" ||
s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" || s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
s == "separate-code" || s == "separate-loadable-segments" ||
s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" || s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
s == "nodelete" || s == "nodlopen" || s == "noexecstack" || s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" || s == "nokeep-text-section-prefix" || s == "norelro" ||
s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" || s == "noseparate-code" || s == "notext" || s == "now" ||
s == "rodynamic" || s == "text" || s == "wxneeded" || s == "origin" || s == "relro" || s == "retpolineplt" ||
s.startswith("common-page-size") || s.startswith("max-page-size=") || s == "rodynamic" || s == "text" || s == "undefs" || s == "wxneeded" ||
s.startswith("common-page-size=") || s.startswith("max-page-size=") ||
s.startswith("stack-size="); s.startswith("stack-size=");
} }
@ -513,6 +536,8 @@ static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) {
case OPT_z: case OPT_z:
if (StringRef(arg->getValue()) == "defs") if (StringRef(arg->getValue()) == "defs")
return errorOrWarn; return errorOrWarn;
if (StringRef(arg->getValue()) == "undefs")
return UnresolvedPolicy::Ignore;
continue; continue;
} }
} }
@ -747,6 +772,12 @@ static bool getCompressDebugSections(opt::InputArgList &args) {
return true; return true;
} }
static StringRef getAliasSpelling(opt::Arg *arg) {
if (const opt::Arg *alias = arg->getAlias())
return alias->getSpelling();
return arg->getSpelling();
}
static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args, static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
unsigned id) { unsigned id) {
auto *arg = args.getLastArg(id); auto *arg = args.getLastArg(id);
@ -756,7 +787,7 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
StringRef s = arg->getValue(); StringRef s = arg->getValue();
std::pair<StringRef, StringRef> ret = s.split(';'); std::pair<StringRef, StringRef> ret = s.split(';');
if (ret.second.empty()) if (ret.second.empty())
error(arg->getSpelling() + " expects 'old;new' format, but got " + s); error(getAliasSpelling(arg) + " expects 'old;new' format, but got " + s);
return ret; return ret;
} }
@ -829,6 +860,7 @@ static void readConfigs(opt::InputArgList &args) {
config->filterList = args::getStrings(args, OPT_filter); config->filterList = args::getStrings(args, OPT_filter);
config->fini = args.getLastArgValue(OPT_fini, "_fini"); config->fini = args.getLastArgValue(OPT_fini, "_fini");
config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419); config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419);
config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8);
config->forceBTI = args.hasArg(OPT_force_bti); config->forceBTI = args.hasArg(OPT_force_bti);
config->requireCET = args.hasArg(OPT_require_cet); config->requireCET = args.hasArg(OPT_require_cet);
config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
@ -847,7 +879,7 @@ static void readConfigs(opt::InputArgList &args) {
config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager); config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager);
config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes); config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
config->ltoo = args::getInteger(args, OPT_lto_O, 2); config->ltoo = args::getInteger(args, OPT_lto_O, 2);
config->ltoObjPath = args.getLastArgValue(OPT_plugin_opt_obj_path_eq); config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq);
config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1); config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile); config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
config->mapFile = args.getLastArgValue(OPT_Map); config->mapFile = args.getLastArgValue(OPT_Map);
@ -892,17 +924,15 @@ static void readConfigs(opt::InputArgList &args) {
config->thinLTOCachePolicy = CHECK( config->thinLTOCachePolicy = CHECK(
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)), parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy"); "--thinlto-cache-policy: invalid cache policy");
config->thinLTOEmitImportsFiles = config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files); config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
config->thinLTOIndexOnly = args.hasArg(OPT_plugin_opt_thinlto_index_only) || args.hasArg(OPT_thinlto_index_only_eq);
args.hasArg(OPT_plugin_opt_thinlto_index_only_eq); config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnlyArg =
args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq);
config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u); config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
config->thinLTOObjectSuffixReplace = config->thinLTOObjectSuffixReplace =
getOldNewOptions(args, OPT_plugin_opt_thinlto_object_suffix_replace_eq); getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
config->thinLTOPrefixReplace = config->thinLTOPrefixReplace =
getOldNewOptions(args, OPT_plugin_opt_thinlto_prefix_replace_eq); getOldNewOptions(args, OPT_thinlto_prefix_replace_eq);
config->trace = args.hasArg(OPT_trace); config->trace = args.hasArg(OPT_trace);
config->undefined = args::getStrings(args, OPT_undefined); config->undefined = args::getStrings(args, OPT_undefined);
config->undefinedVersion = config->undefinedVersion =
@ -935,6 +965,7 @@ static void readConfigs(opt::InputArgList &args) {
config->zRelro = getZFlag(args, "relro", "norelro", true); config->zRelro = getZFlag(args, "relro", "norelro", true);
config->zRetpolineplt = hasZOption(args, "retpolineplt"); config->zRetpolineplt = hasZOption(args, "retpolineplt");
config->zRodynamic = hasZOption(args, "rodynamic"); config->zRodynamic = hasZOption(args, "rodynamic");
config->zSeparate = getZSeparate(args);
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0); config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
config->zText = getZFlag(args, "text", "notext", true); config->zText = getZFlag(args, "text", "notext", true);
config->zWxneeded = hasZOption(args, "wxneeded"); config->zWxneeded = hasZOption(args, "wxneeded");
@ -1010,30 +1041,33 @@ static void readConfigs(opt::InputArgList &args) {
} }
} }
assert(config->versionDefinitions.empty());
config->versionDefinitions.push_back({"local", (uint16_t)VER_NDX_LOCAL, {}});
config->versionDefinitions.push_back(
{"global", (uint16_t)VER_NDX_GLOBAL, {}});
// If --retain-symbol-file is used, we'll keep only the symbols listed in // If --retain-symbol-file is used, we'll keep only the symbols listed in
// the file and discard all others. // the file and discard all others.
if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) { if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) {
config->defaultSymbolVersion = VER_NDX_LOCAL; config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(
{"*", /*isExternCpp=*/false, /*hasWildcard=*/true});
if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())) if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
for (StringRef s : args::getLines(*buffer)) for (StringRef s : args::getLines(*buffer))
config->versionScriptGlobals.push_back( config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(
{s, /*IsExternCpp*/ false, /*HasWildcard*/ false}); {s, /*isExternCpp=*/false, /*hasWildcard=*/false});
} }
bool hasExportDynamic =
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
// Parses -dynamic-list and -export-dynamic-symbol. They make some // Parses -dynamic-list and -export-dynamic-symbol. They make some
// symbols private. Note that -export-dynamic takes precedence over them // symbols private. Note that -export-dynamic takes precedence over them
// as it says all symbols should be exported. // as it says all symbols should be exported.
if (!hasExportDynamic) { if (!config->exportDynamic) {
for (auto *arg : args.filtered(OPT_dynamic_list)) for (auto *arg : args.filtered(OPT_dynamic_list))
if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue())) if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
readDynamicList(*buffer); readDynamicList(*buffer);
for (auto *arg : args.filtered(OPT_export_dynamic_symbol)) for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
config->dynamicList.push_back( config->dynamicList.push_back(
{arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false}); {arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/false});
} }
// If --export-dynamic-symbol=foo is given and symbol foo is defined in // If --export-dynamic-symbol=foo is given and symbol foo is defined in
@ -1659,12 +1693,6 @@ template <class ELFT> static uint32_t getAndFeatures() {
return ret; return ret;
} }
static const char *libcallRoutineNames[] = {
#define HANDLE_LIBCALL(code, name) name,
#include "llvm/IR/RuntimeLibcalls.def"
#undef HANDLE_LIBCALL
};
// Do actual linking. Note that when this function is called, // Do actual linking. Note that when this function is called,
// all linker scripts have already been parsed. // all linker scripts have already been parsed.
template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) { template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
@ -1755,7 +1783,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// libcall symbols will be added to the link after LTO when we add the LTO // libcall symbols will be added to the link after LTO when we add the LTO
// object file to the link. // object file to the link.
if (!bitcodeFiles.empty()) if (!bitcodeFiles.empty())
for (const char *s : libcallRoutineNames) for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
handleLibcall(s); handleLibcall(s);
// Return if there were name resolution errors. // Return if there were name resolution errors.
@ -1880,20 +1908,54 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
"feature detected"); "feature detected");
} }
// This adds a .comment section containing a version string. We have to add it // This adds a .comment section containing a version string.
// before mergeSections because the .comment section is a mergeable section.
if (!config->relocatable) if (!config->relocatable)
inputSections.push_back(createCommentSection()); inputSections.push_back(createCommentSection());
// Replace common symbols with regular symbols. // Replace common symbols with regular symbols.
replaceCommonSymbols(); replaceCommonSymbols();
// Do size optimizations: garbage collection, merging of SHF_MERGE sections // Split SHF_MERGE and .eh_frame sections into pieces in preparation for garbage collection.
// and identical code folding.
splitSections<ELFT>(); splitSections<ELFT>();
// Garbage collection and removal of shared symbols from unused shared objects.
markLive<ELFT>(); markLive<ELFT>();
demoteSharedSymbols(); demoteSharedSymbols();
mergeSections();
// Make copies of any input sections that need to be copied into each
// partition.
copySectionsIntoPartitions();
// Create synthesized sections such as .got and .plt. This is called before
// processSectionCommands() so that they can be placed by SECTIONS commands.
createSyntheticSections<ELFT>();
// Some input sections that are used for exception handling need to be moved
// into synthetic sections. Do that now so that they aren't assigned to
// output sections in the usual way.
if (!config->relocatable)
combineEhSections();
// Create output sections described by SECTIONS commands.
script->processSectionCommands();
// Linker scripts control how input sections are assigned to output sections.
// Input sections that were not handled by scripts are called "orphans", and
// they are assigned to output sections by the default rule. Process that.
script->addOrphanSections();
// Migrate InputSectionDescription::sectionBases to sections. This includes
// merging MergeInputSections into a single MergeSyntheticSection. From this
// point onwards InputSectionDescription::sections should be used instead of
// sectionBases.
for (BaseCommand *base : script->sectionCommands)
if (auto *sec = dyn_cast<OutputSection>(base))
sec->finalizeInputSections();
llvm::erase_if(inputSections,
[](InputSectionBase *s) { return isa<MergeInputSection>(s); });
// Two input sections with different output sections should not be folded.
// ICF runs after processSectionCommands() so that we know the output sections.
if (config->icf != ICFLevel::None) { if (config->icf != ICFLevel::None) {
findKeepUniqueSections<ELFT>(args); findKeepUniqueSections<ELFT>(args);
doIcf<ELFT>(); doIcf<ELFT>();
@ -1910,3 +1972,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
// Write the result to the file. // Write the result to the file.
writeResult<ELFT>(); writeResult<ELFT>();
} }
} // namespace elf
} // namespace lld

View File

@ -30,8 +30,8 @@ using namespace llvm;
using namespace llvm::sys; using namespace llvm::sys;
using namespace llvm::opt; using namespace llvm::opt;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
// Create OptTable // Create OptTable
@ -59,15 +59,15 @@ static void handleColorDiagnostics(opt::InputArgList &args) {
if (!arg) if (!arg)
return; return;
if (arg->getOption().getID() == OPT_color_diagnostics) { if (arg->getOption().getID() == OPT_color_diagnostics) {
errorHandler().colorDiagnostics = true; enableColors(true);
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) { } else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
errorHandler().colorDiagnostics = false; enableColors(false);
} else { } else {
StringRef s = arg->getValue(); StringRef s = arg->getValue();
if (s == "always") if (s == "always")
errorHandler().colorDiagnostics = true; enableColors(true);
else if (s == "never") else if (s == "never")
errorHandler().colorDiagnostics = false; enableColors(false);
else if (s != "auto") else if (s != "auto")
error("unknown option: --color-diagnostics=" + s); error("unknown option: --color-diagnostics=" + s);
} }
@ -143,7 +143,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) {
return args; return args;
} }
void elf::printHelp() { void printHelp() {
ELFOptTable().PrintHelp( ELFOptTable().PrintHelp(
outs(), (config->progName + " [options] file...").str().c_str(), "lld", outs(), (config->progName + " [options] file...").str().c_str(), "lld",
false /*ShowHidden*/, true /*ShowAllAliases*/); false /*ShowHidden*/, true /*ShowAllAliases*/);
@ -165,7 +165,7 @@ static std::string rewritePath(StringRef s) {
// Reconstructs command line arguments so that so that you can re-run // Reconstructs command line arguments so that so that you can re-run
// the same command with the same inputs. This is for --reproduce. // the same command with the same inputs. This is for --reproduce.
std::string elf::createResponseFile(const opt::InputArgList &args) { std::string createResponseFile(const opt::InputArgList &args) {
SmallString<0> data; SmallString<0> data;
raw_svector_ostream os(data); raw_svector_ostream os(data);
os << "--chroot .\n"; os << "--chroot .\n";
@ -216,7 +216,7 @@ static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
return None; return None;
} }
Optional<std::string> elf::findFromSearchPaths(StringRef path) { Optional<std::string> findFromSearchPaths(StringRef path) {
for (StringRef dir : config->searchPaths) for (StringRef dir : config->searchPaths)
if (Optional<std::string> s = findFile(dir, path)) if (Optional<std::string> s = findFile(dir, path))
return s; return s;
@ -225,7 +225,7 @@ Optional<std::string> elf::findFromSearchPaths(StringRef path) {
// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from // This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
// search paths. // search paths.
Optional<std::string> elf::searchLibraryBaseName(StringRef name) { Optional<std::string> searchLibraryBaseName(StringRef name) {
for (StringRef dir : config->searchPaths) { for (StringRef dir : config->searchPaths) {
if (!config->isStatic) if (!config->isStatic)
if (Optional<std::string> s = findFile(dir, "lib" + name + ".so")) if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
@ -237,17 +237,20 @@ Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
} }
// This is for -l<namespec>. // This is for -l<namespec>.
Optional<std::string> elf::searchLibrary(StringRef name) { Optional<std::string> searchLibrary(StringRef name) {
if (name.startswith(":")) if (name.startswith(":"))
return findFromSearchPaths(name.substr(1)); return findFromSearchPaths(name.substr(1));
return searchLibraryBaseName (name); return searchLibraryBaseName(name);
} }
// If a linker/version script doesn't exist in the current directory, we also // If a linker/version script doesn't exist in the current directory, we also
// look for the script in the '-L' search paths. This matches the behaviour of // look for the script in the '-L' search paths. This matches the behaviour of
// '-T', --version-script=, and linker script INPUT() command in ld.bfd. // '-T', --version-script=, and linker script INPUT() command in ld.bfd.
Optional<std::string> elf::searchScript(StringRef name) { Optional<std::string> searchScript(StringRef name) {
if (fs::exists(name)) if (fs::exists(name))
return name.str(); return name.str();
return findFromSearchPaths(name); return findFromSearchPaths(name);
} }
} // namespace elf
} // namespace lld

View File

@ -30,9 +30,8 @@ using namespace llvm::ELF;
using namespace llvm::dwarf; using namespace llvm::dwarf;
using namespace llvm::object; using namespace llvm::object;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
namespace { namespace {
class EhReader { class EhReader {
public: public:
@ -57,7 +56,7 @@ class EhReader {
}; };
} }
size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) { size_t readEhRecordSize(InputSectionBase *s, size_t off) {
return EhReader(s, s->data().slice(off)).readEhRecordSize(); return EhReader(s, s->data().slice(off)).readEhRecordSize();
} }
@ -149,7 +148,7 @@ void EhReader::skipAugP() {
d = d.slice(size); d = d.slice(size);
} }
uint8_t elf::getFdeEncoding(EhSectionPiece *p) { uint8_t getFdeEncoding(EhSectionPiece *p) {
return EhReader(p->sec, p->data()).getFdeEncoding(); return EhReader(p->sec, p->data()).getFdeEncoding();
} }
@ -195,3 +194,6 @@ uint8_t EhReader::getFdeEncoding() {
} }
return DW_EH_PE_absptr; return DW_EH_PE_absptr;
} }
} // namespace elf
} // namespace lld

View File

@ -74,6 +74,8 @@
#include "ICF.h" #include "ICF.h"
#include "Config.h" #include "Config.h"
#include "LinkerScript.h"
#include "OutputSections.h"
#include "SymbolTable.h" #include "SymbolTable.h"
#include "Symbols.h" #include "Symbols.h"
#include "SyntheticSections.h" #include "SyntheticSections.h"
@ -86,12 +88,12 @@
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
using namespace lld;
using namespace lld::elf;
using namespace llvm; using namespace llvm;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace llvm::object; using namespace llvm::object;
namespace lld {
namespace elf {
namespace { namespace {
template <class ELFT> class ICF { template <class ELFT> class ICF {
public: public:
@ -304,10 +306,8 @@ bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
return false; return false;
// If two sections have different output sections, we cannot merge them. // If two sections have different output sections, we cannot merge them.
// FIXME: This doesn't do the right thing in the case where there is a linker assert(a->getParent() && b->getParent());
// script. We probably need to move output section assignment before ICF to if (a->getParent() != b->getParent())
// get the correct behaviour here.
if (getOutputSectionName(a) != getOutputSectionName(b))
return false; return false;
if (a->areRelocsRela) if (a->areRelocsRela)
@ -446,10 +446,11 @@ static void print(const Twine &s) {
// The main function of ICF. // The main function of ICF.
template <class ELFT> void ICF<ELFT>::run() { template <class ELFT> void ICF<ELFT>::run() {
// Collect sections to merge. // Collect sections to merge.
for (InputSectionBase *sec : inputSections) for (InputSectionBase *sec : inputSections) {
if (auto *s = dyn_cast<InputSection>(sec)) auto *s = cast<InputSection>(sec);
if (isEligible(s)) if (isEligible(s))
sections.push_back(s); sections.push_back(s);
}
// Initially, we use hash values to partition sections. // Initially, we use hash values to partition sections.
parallelForEach(sections, [&](InputSection *s) { parallelForEach(sections, [&](InputSection *s) {
@ -499,12 +500,24 @@ template <class ELFT> void ICF<ELFT>::run() {
isec->markDead(); isec->markDead();
} }
}); });
// InputSectionDescription::sections is populated by processSectionCommands().
// ICF may fold some input sections assigned to output sections. Remove them.
for (BaseCommand *base : script->sectionCommands)
if (auto *sec = dyn_cast<OutputSection>(base))
for (BaseCommand *sub_base : sec->sectionCommands)
if (auto *isd = dyn_cast<InputSectionDescription>(sub_base))
llvm::erase_if(isd->sections,
[](InputSection *isec) { return !isec->isLive(); });
} }
// ICF entry point function. // ICF entry point function.
template <class ELFT> void elf::doIcf() { ICF<ELFT>().run(); } template <class ELFT> void doIcf() { ICF<ELFT>().run(); }
template void elf::doIcf<ELF32LE>(); template void doIcf<ELF32LE>();
template void elf::doIcf<ELF32BE>(); template void doIcf<ELF32BE>();
template void elf::doIcf<ELF64LE>(); template void doIcf<ELF64LE>();
template void elf::doIcf<ELF64BE>(); template void doIcf<ELF64BE>();
} // namespace elf
} // namespace lld

View File

@ -17,7 +17,6 @@
#include "lld/Common/Memory.h" #include "lld/Common/Memory.h"
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/Analysis.h" #include "llvm/CodeGen/Analysis.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/LTO/LTO.h" #include "llvm/LTO/LTO.h"
@ -37,18 +36,31 @@ using namespace llvm::sys;
using namespace llvm::sys::fs; using namespace llvm::sys::fs;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace lld; namespace lld {
using namespace lld::elf; // Returns "<internal>", "foo.a(bar.o)" or "baz.o".
std::string toString(const elf::InputFile *f) {
if (!f)
return "<internal>";
if (f->toStringCache.empty()) {
if (f->archiveName.empty())
f->toStringCache = f->getName();
else
f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
}
return f->toStringCache;
}
namespace elf {
bool InputFile::isInGroup; bool InputFile::isInGroup;
uint32_t InputFile::nextGroupId; uint32_t InputFile::nextGroupId;
std::vector<BinaryFile *> elf::binaryFiles; std::vector<BinaryFile *> binaryFiles;
std::vector<BitcodeFile *> elf::bitcodeFiles; std::vector<BitcodeFile *> bitcodeFiles;
std::vector<LazyObjFile *> elf::lazyObjFiles; std::vector<LazyObjFile *> lazyObjFiles;
std::vector<InputFile *> elf::objectFiles; std::vector<InputFile *> objectFiles;
std::vector<SharedFile *> elf::sharedFiles; std::vector<SharedFile *> sharedFiles;
std::unique_ptr<TarWriter> elf::tar; std::unique_ptr<TarWriter> tar;
static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) { static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
unsigned char size; unsigned char size;
@ -88,7 +100,7 @@ InputFile::InputFile(Kind k, MemoryBufferRef m)
++nextGroupId; ++nextGroupId;
} }
Optional<MemoryBufferRef> elf::readFile(StringRef path) { Optional<MemoryBufferRef> readFile(StringRef path) {
// The --chroot option changes our virtual root directory. // The --chroot option changes our virtual root directory.
// This is useful when you are dealing with files created by --reproduce. // This is useful when you are dealing with files created by --reproduce.
if (!config->chroot.empty() && path.startswith("/")) if (!config->chroot.empty() && path.startswith("/"))
@ -127,18 +139,18 @@ static bool isCompatible(InputFile *file) {
if (!config->emulation.empty()) { if (!config->emulation.empty()) {
error(toString(file) + " is incompatible with " + config->emulation); error(toString(file) + " is incompatible with " + config->emulation);
} else { return false;
InputFile *existing;
if (!objectFiles.empty())
existing = objectFiles[0];
else if (!sharedFiles.empty())
existing = sharedFiles[0];
else
existing = bitcodeFiles[0];
error(toString(file) + " is incompatible with " + toString(existing));
} }
InputFile *existing;
if (!objectFiles.empty())
existing = objectFiles[0];
else if (!sharedFiles.empty())
existing = sharedFiles[0];
else
existing = bitcodeFiles[0];
error(toString(file) + " is incompatible with " + toString(existing));
return false; return false;
} }
@ -188,7 +200,7 @@ template <class ELFT> static void doParseFile(InputFile *file) {
} }
// Add symbols in File to the symbol table. // Add symbols in File to the symbol table.
void elf::parseFile(InputFile *file) { void parseFile(InputFile *file) {
switch (config->ekind) { switch (config->ekind) {
case ELF32LEKind: case ELF32LEKind:
doParseFile<ELF32LE>(file); doParseFile<ELF32LE>(file);
@ -252,57 +264,8 @@ std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec,
} }
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() { template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this)); dwarf = make<DWARFCache>(std::make_unique<DWARFContext>(
for (std::unique_ptr<DWARFUnit> &cu : dwarf->compile_units()) { std::make_unique<LLDDwarfObj<ELFT>>(this)));
auto report = [](Error err) {
handleAllErrors(std::move(err),
[](ErrorInfoBase &info) { warn(info.message()); });
};
Expected<const DWARFDebugLine::LineTable *> expectedLT =
dwarf->getLineTableForUnit(cu.get(), report);
const DWARFDebugLine::LineTable *lt = nullptr;
if (expectedLT)
lt = *expectedLT;
else
report(expectedLT.takeError());
if (!lt)
continue;
lineTables.push_back(lt);
// Loop over variable records and insert them to variableLoc.
for (const auto &entry : cu->dies()) {
DWARFDie die(cu.get(), &entry);
// Skip all tags that are not variables.
if (die.getTag() != dwarf::DW_TAG_variable)
continue;
// Skip if a local variable because we don't need them for generating
// error messages. In general, only non-local symbols can fail to be
// linked.
if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0))
continue;
// Get the source filename index for the variable.
unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0);
if (!lt->hasFileAtIndex(file))
continue;
// Get the line number on which the variable is declared.
unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0);
// Here we want to take the variable name to add it into variableLoc.
// Variable can have regular and linkage name associated. At first, we try
// to get linkage name as it can be different, for example when we have
// two variables in different namespaces of the same object. Use common
// name otherwise, but handle the case when it also absent in case if the
// input object file lacks some debug info.
StringRef name =
dwarf::toString(die.find(dwarf::DW_AT_linkage_name),
dwarf::toString(die.find(dwarf::DW_AT_name), ""));
if (!name.empty())
variableLoc.insert({name, {lt, file, line}});
}
}
} }
// Returns the pair of file name and line number describing location of data // Returns the pair of file name and line number describing location of data
@ -312,19 +275,7 @@ Optional<std::pair<std::string, unsigned>>
ObjFile<ELFT>::getVariableLoc(StringRef name) { ObjFile<ELFT>::getVariableLoc(StringRef name) {
llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); }); llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
// Return if we have no debug information about data object. return dwarf->getVariableLoc(name);
auto it = variableLoc.find(name);
if (it == variableLoc.end())
return None;
// Take file name string from line table.
std::string fileName;
if (!it->second.lt->getFileNameByIndex(
it->second.file, {},
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName))
return None;
return std::make_pair(fileName, it->second.line);
} }
// Returns source line information for a given offset // Returns source line information for a given offset
@ -346,28 +297,7 @@ Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s,
// Use fake address calcuated by adding section file offset and offset in // Use fake address calcuated by adding section file offset and offset in
// section. See comments for ObjectInfo class. // section. See comments for ObjectInfo class.
DILineInfo info; return dwarf->getDILineInfo(s->getOffsetInFile() + offset, sectionIndex);
for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) {
if (lt->getFileLineInfoForAddress(
{s->getOffsetInFile() + offset, sectionIndex}, nullptr,
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info))
return info;
}
return None;
}
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
std::string lld::toString(const InputFile *f) {
if (!f)
return "<internal>";
if (f->toStringCache.empty()) {
if (f->archiveName.empty())
f->toStringCache = f->getName();
else
f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
}
return f->toStringCache;
} }
ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) { ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
@ -484,7 +414,8 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
return signature; return signature;
} }
template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec) { template <class ELFT>
bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec, StringRef name) {
// On a regular link we don't merge sections if -O0 (default is -O1). This // On a regular link we don't merge sections if -O0 (default is -O1). This
// sometimes makes the linker significantly faster, although the output will // sometimes makes the linker significantly faster, although the output will
// be bigger. // be bigger.
@ -516,14 +447,16 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec) {
if (entSize == 0) if (entSize == 0)
return false; return false;
if (sec.sh_size % entSize) if (sec.sh_size % entSize)
fatal(toString(this) + fatal(toString(this) + ":(" + name + "): SHF_MERGE section size (" +
": SHF_MERGE section size must be a multiple of sh_entsize"); Twine(sec.sh_size) + ") must be a multiple of sh_entsize (" +
Twine(entSize) + ")");
uint64_t flags = sec.sh_flags; uint64_t flags = sec.sh_flags;
if (!(flags & SHF_MERGE)) if (!(flags & SHF_MERGE))
return false; return false;
if (flags & SHF_WRITE) if (flags & SHF_WRITE)
fatal(toString(this) + ": writable SHF_MERGE section is not supported"); fatal(toString(this) + ":(" + name +
"): writable SHF_MERGE section is not supported");
return true; return true;
} }
@ -573,7 +506,7 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
this->sectionStringTable = this->sectionStringTable =
CHECK(obj.getSectionStringTable(objSections), this); CHECK(obj.getSectionStringTable(objSections), this);
for (size_t i = 0, e = objSections.size(); i < e; i++) { for (size_t i = 0, e = objSections.size(); i < e; ++i) {
if (this->sections[i] == &InputSection::discarded) if (this->sections[i] == &InputSection::discarded)
continue; continue;
const Elf_Shdr &sec = objSections[i]; const Elf_Shdr &sec = objSections[i];
@ -652,25 +585,29 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
default: default:
this->sections[i] = createInputSection(sec); this->sections[i] = createInputSection(sec);
} }
}
for (size_t i = 0, e = objSections.size(); i < e; ++i) {
if (this->sections[i] == &InputSection::discarded)
continue;
const Elf_Shdr &sec = objSections[i];
if (!(sec.sh_flags & SHF_LINK_ORDER))
continue;
// .ARM.exidx sections have a reverse dependency on the InputSection they // .ARM.exidx sections have a reverse dependency on the InputSection they
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link. // have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
if (sec.sh_flags & SHF_LINK_ORDER) { InputSectionBase *linkSec = nullptr;
InputSectionBase *linkSec = nullptr; if (sec.sh_link < this->sections.size())
if (sec.sh_link < this->sections.size()) linkSec = this->sections[sec.sh_link];
linkSec = this->sections[sec.sh_link]; if (!linkSec)
if (!linkSec) fatal(toString(this) + ": invalid sh_link index: " + Twine(sec.sh_link));
fatal(toString(this) +
": invalid sh_link index: " + Twine(sec.sh_link));
InputSection *isec = cast<InputSection>(this->sections[i]); InputSection *isec = cast<InputSection>(this->sections[i]);
linkSec->dependentSections.push_back(isec); linkSec->dependentSections.push_back(isec);
if (!isa<InputSection>(linkSec)) if (!isa<InputSection>(linkSec))
error("a section " + isec->name + error("a section " + isec->name +
" with SHF_LINK_ORDER should not refer a non-regular " " with SHF_LINK_ORDER should not refer a non-regular section: " +
"section: " + toString(linkSec));
toString(linkSec));
}
} }
} }
@ -1030,7 +967,7 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &sec) {
if (name == ".eh_frame" && !config->relocatable) if (name == ".eh_frame" && !config->relocatable)
return make<EhInputSection>(*this, sec, name); return make<EhInputSection>(*this, sec, name);
if (shouldMerge(sec)) if (shouldMerge(sec, name))
return make<MergeInputSection>(*this, sec, name); return make<MergeInputSection>(*this, sec, name);
return make<InputSection>(*this, sec, name); return make<InputSection>(*this, sec, name);
} }
@ -1094,6 +1031,7 @@ template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
// Handle global undefined symbols. // Handle global undefined symbols.
if (eSym.st_shndx == SHN_UNDEF) { if (eSym.st_shndx == SHN_UNDEF) {
this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type}); this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
this->symbols[i]->referenced = true;
continue; continue;
} }
@ -1470,10 +1408,12 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
int c = objSym.getComdatIndex(); int c = objSym.getComdatIndex();
if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) { if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) {
Undefined New(&f, name, binding, visibility, type); Undefined newSym(&f, name, binding, visibility, type);
if (canOmitFromDynSym) if (canOmitFromDynSym)
New.exportDynamic = false; newSym.exportDynamic = false;
return symtab->addSymbol(New); Symbol *ret = symtab->addSymbol(newSym);
ret->referenced = true;
return ret;
} }
if (objSym.isCommon()) if (objSym.isCommon())
@ -1481,10 +1421,10 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
CommonSymbol{&f, name, binding, visibility, STT_OBJECT, CommonSymbol{&f, name, binding, visibility, STT_OBJECT,
objSym.getCommonAlignment(), objSym.getCommonSize()}); objSym.getCommonAlignment(), objSym.getCommonSize()});
Defined New(&f, name, binding, visibility, type, 0, 0, nullptr); Defined newSym(&f, name, binding, visibility, type, 0, 0, nullptr);
if (canOmitFromDynSym) if (canOmitFromDynSym)
New.exportDynamic = false; newSym.exportDynamic = false;
return symtab->addSymbol(New); return symtab->addSymbol(newSym);
} }
template <class ELFT> void BitcodeFile::parse() { template <class ELFT> void BitcodeFile::parse() {
@ -1523,8 +1463,8 @@ void BinaryFile::parse() {
STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr}); STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
} }
InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName, InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive) { uint64_t offsetInArchive) {
if (isBitcode(mb)) if (isBitcode(mb))
return make<BitcodeFile>(mb, archiveName, offsetInArchive); return make<BitcodeFile>(mb, archiveName, offsetInArchive);
@ -1615,7 +1555,7 @@ template <class ELFT> void LazyObjFile::parse() {
} }
} }
std::string elf::replaceThinLTOSuffix(StringRef path) { std::string replaceThinLTOSuffix(StringRef path) {
StringRef suffix = config->thinLTOObjectSuffixReplace.first; StringRef suffix = config->thinLTOObjectSuffixReplace.first;
StringRef repl = config->thinLTOObjectSuffixReplace.second; StringRef repl = config->thinLTOObjectSuffixReplace.second;
@ -1634,12 +1574,15 @@ template void LazyObjFile::parse<ELF32BE>();
template void LazyObjFile::parse<ELF64LE>(); template void LazyObjFile::parse<ELF64LE>();
template void LazyObjFile::parse<ELF64BE>(); template void LazyObjFile::parse<ELF64BE>();
template class elf::ObjFile<ELF32LE>; template class ObjFile<ELF32LE>;
template class elf::ObjFile<ELF32BE>; template class ObjFile<ELF32BE>;
template class elf::ObjFile<ELF64LE>; template class ObjFile<ELF64LE>;
template class elf::ObjFile<ELF64BE>; template class ObjFile<ELF64BE>;
template void SharedFile::parse<ELF32LE>(); template void SharedFile::parse<ELF32LE>();
template void SharedFile::parse<ELF32BE>(); template void SharedFile::parse<ELF32BE>();
template void SharedFile::parse<ELF64LE>(); template void SharedFile::parse<ELF64LE>();
template void SharedFile::parse<ELF64BE>(); template void SharedFile::parse<ELF64BE>();
} // namespace elf
} // namespace lld

View File

@ -10,13 +10,13 @@
#define LLD_ELF_INPUT_FILES_H #define LLD_ELF_INPUT_FILES_H
#include "Config.h" #include "Config.h"
#include "lld/Common/DWARF.h"
#include "lld/Common/ErrorHandler.h" #include "lld/Common/ErrorHandler.h"
#include "lld/Common/LLVM.h" #include "lld/Common/LLVM.h"
#include "lld/Common/Reproduce.h" #include "lld/Common/Reproduce.h"
#include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
#include "llvm/IR/Comdat.h" #include "llvm/IR/Comdat.h"
#include "llvm/Object/Archive.h" #include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h" #include "llvm/Object/ELF.h"
@ -26,22 +26,19 @@
namespace llvm { namespace llvm {
class TarWriter; class TarWriter;
struct DILineInfo;
namespace lto { namespace lto {
class InputFile; class InputFile;
} }
} // namespace llvm } // namespace llvm
namespace lld { namespace lld {
namespace elf {
class InputFile;
class InputSectionBase;
}
// Returns "<internal>", "foo.a(bar.o)" or "baz.o". // Returns "<internal>", "foo.a(bar.o)" or "baz.o".
std::string toString(const elf::InputFile *f); std::string toString(const elf::InputFile *f);
namespace elf { namespace elf {
class InputFile;
class InputSectionBase;
using llvm::object::Archive; using llvm::object::Archive;
@ -261,7 +258,7 @@ template <class ELFT> class ObjFile : public ELFFileBase {
InputSectionBase *createInputSection(const Elf_Shdr &sec); InputSectionBase *createInputSection(const Elf_Shdr &sec);
StringRef getSectionName(const Elf_Shdr &sec); StringRef getSectionName(const Elf_Shdr &sec);
bool shouldMerge(const Elf_Shdr &sec); bool shouldMerge(const Elf_Shdr &sec, StringRef name);
// Each ELF symbol contains a section index which the symbol belongs to. // Each ELF symbol contains a section index which the symbol belongs to.
// However, because the number of bits dedicated for that is limited, a // However, because the number of bits dedicated for that is limited, a
@ -284,14 +281,7 @@ template <class ELFT> class ObjFile : public ELFFileBase {
// reporting. Linker may find reasonable number of errors in a // reporting. Linker may find reasonable number of errors in a
// single object file, so we cache debugging information in order to // single object file, so we cache debugging information in order to
// parse it only once for each object file we link. // parse it only once for each object file we link.
std::unique_ptr<llvm::DWARFContext> dwarf; DWARFCache *dwarf;
std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
struct VarLoc {
const llvm::DWARFDebugLine::LineTable *lt;
unsigned file;
unsigned line;
};
llvm::DenseMap<StringRef, VarLoc> variableLoc;
llvm::once_flag initDwarfLine; llvm::once_flag initDwarfLine;
}; };

View File

@ -37,16 +37,15 @@ using namespace llvm::support;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::sys; using namespace llvm::sys;
using namespace lld; namespace lld {
using namespace lld::elf;
std::vector<InputSectionBase *> elf::inputSections;
// Returns a string to construct an error message. // Returns a string to construct an error message.
std::string lld::toString(const InputSectionBase *sec) { std::string toString(const elf::InputSectionBase *sec) {
return (toString(sec->file) + ":(" + sec->name + ")").str(); return (toString(sec->file) + ":(" + sec->name + ")").str();
} }
namespace elf {
std::vector<InputSectionBase *> inputSections;
template <class ELFT> template <class ELFT>
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file, static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
const typename ELFT::Shdr &hdr) { const typename ELFT::Shdr &hdr) {
@ -608,26 +607,39 @@ static int64_t getTlsTpOffset(const Symbol &s) {
if (&s == ElfSym::tlsModuleBase) if (&s == ElfSym::tlsModuleBase)
return 0; return 0;
// There are 2 TLS layouts. Among targets we support, x86 uses TLS Variant 2
// while most others use Variant 1. At run time TP will be aligned to p_align.
// Variant 1. TP will be followed by an optional gap (which is the size of 2
// pointers on ARM/AArch64, 0 on other targets), followed by alignment
// padding, then the static TLS blocks. The alignment padding is added so that
// (TP + gap + padding) is congruent to p_vaddr modulo p_align.
//
// Variant 2. Static TLS blocks, followed by alignment padding are placed
// before TP. The alignment padding is added so that (TP - padding -
// p_memsz) is congruent to p_vaddr modulo p_align.
PhdrEntry *tls = Out::tlsPhdr;
switch (config->emachine) { switch (config->emachine) {
// Variant 1.
case EM_ARM: case EM_ARM:
case EM_AARCH64: case EM_AARCH64:
// Variant 1. The thread pointer points to a TCB with a fixed 2-word size, return s.getVA(0) + config->wordsize * 2 +
// followed by a variable amount of alignment padding, followed by the TLS ((tls->p_vaddr - config->wordsize * 2) & (tls->p_align - 1));
// segment. case EM_MIPS:
return s.getVA(0) + alignTo(config->wordsize * 2, Out::tlsPhdr->p_align);
case EM_386:
case EM_X86_64:
// Variant 2. The TLS segment is located just before the thread pointer.
return s.getVA(0) - alignTo(Out::tlsPhdr->p_memsz, Out::tlsPhdr->p_align);
case EM_PPC: case EM_PPC:
case EM_PPC64: case EM_PPC64:
// The thread pointer points to a fixed offset from the start of the // Adjusted Variant 1. TP is placed with a displacement of 0x7000, which is
// executable's TLS segment. An offset of 0x7000 allows a signed 16-bit // to allow a signed 16-bit offset to reach 0x1000 of TCB/thread-library
// offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the // data and 0xf000 of the program's TLS segment.
// program's TLS segment. return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)) - 0x7000;
return s.getVA(0) - 0x7000;
case EM_RISCV: case EM_RISCV:
return s.getVA(0); return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1));
// Variant 2.
case EM_386:
case EM_X86_64:
return s.getVA(0) - tls->p_memsz -
((-tls->p_vaddr - tls->p_memsz) & (tls->p_align - 1));
default: default:
llvm_unreachable("unhandled Config->EMachine"); llvm_unreachable("unhandled Config->EMachine");
} }
@ -671,8 +683,6 @@ static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a,
case R_GOT_PC: case R_GOT_PC:
case R_RELAX_TLS_GD_TO_IE: case R_RELAX_TLS_GD_TO_IE:
return sym.getGotVA() + a - p; return sym.getGotVA() + a - p;
case R_HEXAGON_GOT:
return sym.getGotVA() - in.gotPlt->getVA();
case R_MIPS_GOTREL: case R_MIPS_GOTREL:
return sym.getVA(a) - in.mipsGot->getGp(file); return sym.getVA(a) - in.mipsGot->getGp(file);
case R_MIPS_GOT_GP: case R_MIPS_GOT_GP:
@ -1071,7 +1081,7 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *buf,
end, f->stOther)) end, f->stOther))
continue; continue;
if (!getFile<ELFT>()->someNoSplitStack) if (!getFile<ELFT>()->someNoSplitStack)
error(lld::toString(this) + ": " + f->getName() + error(toString(this) + ": " + f->getName() +
" (with -fsplit-stack) calls " + rel.sym->getName() + " (with -fsplit-stack) calls " + rel.sym->getName() +
" (without -fsplit-stack), but couldn't adjust its prologue"); " (without -fsplit-stack), but couldn't adjust its prologue");
} }
@ -1334,3 +1344,6 @@ template void EhInputSection::split<ELF32LE>();
template void EhInputSection::split<ELF32BE>(); template void EhInputSection::split<ELF32BE>();
template void EhInputSection::split<ELF64LE>(); template void EhInputSection::split<ELF64LE>();
template void EhInputSection::split<ELF64BE>(); template void EhInputSection::split<ELF64BE>();
} // namespace elf
} // namespace lld

View File

@ -54,22 +54,9 @@ class SectionBase {
unsigned sectionKind : 3; unsigned sectionKind : 3;
// The next three bit fields are only used by InputSectionBase, but we // The next two bit fields are only used by InputSectionBase, but we
// put them here so the struct packs better. // put them here so the struct packs better.
// True if this section has already been placed to a linker script
// output section. This is needed because, in a linker script, you
// can refer to the same section more than once. For example, in
// the following linker script,
//
// .foo : { *(.text) }
// .bar : { *(.text) }
//
// .foo takes all .text sections, and .bar becomes empty. To achieve
// this, we need to memorize whether a section has been placed or
// not for each input section.
unsigned assigned : 1;
unsigned bss : 1; unsigned bss : 1;
// Set for sections that should not be folded by ICF. // Set for sections that should not be folded by ICF.
@ -108,9 +95,9 @@ class SectionBase {
SectionBase(Kind sectionKind, StringRef name, uint64_t flags, SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
uint64_t entsize, uint64_t alignment, uint32_t type, uint64_t entsize, uint64_t alignment, uint32_t type,
uint32_t info, uint32_t link) uint32_t info, uint32_t link)
: name(name), repl(this), sectionKind(sectionKind), assigned(false), : name(name), repl(this), sectionKind(sectionKind), bss(false),
bss(false), keepUnique(false), partition(0), alignment(alignment), keepUnique(false), partition(0), alignment(alignment), flags(flags),
flags(flags), entsize(entsize), type(type), link(link), info(info) {} entsize(entsize), type(type), link(link), info(info) {}
}; };
// This corresponds to a section of an input file. // This corresponds to a section of an input file.

View File

@ -42,15 +42,15 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
// Creates an empty file to store a list of object files for final // Creates an empty file to store a list of object files for final
// linking of distributed ThinLTO. // linking of distributed ThinLTO.
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) { static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
std::error_code ec; std::error_code ec;
auto ret = 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) { if (ec) {
error("cannot open " + file + ": " + ec.message()); error("cannot open " + file + ": " + ec.message());
return nullptr; return nullptr;
@ -76,7 +76,9 @@ static lto::Config createConfig() {
c.Options.FunctionSections = true; c.Options.FunctionSections = true;
c.Options.DataSections = true; c.Options.DataSections = true;
if (config->relocatable) if (auto relocModel = getRelocModelFromCMModel())
c.RelocModel = *relocModel;
else if (config->relocatable)
c.RelocModel = None; c.RelocModel = None;
else if (config->isPic) else if (config->isPic)
c.RelocModel = Reloc::PIC_; c.RelocModel = Reloc::PIC_;
@ -139,7 +141,7 @@ BitcodeCompiler::BitcodeCompiler() {
backend = lto::createInProcessThinBackend(config->thinLTOJobs); backend = lto::createInProcessThinBackend(config->thinLTOJobs);
} }
ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend, ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
config->ltoPartitions); config->ltoPartitions);
// Initialize usedStartStop. // Initialize usedStartStop.
@ -249,8 +251,8 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
if (!bitcodeFiles.empty()) if (!bitcodeFiles.empty())
checkError(ltoObj->run( checkError(ltoObj->run(
[&](size_t task) { [&](size_t task) {
return llvm::make_unique<lto::NativeObjectStream>( return std::make_unique<lto::NativeObjectStream>(
llvm::make_unique<raw_svector_ostream>(buf[task])); std::make_unique<raw_svector_ostream>(buf[task]));
}, },
cache)); cache));
@ -301,3 +303,6 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
ret.push_back(createObjectFile(*file)); ret.push_back(createObjectFile(*file));
return ret; return ret;
} }
} // namespace elf
} // namespace lld

View File

@ -43,29 +43,27 @@ using namespace llvm;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
LinkerScript *elf::script; namespace lld {
namespace elf {
LinkerScript *script;
static uint64_t getOutputSectionVA(SectionBase *inputSec, StringRef loc) { static uint64_t getOutputSectionVA(SectionBase *sec) {
if (OutputSection *os = inputSec->getOutputSection()) OutputSection *os = sec->getOutputSection();
return os->addr; assert(os && "input section has no output section assigned");
error(loc + ": unable to evaluate expression: input section " + return os ? os->addr : 0;
inputSec->name + " has no output section assigned");
return 0;
} }
uint64_t ExprValue::getValue() const { uint64_t ExprValue::getValue() const {
if (sec) if (sec)
return alignTo(sec->getOffset(val) + getOutputSectionVA(sec, loc), return alignTo(sec->getOffset(val) + getOutputSectionVA(sec),
alignment); alignment);
return alignTo(val, alignment); return alignTo(val, alignment);
} }
uint64_t ExprValue::getSecAddr() const { uint64_t ExprValue::getSecAddr() const {
if (sec) if (sec)
return sec->getOffset(0) + getOutputSectionVA(sec, loc); return sec->getOffset(0) + getOutputSectionVA(sec);
return 0; return 0;
} }
@ -73,7 +71,7 @@ uint64_t ExprValue::getSectionOffset() const {
// If the alignment is trivial, we don't have to compute the full // If the alignment is trivial, we don't have to compute the full
// value to know the offset. This allows this function to succeed in // value to know the offset. This allows this function to succeed in
// cases where the output section is not yet known. // cases where the output section is not yet known.
if (alignment == 1 && (!sec || !sec->getOutputSection())) if (alignment == 1 && !sec)
return val; return val;
return getValue() - getSecAddr(); return getValue() - getSecAddr();
} }
@ -157,8 +155,8 @@ static bool shouldDefineSym(SymbolAssignment *cmd) {
return false; return false;
} }
// This function is called from processSectionCommands, // Called by processSymbolAssignments() to assign definitions to
// while we are fixing the output section layout. // linker-script-defined symbols.
void LinkerScript::addSymbol(SymbolAssignment *cmd) { void LinkerScript::addSymbol(SymbolAssignment *cmd) {
if (!shouldDefineSym(cmd)) if (!shouldDefineSym(cmd))
return; return;
@ -181,12 +179,12 @@ void LinkerScript::addSymbol(SymbolAssignment *cmd) {
// write expressions like this: `alignment = 16; . = ALIGN(., alignment)`. // write expressions like this: `alignment = 16; . = ALIGN(., alignment)`.
uint64_t symValue = value.sec ? 0 : value.getValue(); uint64_t symValue = value.sec ? 0 : value.getValue();
Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, symValue, Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE,
0, sec); symValue, 0, sec);
Symbol *sym = symtab->insert(cmd->name); Symbol *sym = symtab->insert(cmd->name);
sym->mergeProperties(New); sym->mergeProperties(newSym);
sym->replace(New); sym->replace(newSym);
cmd->sym = cast<Defined>(sym); cmd->sym = cast<Defined>(sym);
} }
@ -197,19 +195,57 @@ static void declareSymbol(SymbolAssignment *cmd) {
return; return;
uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT; uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT;
Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0, Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0,
nullptr); nullptr);
// We can't calculate final value right now. // We can't calculate final value right now.
Symbol *sym = symtab->insert(cmd->name); Symbol *sym = symtab->insert(cmd->name);
sym->mergeProperties(New); sym->mergeProperties(newSym);
sym->replace(New); sym->replace(newSym);
cmd->sym = cast<Defined>(sym); cmd->sym = cast<Defined>(sym);
cmd->provide = false; cmd->provide = false;
sym->scriptDefined = true; sym->scriptDefined = true;
} }
using SymbolAssignmentMap =
DenseMap<const Defined *, std::pair<SectionBase *, uint64_t>>;
// Collect section/value pairs of linker-script-defined symbols. This is used to
// check whether symbol values converge.
static SymbolAssignmentMap
getSymbolAssignmentValues(const std::vector<BaseCommand *> &sectionCommands) {
SymbolAssignmentMap ret;
for (BaseCommand *base : sectionCommands) {
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
if (cmd->sym) // sym is nullptr for dot.
ret.try_emplace(cmd->sym,
std::make_pair(cmd->sym->section, cmd->sym->value));
continue;
}
for (BaseCommand *sub_base : cast<OutputSection>(base)->sectionCommands)
if (auto *cmd = dyn_cast<SymbolAssignment>(sub_base))
if (cmd->sym)
ret.try_emplace(cmd->sym,
std::make_pair(cmd->sym->section, cmd->sym->value));
}
return ret;
}
// Returns the lexicographical smallest (for determinism) Defined whose
// section/value has changed.
static const Defined *
getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) {
const Defined *changed = nullptr;
for (auto &it : oldValues) {
const Defined *sym = it.first;
if (std::make_pair(sym->section, sym->value) != it.second &&
(!changed || sym->getName() < changed->getName()))
changed = sym;
}
return changed;
}
// This method is used to handle INSERT AFTER statement. Here we rebuild // This method is used to handle INSERT AFTER statement. Here we rebuild
// the list of script commands to mix sections inserted into. // the list of script commands to mix sections inserted into.
void LinkerScript::processInsertCommands() { void LinkerScript::processInsertCommands() {
@ -305,46 +341,44 @@ bool LinkerScript::shouldKeep(InputSectionBase *s) {
} }
// A helper function for the SORT() command. // A helper function for the SORT() command.
static std::function<bool(InputSectionBase *, InputSectionBase *)> static bool matchConstraints(ArrayRef<InputSectionBase *> sections,
getComparator(SortSectionPolicy k) {
switch (k) {
case SortSectionPolicy::Alignment:
return [](InputSectionBase *a, InputSectionBase *b) {
// ">" is not a mistake. Sections with larger alignments are placed
// before sections with smaller alignments in order to reduce the
// amount of padding necessary. This is compatible with GNU.
return a->alignment > b->alignment;
};
case SortSectionPolicy::Name:
return [](InputSectionBase *a, InputSectionBase *b) {
return a->name < b->name;
};
case SortSectionPolicy::Priority:
return [](InputSectionBase *a, InputSectionBase *b) {
return getPriority(a->name) < getPriority(b->name);
};
default:
llvm_unreachable("unknown sort policy");
}
}
// A helper function for the SORT() command.
static bool matchConstraints(ArrayRef<InputSection *> sections,
ConstraintKind kind) { ConstraintKind kind) {
if (kind == ConstraintKind::NoConstraint) if (kind == ConstraintKind::NoConstraint)
return true; return true;
bool isRW = llvm::any_of( bool isRW = llvm::any_of(
sections, [](InputSection *sec) { return sec->flags & SHF_WRITE; }); sections, [](InputSectionBase *sec) { return sec->flags & SHF_WRITE; });
return (isRW && kind == ConstraintKind::ReadWrite) || return (isRW && kind == ConstraintKind::ReadWrite) ||
(!isRW && kind == ConstraintKind::ReadOnly); (!isRW && kind == ConstraintKind::ReadOnly);
} }
static void sortSections(MutableArrayRef<InputSection *> vec, static void sortSections(MutableArrayRef<InputSectionBase *> vec,
SortSectionPolicy k) { SortSectionPolicy k) {
if (k != SortSectionPolicy::Default && k != SortSectionPolicy::None) auto alignmentComparator = [](InputSectionBase *a, InputSectionBase *b) {
llvm::stable_sort(vec, getComparator(k)); // ">" is not a mistake. Sections with larger alignments are placed
// before sections with smaller alignments in order to reduce the
// amount of padding necessary. This is compatible with GNU.
return a->alignment > b->alignment;
};
auto nameComparator = [](InputSectionBase *a, InputSectionBase *b) {
return a->name < b->name;
};
auto priorityComparator = [](InputSectionBase *a, InputSectionBase *b) {
return getPriority(a->name) < getPriority(b->name);
};
switch (k) {
case SortSectionPolicy::Default:
case SortSectionPolicy::None:
return;
case SortSectionPolicy::Alignment:
return llvm::stable_sort(vec, alignmentComparator);
case SortSectionPolicy::Name:
return llvm::stable_sort(vec, nameComparator);
case SortSectionPolicy::Priority:
return llvm::stable_sort(vec, priorityComparator);
}
} }
// Sort sections as instructed by SORT-family commands and --sort-section // Sort sections as instructed by SORT-family commands and --sort-section
@ -358,7 +392,7 @@ static void sortSections(MutableArrayRef<InputSection *> vec,
// --sort-section is handled as an inner SORT command. // --sort-section is handled as an inner SORT command.
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort. // 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
// 4. If no SORT command is given, sort according to --sort-section. // 4. If no SORT command is given, sort according to --sort-section.
static void sortInputSections(MutableArrayRef<InputSection *> vec, static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
const SectionPattern &pat) { const SectionPattern &pat) {
if (pat.sortOuter == SortSectionPolicy::None) if (pat.sortOuter == SortSectionPolicy::None)
return; return;
@ -371,16 +405,16 @@ static void sortInputSections(MutableArrayRef<InputSection *> vec,
} }
// Compute and remember which sections the InputSectionDescription matches. // Compute and remember which sections the InputSectionDescription matches.
std::vector<InputSection *> std::vector<InputSectionBase *>
LinkerScript::computeInputSections(const InputSectionDescription *cmd) { LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
std::vector<InputSection *> ret; std::vector<InputSectionBase *> ret;
// Collects all sections that satisfy constraints of Cmd. // Collects all sections that satisfy constraints of Cmd.
for (const SectionPattern &pat : cmd->sectionPatterns) { for (const SectionPattern &pat : cmd->sectionPatterns) {
size_t sizeBefore = ret.size(); size_t sizeBefore = ret.size();
for (InputSectionBase *sec : inputSections) { for (InputSectionBase *sec : inputSections) {
if (!sec->isLive() || sec->assigned) if (!sec->isLive() || sec->parent)
continue; continue;
// For -emit-relocs we have to ignore entries like // For -emit-relocs we have to ignore entries like
@ -388,9 +422,9 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
// which are common because they are in the default bfd script. // which are common because they are in the default bfd script.
// We do not ignore SHT_REL[A] linker-synthesized sections here because // We do not ignore SHT_REL[A] linker-synthesized sections here because
// want to support scripts that do custom layout for them. // want to support scripts that do custom layout for them.
if (auto *isec = dyn_cast<InputSection>(sec)) if (isa<InputSection>(sec) &&
if (isec->getRelocatedSection()) cast<InputSection>(sec)->getRelocatedSection())
continue; continue;
std::string filename = getFilename(sec->file); std::string filename = getFilename(sec->file);
if (!cmd->filePat.match(filename) || if (!cmd->filePat.match(filename) ||
@ -398,88 +432,60 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
!pat.sectionPat.match(sec->name)) !pat.sectionPat.match(sec->name))
continue; continue;
// It is safe to assume that Sec is an InputSection ret.push_back(sec);
// because mergeable or EH input sections have already been
// handled and eliminated.
ret.push_back(cast<InputSection>(sec));
sec->assigned = true;
} }
sortInputSections(MutableArrayRef<InputSection *>(ret).slice(sizeBefore), sortInputSections(
pat); MutableArrayRef<InputSectionBase *>(ret).slice(sizeBefore), pat);
} }
return ret; return ret;
} }
void LinkerScript::discard(ArrayRef<InputSection *> v) { void LinkerScript::discard(InputSectionBase *s) {
for (InputSection *s : v) { if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn) error("discarding " + s->name + " section is not allowed");
error("discarding " + s->name + " section is not allowed");
// You can discard .hash and .gnu.hash sections by linker scripts. Since // You can discard .hash and .gnu.hash sections by linker scripts. Since
// they are synthesized sections, we need to handle them differently than // they are synthesized sections, we need to handle them differently than
// other regular sections. // other regular sections.
if (s == mainPart->gnuHashTab) if (s == mainPart->gnuHashTab)
mainPart->gnuHashTab = nullptr; mainPart->gnuHashTab = nullptr;
if (s == mainPart->hashTab) if (s == mainPart->hashTab)
mainPart->hashTab = nullptr; mainPart->hashTab = nullptr;
s->assigned = false; s->markDead();
s->markDead(); s->parent = nullptr;
discard(s->dependentSections); for (InputSection *ds : s->dependentSections)
} discard(ds);
} }
std::vector<InputSection *> std::vector<InputSectionBase *>
LinkerScript::createInputSectionList(OutputSection &outCmd) { LinkerScript::createInputSectionList(OutputSection &outCmd) {
std::vector<InputSection *> ret; std::vector<InputSectionBase *> ret;
for (BaseCommand *base : outCmd.sectionCommands) { for (BaseCommand *base : outCmd.sectionCommands) {
if (auto *cmd = dyn_cast<InputSectionDescription>(base)) { if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
cmd->sections = computeInputSections(cmd); cmd->sectionBases = computeInputSections(cmd);
ret.insert(ret.end(), cmd->sections.begin(), cmd->sections.end()); for (InputSectionBase *s : cmd->sectionBases)
s->parent = &outCmd;
ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end());
} }
} }
return ret; return ret;
} }
// Create output sections described by SECTIONS commands.
void LinkerScript::processSectionCommands() { void LinkerScript::processSectionCommands() {
// A symbol can be assigned before any section is mentioned in the linker
// script. In an DSO, the symbol values are addresses, so the only important
// section values are:
// * SHN_UNDEF
// * SHN_ABS
// * Any value meaning a regular section.
// To handle that, create a dummy aether section that fills the void before
// the linker scripts switches to another section. It has an index of one
// which will map to whatever the first actual section is.
aether = make<OutputSection>("", 0, SHF_ALLOC);
aether->sectionIndex = 1;
// Ctx captures the local AddressState and makes it accessible deliberately.
// This is needed as there are some cases where we cannot just
// thread the current state through to a lambda function created by the
// script parser.
auto deleter = make_unique<AddressState>();
ctx = deleter.get();
ctx->outSec = aether;
size_t i = 0; size_t i = 0;
// Add input sections to output sections.
for (BaseCommand *base : sectionCommands) { for (BaseCommand *base : sectionCommands) {
// Handle symbol assignments outside of any output section.
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
addSymbol(cmd);
continue;
}
if (auto *sec = dyn_cast<OutputSection>(base)) { if (auto *sec = dyn_cast<OutputSection>(base)) {
std::vector<InputSection *> v = createInputSectionList(*sec); std::vector<InputSectionBase *> v = createInputSectionList(*sec);
// The output section name `/DISCARD/' is special. // The output section name `/DISCARD/' is special.
// Any input section assigned to it is discarded. // Any input section assigned to it is discarded.
if (sec->name == "/DISCARD/") { if (sec->name == "/DISCARD/") {
discard(v); for (InputSectionBase *s : v)
discard(s);
sec->sectionCommands.clear(); sec->sectionCommands.clear();
continue; continue;
} }
@ -493,17 +499,11 @@ void LinkerScript::processSectionCommands() {
// way to "make it as if it wasn't present" is to make it empty. // way to "make it as if it wasn't present" is to make it empty.
if (!matchConstraints(v, sec->constraint)) { if (!matchConstraints(v, sec->constraint)) {
for (InputSectionBase *s : v) for (InputSectionBase *s : v)
s->assigned = false; s->parent = nullptr;
sec->sectionCommands.clear(); sec->sectionCommands.clear();
continue; continue;
} }
// A directive may contain symbol definitions like this:
// ".foo : { ...; bar = .; }". Handle them.
for (BaseCommand *base : sec->sectionCommands)
if (auto *outCmd = dyn_cast<SymbolAssignment>(base))
addSymbol(outCmd);
// Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign // Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
// is given, input sections are aligned to that value, whether the // is given, input sections are aligned to that value, whether the
// given value is larger or smaller than the original section alignment. // given value is larger or smaller than the original section alignment.
@ -513,17 +513,40 @@ void LinkerScript::processSectionCommands() {
s->alignment = subalign; s->alignment = subalign;
} }
// Add input sections to an output section. // Set the partition field the same way OutputSection::recordSection()
for (InputSection *s : v) // does. Partitions cannot be used with the SECTIONS command, so this is
sec->addSection(s); // always 1.
sec->partition = 1;
sec->sectionIndex = i++; sec->sectionIndex = i++;
if (sec->noload)
sec->type = SHT_NOBITS;
if (sec->nonAlloc)
sec->flags &= ~(uint64_t)SHF_ALLOC;
} }
} }
}
void LinkerScript::processSymbolAssignments() {
// Dot outside an output section still represents a relative address, whose
// sh_shndx should not be SHN_UNDEF or SHN_ABS. Create a dummy aether section
// that fills the void outside a section. It has an index of one, which is
// indistinguishable from any other regular section index.
aether = make<OutputSection>("", 0, SHF_ALLOC);
aether->sectionIndex = 1;
// ctx captures the local AddressState and makes it accessible deliberately.
// This is needed as there are some cases where we cannot just thread the
// current state through to a lambda function created by the script parser.
AddressState state;
ctx = &state;
ctx->outSec = aether;
for (BaseCommand *base : sectionCommands) {
if (auto *cmd = dyn_cast<SymbolAssignment>(base))
addSymbol(cmd);
else
for (BaseCommand *sub_base : cast<OutputSection>(base)->sectionCommands)
if (auto *cmd = dyn_cast<SymbolAssignment>(sub_base))
addSymbol(cmd);
}
ctx = nullptr; ctx = nullptr;
} }
@ -539,7 +562,7 @@ static OutputSection *findByName(ArrayRef<BaseCommand *> vec,
static OutputSection *createSection(InputSectionBase *isec, static OutputSection *createSection(InputSectionBase *isec,
StringRef outsecName) { StringRef outsecName) {
OutputSection *sec = script->createOutputSection(outsecName, "<internal>"); OutputSection *sec = script->createOutputSection(outsecName, "<internal>");
sec->addSection(cast<InputSection>(isec)); sec->recordSection(isec);
return sec; return sec;
} }
@ -568,7 +591,7 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
OutputSection *out = sec->getRelocatedSection()->getOutputSection(); OutputSection *out = sec->getRelocatedSection()->getOutputSection();
if (out->relocationSection) { if (out->relocationSection) {
out->relocationSection->addSection(sec); out->relocationSection->recordSection(sec);
return nullptr; return nullptr;
} }
@ -576,12 +599,6 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
return out->relocationSection; return out->relocationSection;
} }
// When control reaches here, mergeable sections have already been merged into
// synthetic sections. For relocatable case we want to create one output
// section per syntetic section so that they have a valid sh_entsize.
if (config->relocatable && (isec->flags & SHF_MERGE))
return createSection(isec, outsecName);
// The ELF spec just says // The ELF spec just says
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// In the first phase, input sections that match in name, type and // In the first phase, input sections that match in name, type and
@ -628,7 +645,21 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
for (OutputSection *sec : v) { for (OutputSection *sec : v) {
if (sec->partition != isec->partition) if (sec->partition != isec->partition)
continue; continue;
sec->addSection(cast<InputSection>(isec));
if (config->relocatable && (isec->flags & SHF_LINK_ORDER)) {
// Merging two SHF_LINK_ORDER sections with different sh_link fields will
// change their semantics, so we only merge them in -r links if they will
// end up being linked to the same output section. The casts are fine
// because everything in the map was created by the orphan placement code.
auto *firstIsec = cast<InputSectionBase>(
cast<InputSectionDescription>(sec->sectionCommands[0])
->sectionBases[0]);
if (firstIsec->getLinkOrderDep()->getOutputSection() !=
isec->getLinkOrderDep()->getOutputSection())
continue;
}
sec->recordSection(isec);
return nullptr; return nullptr;
} }
@ -642,25 +673,30 @@ void LinkerScript::addOrphanSections() {
StringMap<TinyPtrVector<OutputSection *>> map; StringMap<TinyPtrVector<OutputSection *>> map;
std::vector<OutputSection *> v; std::vector<OutputSection *> v;
auto add = [&](InputSectionBase *s) { std::function<void(InputSectionBase *)> add;
if (!s->isLive() || s->parent) add = [&](InputSectionBase *s) {
return; if (s->isLive() && !s->parent) {
StringRef name = getOutputSectionName(s);
StringRef name = getOutputSectionName(s); if (config->orphanHandling == OrphanHandlingPolicy::Error)
error(toString(s) + " is being placed in '" + name + "'");
else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
warn(toString(s) + " is being placed in '" + name + "'");
if (config->orphanHandling == OrphanHandlingPolicy::Error) if (OutputSection *sec = findByName(sectionCommands, name)) {
error(toString(s) + " is being placed in '" + name + "'"); sec->recordSection(s);
else if (config->orphanHandling == OrphanHandlingPolicy::Warn) } else {
warn(toString(s) + " is being placed in '" + name + "'"); if (OutputSection *os = addInputSec(map, s, name))
v.push_back(os);
if (OutputSection *sec = findByName(sectionCommands, name)) { assert(isa<MergeInputSection>(s) ||
sec->addSection(cast<InputSection>(s)); s->getOutputSection()->sectionIndex == UINT32_MAX);
return; }
} }
if (OutputSection *os = addInputSec(map, s, name)) if (config->relocatable)
v.push_back(os); for (InputSectionBase *depSec : s->dependentSections)
assert(s->getOutputSection()->sectionIndex == UINT32_MAX); if (depSec->flags & SHF_LINK_ORDER)
add(depSec);
}; };
// For futher --emit-reloc handling code we need target output section // For futher --emit-reloc handling code we need target output section
@ -668,6 +704,12 @@ void LinkerScript::addOrphanSections() {
// to create target sections first. We do not want priority handling // to create target sections first. We do not want priority handling
// for synthetic sections because them are special. // for synthetic sections because them are special.
for (InputSectionBase *isec : inputSections) { for (InputSectionBase *isec : inputSections) {
// In -r links, SHF_LINK_ORDER sections are added while adding their parent
// sections because we need to know the parent's output section before we
// can select an output section for the SHF_LINK_ORDER section.
if (config->relocatable && (isec->flags & SHF_LINK_ORDER))
continue;
if (auto *sec = dyn_cast<InputSection>(isec)) if (auto *sec = dyn_cast<InputSection>(isec))
if (InputSectionBase *rel = sec->getRelocatedSection()) if (InputSectionBase *rel = sec->getRelocatedSection())
if (auto *relIS = dyn_cast_or_null<InputSectionBase>(rel->parent)) if (auto *relIS = dyn_cast_or_null<InputSectionBase>(rel->parent))
@ -772,6 +814,14 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
if ((sec->flags & SHF_ALLOC) && sec->addrExpr) if ((sec->flags & SHF_ALLOC) && sec->addrExpr)
setDot(sec->addrExpr, sec->location, false); setDot(sec->addrExpr, sec->location, false);
// If the address of the section has been moved forward by an explicit
// expression so that it now starts past the current curPos of the enclosing
// region, we need to expand the current region to account for the space
// between the previous section, if any, and the start of this section.
if (ctx->memRegion && ctx->memRegion->curPos < dot)
expandMemoryRegion(ctx->memRegion, dot - ctx->memRegion->curPos,
ctx->memRegion->name, sec->name);
switchTo(sec); switchTo(sec);
if (sec->lmaExpr) if (sec->lmaExpr)
@ -972,17 +1022,13 @@ static uint64_t computeBase(uint64_t min, bool allocateHeaders) {
return alignDown(min, config->maxPageSize); return alignDown(min, config->maxPageSize);
} }
// Try to find an address for the file and program headers output sections, // When the SECTIONS command is used, try to find an address for the file and
// which were unconditionally added to the first PT_LOAD segment earlier. // program headers output sections, which can be added to the first PT_LOAD
// segment when program headers are created.
// //
// When using the default layout, we check if the headers fit below the first // We check if the headers fit below the first allocated section. If there isn't
// allocated section. When using a linker script, we also check if the headers // enough space for these sections, we'll remove them from the PT_LOAD segment,
// are covered by the output section. This allows omitting the headers by not // and we'll also remove the PT_PHDR segment.
// leaving enough space for them in the linker script; this pattern is common
// in embedded systems.
//
// If there isn't enough space for these sections, we'll remove them from the
// PT_LOAD segment, and we'll also remove the PT_PHDR segment.
void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) { void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
uint64_t min = std::numeric_limits<uint64_t>::max(); uint64_t min = std::numeric_limits<uint64_t>::max();
for (OutputSection *sec : outputSections) for (OutputSection *sec : outputSections)
@ -1028,32 +1074,30 @@ LinkerScript::AddressState::AddressState() {
} }
} }
static uint64_t getInitialDot() {
// By default linker scripts use an initial value of 0 for '.',
// but prefer -image-base if set.
if (script->hasSectionsCommand)
return config->imageBase ? *config->imageBase : 0;
uint64_t startAddr = UINT64_MAX;
// The sections with -T<section> have been sorted in order of ascending
// address. We must lower startAddr if the lowest -T<section address> as
// calls to setDot() must be monotonically increasing.
for (auto &kv : config->sectionStartMap)
startAddr = std::min(startAddr, kv.second);
return std::min(startAddr, target->getImageBase() + elf::getHeaderSize());
}
// Here we assign addresses as instructed by linker script SECTIONS // Here we assign addresses as instructed by linker script SECTIONS
// sub-commands. Doing that allows us to use final VA values, so here // sub-commands. Doing that allows us to use final VA values, so here
// we also handle rest commands like symbol assignments and ASSERTs. // we also handle rest commands like symbol assignments and ASSERTs.
void LinkerScript::assignAddresses() { // Returns a symbol that has changed its section or value, or nullptr if no
dot = getInitialDot(); // symbol has changed.
const Defined *LinkerScript::assignAddresses() {
if (script->hasSectionsCommand) {
// With a linker script, assignment of addresses to headers is covered by
// allocateHeaders().
dot = config->imageBase.getValueOr(0);
} else {
// Assign addresses to headers right now.
dot = target->getImageBase();
Out::elfHeader->addr = dot;
Out::programHeaders->addr = dot + Out::elfHeader->size;
dot += getHeaderSize();
}
auto deleter = make_unique<AddressState>(); auto deleter = std::make_unique<AddressState>();
ctx = deleter.get(); ctx = deleter.get();
errorOnMissingSection = true; errorOnMissingSection = true;
switchTo(aether); switchTo(aether);
SymbolAssignmentMap oldValues = getSymbolAssignmentValues(sectionCommands);
for (BaseCommand *base : sectionCommands) { for (BaseCommand *base : sectionCommands) {
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) { if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
cmd->addr = dot; cmd->addr = dot;
@ -1063,7 +1107,9 @@ void LinkerScript::assignAddresses() {
} }
assignOffsets(cast<OutputSection>(base)); assignOffsets(cast<OutputSection>(base));
} }
ctx = nullptr; ctx = nullptr;
return getChangedSymbolAssignment(oldValues);
} }
// Creates program headers as instructed by PHDRS linker script command. // Creates program headers as instructed by PHDRS linker script command.
@ -1156,3 +1202,6 @@ std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *cmd) {
} }
return ret; return ret;
} }
} // namespace elf
} // namespace lld

View File

@ -168,6 +168,12 @@ struct InputSectionDescription : BaseCommand {
// will be associated with this InputSectionDescription. // will be associated with this InputSectionDescription.
std::vector<SectionPattern> sectionPatterns; std::vector<SectionPattern> sectionPatterns;
// Includes InputSections and MergeInputSections. Used temporarily during
// assignment of input sections to output sections.
std::vector<InputSectionBase *> sectionBases;
// Used after the finalizeInputSections() pass. MergeInputSections have been
// merged into MergeSyntheticSections.
std::vector<InputSection *> sections; std::vector<InputSection *> sections;
// Temporary record of synthetic ThunkSection instances and the pass that // Temporary record of synthetic ThunkSection instances and the pass that
@ -226,10 +232,10 @@ class LinkerScript final {
void expandOutputSection(uint64_t size); void expandOutputSection(uint64_t size);
void expandMemoryRegions(uint64_t size); void expandMemoryRegions(uint64_t size);
std::vector<InputSection *> std::vector<InputSectionBase *>
computeInputSections(const InputSectionDescription *); computeInputSections(const InputSectionDescription *);
std::vector<InputSection *> createInputSectionList(OutputSection &cmd); std::vector<InputSectionBase *> createInputSectionList(OutputSection &cmd);
std::vector<size_t> getPhdrIndices(OutputSection *sec); std::vector<size_t> getPhdrIndices(OutputSection *sec);
@ -259,7 +265,7 @@ class LinkerScript final {
bool hasPhdrsCommands() { return !phdrsCommands.empty(); } bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
uint64_t getDot() { return dot; } uint64_t getDot() { return dot; }
void discard(ArrayRef<InputSection *> v); void discard(InputSectionBase *s);
ExprValue getSymbolValue(StringRef name, const Twine &loc); ExprValue getSymbolValue(StringRef name, const Twine &loc);
@ -271,9 +277,10 @@ class LinkerScript final {
bool needsInterpSection(); bool needsInterpSection();
bool shouldKeep(InputSectionBase *s); bool shouldKeep(InputSectionBase *s);
void assignAddresses(); const Defined *assignAddresses();
void allocateHeaders(std::vector<PhdrEntry *> &phdrs); void allocateHeaders(std::vector<PhdrEntry *> &phdrs);
void processSectionCommands(); void processSectionCommands();
void processSymbolAssignments();
void declareSymbols(); void declareSymbols();
// Used to handle INSERT AFTER statements. // Used to handle INSERT AFTER statements.

View File

@ -34,13 +34,12 @@
using namespace llvm; using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>; using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
static const std::string indent8 = " "; // 8 spaces static constexpr char indent8[] = " "; // 8 spaces
static const std::string indent16 = " "; // 16 spaces static constexpr char indent16[] = " "; // 16 spaces
// Print out the first three columns of a line. // Print out the first three columns of a line.
static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma, static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma,
@ -139,13 +138,13 @@ static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) {
} }
} }
void elf::writeMapFile() { void writeMapFile() {
if (config->mapFile.empty()) if (config->mapFile.empty())
return; return;
// Open a map file for writing. // Open a map file for writing.
std::error_code ec; 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) { if (ec) {
error("cannot open " + config->mapFile + ": " + ec.message()); error("cannot open " + config->mapFile + ": " + ec.message());
return; return;
@ -228,7 +227,7 @@ static void print(StringRef a, StringRef b) {
// //
// In this case, strlen is defined by libc.so.6 and used by other two // In this case, strlen is defined by libc.so.6 and used by other two
// files. // files.
void elf::writeCrossReferenceTable() { void writeCrossReferenceTable() {
if (!config->cref) if (!config->cref)
return; return;
@ -259,3 +258,6 @@ void elf::writeCrossReferenceTable() {
print("", toString(file)); print("", toString(file));
} }
} }
} // namespace elf
} // namespace lld

View File

@ -37,11 +37,11 @@
using namespace llvm; using namespace llvm;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian;
using namespace lld; namespace endian = llvm::support::endian;
using namespace lld::elf;
namespace lld {
namespace elf {
namespace { namespace {
template <class ELFT> class MarkLive { template <class ELFT> class MarkLive {
public: public:
@ -141,7 +141,7 @@ void MarkLive<ELFT>::scanEhFrameSection(EhInputSection &eh,
if (firstRelI == (unsigned)-1) if (firstRelI == (unsigned)-1)
continue; continue;
if (read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) { if (endian::read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
// This is a CIE, we only need to worry about the first relocation. It is // This is a CIE, we only need to worry about the first relocation. It is
// known to point to the personality function. // known to point to the personality function.
resolveReloc(eh, rels[firstRelI], false); resolveReloc(eh, rels[firstRelI], false);
@ -291,6 +291,10 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
// GOT, which means that the ifunc must be available when the main partition is // GOT, which means that the ifunc must be available when the main partition is
// loaded) and TLS symbols (because we only know how to correctly process TLS // loaded) and TLS symbols (because we only know how to correctly process TLS
// relocations for the main partition). // relocations for the main partition).
//
// We also need to move sections whose names are C identifiers that are referred
// to from __start_/__stop_ symbols because there will only be one set of
// symbols for the whole program.
template <class ELFT> void MarkLive<ELFT>::moveToMain() { template <class ELFT> void MarkLive<ELFT>::moveToMain() {
for (InputFile *file : objectFiles) for (InputFile *file : objectFiles)
for (Symbol *s : file->getSymbols()) for (Symbol *s : file->getSymbols())
@ -299,13 +303,21 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
d->section->isLive()) d->section->isLive())
markSymbol(s); markSymbol(s);
for (InputSectionBase *sec : inputSections) {
if (!sec->isLive() || !isValidCIdentifier(sec->name))
continue;
if (symtab->find(("__start_" + sec->name).str()) ||
symtab->find(("__stop_" + sec->name).str()))
enqueue(sec, 0);
}
mark(); mark();
} }
// Before calling this function, Live bits are off for all // Before calling this function, Live bits are off for all
// input sections. This function make some or all of them on // input sections. This function make some or all of them on
// so that they are emitted to the output file. // so that they are emitted to the output file.
template <class ELFT> void elf::markLive() { template <class ELFT> void markLive() {
// If -gc-sections is not given, no sections are removed. // If -gc-sections is not given, no sections are removed.
if (!config->gcSections) { if (!config->gcSections) {
for (InputSectionBase *sec : inputSections) for (InputSectionBase *sec : inputSections)
@ -367,7 +379,10 @@ template <class ELFT> void elf::markLive() {
message("removing unused section " + toString(sec)); message("removing unused section " + toString(sec));
} }
template void elf::markLive<ELF32LE>(); template void markLive<ELF32LE>();
template void elf::markLive<ELF32BE>(); template void markLive<ELF32BE>();
template void elf::markLive<ELF64LE>(); template void markLive<ELF64LE>();
template void elf::markLive<ELF64BE>(); template void markLive<ELF64BE>();
} // namespace elf
} // namespace lld

View File

@ -171,6 +171,9 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">, def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">; HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
def fix_cortex_a8: F<"fix-cortex-a8">,
HelpText<"Apply fixes for ARM Cortex-A8 erratum 657417">;
// This option is intentionally hidden from the user as the implementation // This option is intentionally hidden from the user as the implementation
// is not complete. // is not complete.
def require_cet: F<"require-cet">; def require_cet: F<"require-cet">;
@ -306,7 +309,7 @@ def push_state: F<"push-state">,
def print_map: F<"print-map">, def print_map: F<"print-map">,
HelpText<"Print a link map to the standard output">; HelpText<"Print a link map to the standard output">;
defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">; defm reproduce: Eq<"reproduce", "Write a tar file containing input files and command line options to reproduce link">;
defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">; defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
@ -478,6 +481,7 @@ def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
HelpText<"Perform context senstive PGO instrumentation">; HelpText<"Perform context senstive PGO instrumentation">;
def lto_cs_profile_file: J<"lto-cs-profile-file=">, def lto_cs_profile_file: J<"lto-cs-profile-file=">,
HelpText<"Context sensitive profile file path">; HelpText<"Context sensitive profile file path">;
def lto_obj_path_eq: J<"lto-obj-path=">;
def lto_sample_profile: J<"lto-sample-profile=">, def lto_sample_profile: J<"lto-sample-profile=">,
HelpText<"Sample profile file path">; HelpText<"Sample profile file path">;
def disable_verify: F<"disable-verify">; def disable_verify: F<"disable-verify">;
@ -495,7 +499,12 @@ def save_temps: F<"save-temps">;
def thinlto_cache_dir: J<"thinlto-cache-dir=">, def thinlto_cache_dir: J<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">; HelpText<"Path to ThinLTO cached object file directory">;
defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">; defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
def thinlto_emit_imports_files: F<"thinlto-emit-imports-files">;
def thinlto_index_only: F<"thinlto-index-only">;
def thinlto_index_only_eq: J<"thinlto-index-only=">;
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">; def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
def thinlto_object_suffix_replace_eq: J<"thinlto-object-suffix-replace=">;
def thinlto_prefix_replace_eq: J<"thinlto-prefix-replace=">;
def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">; def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">;
def: F<"plugin-opt=debug-pass-manager">, def: F<"plugin-opt=debug-pass-manager">,
@ -509,19 +518,31 @@ def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">; def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
def: F<"plugin-opt=new-pass-manager">, def: F<"plugin-opt=new-pass-manager">,
Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">; Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
def plugin_opt_obj_path_eq: J<"plugin-opt=obj-path=">;
def: F<"plugin-opt=cs-profile-generate">, def: F<"plugin-opt=cs-profile-generate">,
Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">; Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">;
def: J<"plugin-opt=cs-profile-path=">, def: J<"plugin-opt=cs-profile-path=">,
Alias<lto_cs_profile_file>, HelpText<"Alias for -lto-cs-profile-file">; Alias<lto_cs_profile_file>, HelpText<"Alias for -lto-cs-profile-file">;
def: J<"plugin-opt=obj-path=">,
Alias<lto_obj_path_eq>,
HelpText<"Alias for -lto-obj-path=">;
def: J<"plugin-opt=sample-profile=">, def: J<"plugin-opt=sample-profile=">,
Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">; Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">; def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
def plugin_opt_thinlto_emit_imports_files: F<"plugin-opt=thinlto-emit-imports-files">; def: F<"plugin-opt=thinlto-emit-imports-files">,
def plugin_opt_thinlto_index_only: F<"plugin-opt=thinlto-index-only">; Alias<thinlto_emit_imports_files>,
def plugin_opt_thinlto_index_only_eq: J<"plugin-opt=thinlto-index-only=">; HelpText<"Alias for -thinlto-emit-imports-files">;
def plugin_opt_thinlto_object_suffix_replace_eq: J<"plugin-opt=thinlto-object-suffix-replace=">; def: F<"plugin-opt=thinlto-index-only">,
def plugin_opt_thinlto_prefix_replace_eq: J<"plugin-opt=thinlto-prefix-replace=">; Alias<thinlto_index_only>,
HelpText<"Alias for -thinlto-index-only">;
def: J<"plugin-opt=thinlto-index-only=">,
Alias<thinlto_index_only_eq>,
HelpText<"Alias for -thinlto-index-only=">;
def: J<"plugin-opt=thinlto-object-suffix-replace=">,
Alias<thinlto_object_suffix_replace_eq>,
HelpText<"Alias for -thinlto-object-suffix-replace=">;
def: J<"plugin-opt=thinlto-prefix-replace=">,
Alias<thinlto_prefix_replace_eq>,
HelpText<"Alias for -thinlto-prefix-replace=">;
// Ignore LTO plugin-related options. // Ignore LTO plugin-related options.
// clang -flto passes -plugin and -plugin-opt to the linker. This is required // clang -flto passes -plugin and -plugin-opt to the linker. This is required

View File

@ -27,9 +27,8 @@ using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
uint8_t *Out::bufferStart; uint8_t *Out::bufferStart;
uint8_t Out::first; uint8_t Out::first;
PhdrEntry *Out::tlsPhdr; PhdrEntry *Out::tlsPhdr;
@ -39,7 +38,7 @@ OutputSection *Out::preinitArray;
OutputSection *Out::initArray; OutputSection *Out::initArray;
OutputSection *Out::finiArray; OutputSection *Out::finiArray;
std::vector<OutputSection *> elf::outputSections; std::vector<OutputSection *> outputSections;
uint32_t OutputSection::getPhdrFlags() const { uint32_t OutputSection::getPhdrFlags() const {
uint32_t ret = 0; uint32_t ret = 0;
@ -83,12 +82,32 @@ static bool canMergeToProgbits(unsigned type) {
type == SHT_NOTE; type == SHT_NOTE;
} }
void OutputSection::addSection(InputSection *isec) { // Record that isec will be placed in the OutputSection. isec does not become
// permanent until finalizeInputSections() is called. The function should not be
// used after finalizeInputSections() is called. If you need to add an
// InputSection post finalizeInputSections(), then you must do the following:
//
// 1. Find or create an InputSectionDescription to hold InputSection.
// 2. Add the InputSection to the InputSectionDesciption::sections.
// 3. Call commitSection(isec).
void OutputSection::recordSection(InputSectionBase *isec) {
partition = isec->partition;
isec->parent = this;
if (sectionCommands.empty() ||
!isa<InputSectionDescription>(sectionCommands.back()))
sectionCommands.push_back(make<InputSectionDescription>(""));
auto *isd = cast<InputSectionDescription>(sectionCommands.back());
isd->sectionBases.push_back(isec);
}
// Update fields (type, flags, alignment, etc) according to the InputSection
// isec. Also check whether the InputSection flags and type are consistent with
// other InputSections.
void OutputSection::commitSection(InputSection *isec) {
if (!hasInputSections) { if (!hasInputSections) {
// If IS is the first section to be added to this section, // If IS is the first section to be added to this section,
// initialize Partition, Type, Entsize and flags from IS. // initialize type, entsize and flags from isec.
hasInputSections = true; hasInputSections = true;
partition = isec->partition;
type = isec->type; type = isec->type;
entsize = isec->entsize; entsize = isec->entsize;
flags = isec->flags; flags = isec->flags;
@ -110,6 +129,8 @@ void OutputSection::addSection(InputSection *isec) {
type = SHT_PROGBITS; type = SHT_PROGBITS;
} }
} }
if (noload)
type = SHT_NOBITS;
isec->parent = this; isec->parent = this;
uint64_t andMask = uint64_t andMask =
@ -118,6 +139,8 @@ void OutputSection::addSection(InputSection *isec) {
uint64_t andFlags = (flags & isec->flags) & andMask; uint64_t andFlags = (flags & isec->flags) & andMask;
uint64_t orFlags = (flags | isec->flags) & orMask; uint64_t orFlags = (flags | isec->flags) & orMask;
flags = andFlags | orFlags; flags = andFlags | orFlags;
if (nonAlloc)
flags &= ~(uint64_t)SHF_ALLOC;
alignment = std::max(alignment, isec->alignment); alignment = std::max(alignment, isec->alignment);
@ -126,15 +149,69 @@ void OutputSection::addSection(InputSection *isec) {
// set sh_entsize to 0. // set sh_entsize to 0.
if (entsize != isec->entsize) if (entsize != isec->entsize)
entsize = 0; entsize = 0;
}
if (!isec->assigned) { // This function scans over the InputSectionBase list sectionBases to create
isec->assigned = true; // InputSectionDescription::sections.
if (sectionCommands.empty() || //
!isa<InputSectionDescription>(sectionCommands.back())) // It removes MergeInputSections from the input section array and adds
sectionCommands.push_back(make<InputSectionDescription>("")); // new synthetic sections at the location of the first input section
auto *isd = cast<InputSectionDescription>(sectionCommands.back()); // that it replaces. It then finalizes each synthetic section in order
isd->sections.push_back(isec); // to compute an output offset for each piece of each input section.
void OutputSection::finalizeInputSections() {
std::vector<MergeSyntheticSection *> mergeSections;
for (BaseCommand *base : sectionCommands) {
auto *cmd = dyn_cast<InputSectionDescription>(base);
if (!cmd)
continue;
cmd->sections.reserve(cmd->sectionBases.size());
for (InputSectionBase *s : cmd->sectionBases) {
MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
if (!ms) {
cmd->sections.push_back(cast<InputSection>(s));
continue;
}
// We do not want to handle sections that are not alive, so just remove
// them instead of trying to merge.
if (!ms->isLive())
continue;
auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) {
// While we could create a single synthetic section for two different
// values of Entsize, it is better to take Entsize into consideration.
//
// With a single synthetic section no two pieces with different Entsize
// could be equal, so we may as well have two sections.
//
// Using Entsize in here also allows us to propagate it to the synthetic
// section.
//
// SHF_STRINGS section with different alignments should not be merged.
return sec->flags == ms->flags && sec->entsize == ms->entsize &&
(sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
});
if (i == mergeSections.end()) {
MergeSyntheticSection *syn =
createMergeSynthetic(name, ms->type, ms->flags, ms->alignment);
mergeSections.push_back(syn);
i = std::prev(mergeSections.end());
syn->entsize = ms->entsize;
cmd->sections.push_back(syn);
}
(*i)->addSection(ms);
}
// sectionBases should not be used from this point onwards. Clear it to
// catch misuses.
cmd->sectionBases.clear();
// Some input sections may be removed from the list after ICF.
for (InputSection *s : cmd->sections)
commitSection(s);
} }
for (auto *ms : mergeSections)
ms->finalizeContents();
} }
static void sortByOrder(MutableArrayRef<InputSection *> in, static void sortByOrder(MutableArrayRef<InputSection *> in,
@ -148,7 +225,7 @@ static void sortByOrder(MutableArrayRef<InputSection *> in,
in[i] = v[i].second; in[i] = v[i].second;
} }
uint64_t elf::getHeaderSize() { uint64_t getHeaderSize() {
if (config->oFormatBinary) if (config->oFormatBinary)
return 0; return 0;
return Out::elfHeader->size + Out::programHeaders->size; return Out::elfHeader->size + Out::programHeaders->size;
@ -368,7 +445,7 @@ void OutputSection::sortCtorsDtors() {
// If an input string is in the form of "foo.N" where N is a number, // If an input string is in the form of "foo.N" where N is a number,
// return N. Otherwise, returns 65536, which is one greater than the // return N. Otherwise, returns 65536, which is one greater than the
// lowest priority. // lowest priority.
int elf::getPriority(StringRef s) { int getPriority(StringRef s) {
size_t pos = s.rfind('.'); size_t pos = s.rfind('.');
if (pos == StringRef::npos) if (pos == StringRef::npos)
return 65536; return 65536;
@ -378,7 +455,7 @@ int elf::getPriority(StringRef s) {
return v; return v;
} }
std::vector<InputSection *> elf::getInputSections(OutputSection *os) { std::vector<InputSection *> getInputSections(OutputSection *os) {
std::vector<InputSection *> ret; std::vector<InputSection *> ret;
for (BaseCommand *base : os->sectionCommands) for (BaseCommand *base : os->sectionCommands)
if (auto *isd = dyn_cast<InputSectionDescription>(base)) if (auto *isd = dyn_cast<InputSectionDescription>(base))
@ -419,3 +496,6 @@ template void OutputSection::maybeCompress<ELF32LE>();
template void OutputSection::maybeCompress<ELF32BE>(); template void OutputSection::maybeCompress<ELF32BE>();
template void OutputSection::maybeCompress<ELF64LE>(); template void OutputSection::maybeCompress<ELF64LE>();
template void OutputSection::maybeCompress<ELF64BE>(); template void OutputSection::maybeCompress<ELF64BE>();
} // namespace elf
} // namespace lld

View File

@ -71,7 +71,9 @@ class OutputSection final : public BaseCommand, public SectionBase {
uint64_t addr = 0; uint64_t addr = 0;
uint32_t shName = 0; uint32_t shName = 0;
void addSection(InputSection *isec); void recordSection(InputSectionBase *isec);
void commitSection(InputSection *isec);
void finalizeInputSections();
// The following members are normally only used in linker scripts. // The following members are normally only used in linker scripts.
MemoryRegion *memRegion = nullptr; MemoryRegion *memRegion = nullptr;

View File

@ -62,9 +62,8 @@ using namespace llvm::ELF;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) { static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
for (BaseCommand *base : script->sectionCommands) for (BaseCommand *base : script->sectionCommands)
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) if (auto *cmd = dyn_cast<SymbolAssignment>(base))
@ -344,9 +343,9 @@ static bool needsPlt(RelExpr expr) {
// returns false for TLS variables even though they need GOT, because // returns false for TLS variables even though they need GOT, because
// TLS variables uses GOT differently than the regular variables. // TLS variables uses GOT differently than the regular variables.
static bool needsGot(RelExpr expr) { static bool needsGot(RelExpr expr) {
return oneof<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE, return oneof<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT>(
R_GOT_PC, R_GOTPLT>(expr); expr);
} }
// True if this expression is of the form Sym - X, where X is a position in the // True if this expression is of the form Sym - X, where X is a position in the
@ -369,7 +368,7 @@ static bool isRelExpr(RelExpr expr) {
static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym, static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
InputSectionBase &s, uint64_t relOff) { InputSectionBase &s, uint64_t relOff) {
// These expressions always compute a constant // These expressions always compute a constant
if (oneof<R_DTPREL, R_GOTPLT, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF, if (oneof<R_DTPREL, R_GOTPLT, R_GOT_OFF, R_TLSLD_GOT_OFF,
R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC, R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
@ -510,10 +509,8 @@ static void replaceWithDefined(Symbol &sym, SectionBase *sec, uint64_t value,
sym.gotIndex = old.gotIndex; sym.gotIndex = old.gotIndex;
sym.verdefIndex = old.verdefIndex; sym.verdefIndex = old.verdefIndex;
sym.ppc64BranchltIndex = old.ppc64BranchltIndex; sym.ppc64BranchltIndex = old.ppc64BranchltIndex;
sym.isPreemptible = true;
sym.exportDynamic = true; sym.exportDynamic = true;
sym.isUsedInRegularObj = true; sym.isUsedInRegularObj = true;
sym.used = true;
} }
// Reserve space in .bss or .bss.rel.ro for copy relocation. // Reserve space in .bss or .bss.rel.ro for copy relocation.
@ -569,10 +566,16 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &ss) {
bool isRO = isReadOnly<ELFT>(ss); bool isRO = isReadOnly<ELFT>(ss);
BssSection *sec = BssSection *sec =
make<BssSection>(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment); make<BssSection>(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment);
if (isRO) OutputSection *osec = (isRO ? in.bssRelRo : in.bss)->getParent();
in.bssRelRo->getParent()->addSection(sec);
else // At this point, sectionBases has been migrated to sections. Append sec to
in.bss->getParent()->addSection(sec); // sections.
if (osec->sectionCommands.empty() ||
!isa<InputSectionDescription>(osec->sectionCommands.back()))
osec->sectionCommands.push_back(make<InputSectionDescription>(""));
auto *isd = cast<InputSectionDescription>(osec->sectionCommands.back());
isd->sections.push_back(sec);
osec->commitSection(sec);
// Look through the DSO's dynamic symbol table for aliases and create a // Look through the DSO's dynamic symbol table for aliases and create a
// dynamic symbol for each one. This causes the copy relocation to correctly // dynamic symbol for each one. This causes the copy relocation to correctly
@ -693,8 +696,75 @@ struct UndefinedDiag {
static std::vector<UndefinedDiag> undefs; static std::vector<UndefinedDiag> undefs;
// Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns
// the suggested symbol, which is either in the symbol table, or in the same
// file of sym.
static const Symbol *getAlternativeSpelling(const Undefined &sym) {
// Build a map of local defined symbols.
DenseMap<StringRef, const Symbol *> map;
if (sym.file && !isa<SharedFile>(sym.file)) {
for (const Symbol *s : sym.file->getSymbols())
if (s->isLocal() && s->isDefined())
map.try_emplace(s->getName(), s);
}
auto suggest = [&](StringRef newName) -> const Symbol * {
// If defined locally.
if (const Symbol *s = map.lookup(newName))
return s;
// If in the symbol table and not undefined.
if (const Symbol *s = symtab->find(newName))
if (!s->isUndefined())
return s;
return nullptr;
};
// This loop enumerates all strings of Levenshtein distance 1 as typo
// correction candidates and suggests the one that exists as a non-undefined
// symbol.
StringRef name = sym.getName();
for (size_t i = 0, e = name.size(); i != e + 1; ++i) {
// Insert a character before name[i].
std::string newName = (name.substr(0, i) + "0" + name.substr(i)).str();
for (char c = '0'; c <= 'z'; ++c) {
newName[i] = c;
if (const Symbol *s = suggest(newName))
return s;
}
if (i == e)
break;
// Substitute name[i].
newName = name;
for (char c = '0'; c <= 'z'; ++c) {
newName[i] = c;
if (const Symbol *s = suggest(newName))
return s;
}
// Transpose name[i] and name[i+1]. This is of edit distance 2 but it is
// common.
if (i + 1 < e) {
newName[i] = name[i + 1];
newName[i + 1] = name[i];
if (const Symbol *s = suggest(newName))
return s;
}
// Delete name[i].
newName = (name.substr(0, i) + name.substr(i + 1)).str();
if (const Symbol *s = suggest(newName))
return s;
}
return nullptr;
}
template <class ELFT> template <class ELFT>
static void reportUndefinedSymbol(const UndefinedDiag &undef) { static void reportUndefinedSymbol(const UndefinedDiag &undef,
bool correctSpelling) {
Symbol &sym = *undef.sym; Symbol &sym = *undef.sym;
auto visibility = [&]() -> std::string { auto visibility = [&]() -> std::string {
@ -734,6 +804,14 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef) {
msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times") msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times")
.str(); .str();
if (correctSpelling)
if (const Symbol *corrected =
getAlternativeSpelling(cast<Undefined>(sym))) {
msg += "\n>>> did you mean: " + toString(*corrected);
if (corrected->file)
msg += "\n>>> defined in: " + toString(corrected->file);
}
if (sym.getName().startswith("_ZTV")) if (sym.getName().startswith("_ZTV"))
msg += "\nthe vtable symbol may be undefined because the class is missing " msg += "\nthe vtable symbol may be undefined because the class is missing "
"its key function (see https://lld.llvm.org/missingkeyfunction)"; "its key function (see https://lld.llvm.org/missingkeyfunction)";
@ -744,7 +822,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef) {
error(msg); error(msg);
} }
template <class ELFT> void elf::reportUndefinedSymbols() { template <class ELFT> void reportUndefinedSymbols() {
// Find the first "undefined symbol" diagnostic for each diagnostic, and // Find the first "undefined symbol" diagnostic for each diagnostic, and
// collect all "referenced from" lines at the first diagnostic. // collect all "referenced from" lines at the first diagnostic.
DenseMap<Symbol *, UndefinedDiag *> firstRef; DenseMap<Symbol *, UndefinedDiag *> firstRef;
@ -757,23 +835,21 @@ template <class ELFT> void elf::reportUndefinedSymbols() {
firstRef[undef.sym] = &undef; firstRef[undef.sym] = &undef;
} }
for (const UndefinedDiag &undef : undefs) { // Enable spell corrector for the first 2 diagnostics.
if (!undef.locs.empty()) for (auto it : enumerate(undefs))
reportUndefinedSymbol<ELFT>(undef); if (!it.value().locs.empty())
} reportUndefinedSymbol<ELFT>(it.value(), it.index() < 2);
undefs.clear(); undefs.clear();
} }
// Report an undefined symbol if necessary. // Report an undefined symbol if necessary.
// Returns true if the undefined symbol will produce an error message. // Returns true if the undefined symbol will produce an error message.
template <class ELFT>
static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec, static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
uint64_t offset) { uint64_t offset) {
if (!sym.isUndefined() || sym.isWeak()) if (!sym.isUndefined() || sym.isWeak())
return false; return false;
bool canBeExternal = !sym.isLocal() && sym.computeBinding() != STB_LOCAL && bool canBeExternal = !sym.isLocal() && sym.visibility == STV_DEFAULT;
sym.visibility == STV_DEFAULT;
if (config->unresolvedSymbols == UnresolvedPolicy::Ignore && canBeExternal) if (config->unresolvedSymbols == UnresolvedPolicy::Ignore && canBeExternal)
return false; return false;
@ -997,56 +1073,29 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
} }
} }
if (!canWrite && (config->isPic && !isRelExpr(expr))) { // When producing an executable, we can perform copy relocations (for
error( // STT_OBJECT) and canonical PLT (for STT_FUNC).
"can't create dynamic relocation " + toString(type) + " against " + if (!config->shared) {
(sym.getName().empty() ? "local symbol" : "symbol: " + toString(sym)) + if (!canDefineSymbolInExecutable(sym)) {
" in readonly segment; recompile object files with -fPIC " errorOrWarn("cannot preempt symbol: " + toString(sym) +
"or pass '-Wl,-z,notext' to allow text relocations in the output" + getLocation(sec, sym, offset));
getLocation(sec, sym, offset)); return;
return; }
}
if (sym.isObject()) {
// Copy relocations (for STT_OBJECT) and canonical PLT (for STT_FUNC) are only // Produce a copy relocation.
// possible in an executable. if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
// if (!config->zCopyreloc)
// Among R_ABS relocatoin types, symbolicRel has the same size as the word error("unresolvable relocation " + toString(type) +
// size. Others have fewer bits and may cause runtime overflow in -pie/-shared " against symbol '" + toString(*ss) +
// mode. Disallow them. "'; recompile with -fPIC or remove '-z nocopyreloc'" +
if (config->shared || getLocation(sec, sym, offset));
(config->pie && expr == R_ABS && type != target->symbolicRel)) { addCopyRelSymbol<ELFT>(*ss);
errorOrWarn( }
"relocation " + toString(type) + " cannot be used against " + sec.relocations.push_back({expr, type, offset, addend, &sym});
(sym.getName().empty() ? "local symbol" : "symbol " + toString(sym)) + return;
"; recompile with -fPIC" + getLocation(sec, sym, offset));
return;
}
// If the symbol is undefined we already reported any relevant errors.
if (sym.isUndefined())
return;
if (!canDefineSymbolInExecutable(sym)) {
error("cannot preempt symbol: " + toString(sym) +
getLocation(sec, sym, offset));
return;
}
if (sym.isObject()) {
// Produce a copy relocation.
if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
if (!config->zCopyreloc)
error("unresolvable relocation " + toString(type) +
" against symbol '" + toString(*ss) +
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
getLocation(sec, sym, offset));
addCopyRelSymbol<ELFT>(*ss);
} }
sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
}
if (sym.isFunc()) {
// This handles a non PIC program call to function in a shared library. In // This handles a non PIC program call to function in a shared library. In
// an ideal world, we could just report an error saying the relocation can // an ideal world, we could just report an error saying the relocation can
// overflow at runtime. In the real world with glibc, crt1.o has a // overflow at runtime. In the real world with glibc, crt1.o has a
@ -1074,18 +1123,37 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
// compiled without -fPIE/-fPIC and doesn't maintain ebx. // compiled without -fPIE/-fPIC and doesn't maintain ebx.
// * If a library definition gets preempted to the executable, it will have // * If a library definition gets preempted to the executable, it will have
// the wrong ebx value. // the wrong ebx value.
if (config->pie && config->emachine == EM_386) if (sym.isFunc()) {
errorOrWarn("symbol '" + toString(sym) + if (config->pie && config->emachine == EM_386)
"' cannot be preempted; recompile with -fPIE" + errorOrWarn("symbol '" + toString(sym) +
getLocation(sec, sym, offset)); "' cannot be preempted; recompile with -fPIE" +
if (!sym.isInPlt()) getLocation(sec, sym, offset));
addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym); if (!sym.isInPlt())
if (!sym.isDefined()) addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
replaceWithDefined( if (!sym.isDefined())
sym, in.plt, replaceWithDefined(
target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0); sym, in.plt,
sym.needsPltAddr = true; target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
sec.relocations.push_back({expr, type, offset, addend, &sym}); sym.needsPltAddr = true;
sec.relocations.push_back({expr, type, offset, addend, &sym});
return;
}
}
if (config->isPic) {
if (!canWrite && !isRelExpr(expr))
errorOrWarn(
"can't create dynamic relocation " + toString(type) + " against " +
(sym.getName().empty() ? "local symbol"
: "symbol: " + toString(sym)) +
" in readonly segment; recompile object files with -fPIC "
"or pass '-Wl,-z,notext' to allow text relocations in the output" +
getLocation(sec, sym, offset));
else
errorOrWarn(
"relocation " + toString(type) + " cannot be used against " +
(sym.getName().empty() ? "local symbol" : "symbol " + toString(sym)) +
"; recompile with -fPIC" + getLocation(sec, sym, offset));
return; return;
} }
@ -1093,15 +1161,6 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
getLocation(sec, sym, offset)); getLocation(sec, sym, offset));
} }
struct IRelativeReloc {
RelType type;
InputSectionBase *sec;
uint64_t offset;
Symbol *sym;
};
static std::vector<IRelativeReloc> iRelativeRelocs;
template <class ELFT, class RelTy> template <class ELFT, class RelTy>
static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i, static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
RelTy *end) { RelTy *end) {
@ -1125,7 +1184,7 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
// Error if the target symbol is undefined. Symbol index 0 may be used by // Error if the target symbol is undefined. Symbol index 0 may be used by
// marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them. // marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
if (symIndex != 0 && maybeReportUndefined<ELFT>(sym, sec, rel.r_offset)) if (symIndex != 0 && maybeReportUndefined(sym, sec, rel.r_offset))
return; return;
const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset; const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset;
@ -1269,12 +1328,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
// correctly, the IRELATIVE relocations are stored in an array which a // correctly, the IRELATIVE relocations are stored in an array which a
// statically linked executable's startup code must enumerate using the // statically linked executable's startup code must enumerate using the
// linker-defined symbols __rela?_iplt_{start,end}. // linker-defined symbols __rela?_iplt_{start,end}.
//
// - An absolute relocation to a non-preemptible ifunc (such as a global
// variable containing a pointer to the ifunc) needs to be relocated in
// the exact same way as a GOT entry, so we can avoid needing to make the
// PLT entry canonical by translating such relocations into IRELATIVE
// relocations in the relaIplt.
if (!sym.isInPlt()) { if (!sym.isInPlt()) {
// Create PLT and GOTPLT slots for the symbol. // Create PLT and GOTPLT slots for the symbol.
sym.isInIplt = true; sym.isInIplt = true;
@ -1291,17 +1344,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
*directSym); *directSym);
sym.pltIndex = directSym->pltIndex; sym.pltIndex = directSym->pltIndex;
} }
if (expr == R_ABS && addend == 0 && (sec.flags & SHF_WRITE)) {
// We might be able to represent this as an IRELATIVE. But we don't know
// yet whether some later relocation will make the symbol point to a
// canonical PLT, which would make this either a dynamic RELATIVE (PIC) or
// static (non-PIC) relocation. So we keep a record of the information
// required to process the relocation, and after scanRelocs() has been
// called on all relocations, the relocation is resolved by
// addIRelativeRelocs().
iRelativeRelocs.push_back({type, &sec, offset, &sym});
return;
}
if (needsGot(expr)) { if (needsGot(expr)) {
// Redirect GOT accesses to point to the Igot. // Redirect GOT accesses to point to the Igot.
// //
@ -1362,28 +1404,13 @@ static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
}); });
} }
template <class ELFT> void elf::scanRelocations(InputSectionBase &s) { template <class ELFT> void scanRelocations(InputSectionBase &s) {
if (s.areRelocsRela) if (s.areRelocsRela)
scanRelocs<ELFT>(s, s.relas<ELFT>()); scanRelocs<ELFT>(s, s.relas<ELFT>());
else else
scanRelocs<ELFT>(s, s.rels<ELFT>()); scanRelocs<ELFT>(s, s.rels<ELFT>());
} }
// Figure out which representation to use for any absolute relocs to
// non-preemptible ifuncs that we visited during scanRelocs().
void elf::addIRelativeRelocs() {
for (IRelativeReloc &r : iRelativeRelocs) {
if (r.sym->type == STT_GNU_IFUNC)
in.relaIplt->addReloc(
{target->iRelativeRel, r.sec, r.offset, true, r.sym, 0});
else if (config->isPic)
addRelativeReloc(r.sec, r.offset, r.sym, 0, R_ABS, r.type);
else
r.sec->relocations.push_back({R_ABS, r.type, r.offset, 0, r.sym});
}
iRelativeRelocs.clear();
}
static bool mergeCmp(const InputSection *a, const InputSection *b) { static bool mergeCmp(const InputSection *a, const InputSection *b) {
// std::merge requires a strict weak ordering. // std::merge requires a strict weak ordering.
if (a->outSecOff < b->outSecOff) if (a->outSecOff < b->outSecOff)
@ -1745,11 +1772,6 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
if (pass == 0 && target->getThunkSectionSpacing()) if (pass == 0 && target->getThunkSectionSpacing())
createInitialThunkSections(outputSections); createInitialThunkSections(outputSections);
// With Thunk Size much smaller than branch range we expect to
// converge quickly; if we get to 10 something has gone wrong.
if (pass == 10)
fatal("thunk creation not converged");
// Create all the Thunks and insert them into synthetic ThunkSections. The // Create all the Thunks and insert them into synthetic ThunkSections. The
// ThunkSections are later inserted back into InputSectionDescriptions. // ThunkSections are later inserted back into InputSectionDescriptions.
// We separate the creation of ThunkSections from the insertion of the // We separate the creation of ThunkSections from the insertion of the
@ -1809,11 +1831,14 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
return addressesChanged; return addressesChanged;
} }
template void elf::scanRelocations<ELF32LE>(InputSectionBase &); template void scanRelocations<ELF32LE>(InputSectionBase &);
template void elf::scanRelocations<ELF32BE>(InputSectionBase &); template void scanRelocations<ELF32BE>(InputSectionBase &);
template void elf::scanRelocations<ELF64LE>(InputSectionBase &); template void scanRelocations<ELF64LE>(InputSectionBase &);
template void elf::scanRelocations<ELF64BE>(InputSectionBase &); template void scanRelocations<ELF64BE>(InputSectionBase &);
template void elf::reportUndefinedSymbols<ELF32LE>(); template void reportUndefinedSymbols<ELF32LE>();
template void elf::reportUndefinedSymbols<ELF32BE>(); template void reportUndefinedSymbols<ELF32BE>();
template void elf::reportUndefinedSymbols<ELF64LE>(); template void reportUndefinedSymbols<ELF64LE>();
template void elf::reportUndefinedSymbols<ELF64BE>(); template void reportUndefinedSymbols<ELF64BE>();
} // namespace elf
} // namespace lld

View File

@ -82,7 +82,6 @@ enum RelExpr {
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC, R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
R_AARCH64_TLSDESC_PAGE, R_AARCH64_TLSDESC_PAGE,
R_ARM_SBREL, R_ARM_SBREL,
R_HEXAGON_GOT,
R_MIPS_GOTREL, R_MIPS_GOTREL,
R_MIPS_GOT_GP, R_MIPS_GOT_GP,
R_MIPS_GOT_GP_PC, R_MIPS_GOT_GP_PC,

View File

@ -36,9 +36,9 @@
#include "llvm/ADT/Twine.h" #include "llvm/ADT/Twine.h"
using namespace llvm; using namespace llvm;
using namespace lld;
using namespace lld::elf;
namespace lld {
namespace elf {
// Returns a whole line containing the current token. // Returns a whole line containing the current token.
StringRef ScriptLexer::getLine() { StringRef ScriptLexer::getLine() {
StringRef s = getCurrentMB().getBuffer(); StringRef s = getCurrentMB().getBuffer();
@ -298,3 +298,6 @@ MemoryBufferRef ScriptLexer::getCurrentMB() {
return mb; return mb;
llvm_unreachable("getCurrentMB: failed to find a token"); llvm_unreachable("getCurrentMB: failed to find a token");
} }
} // namespace elf
} // namespace lld

View File

@ -37,9 +37,9 @@
using namespace llvm; using namespace llvm;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace lld;
using namespace lld::elf;
namespace lld {
namespace elf {
namespace { namespace {
class ScriptParser final : ScriptLexer { class ScriptParser final : ScriptLexer {
public: public:
@ -720,7 +720,7 @@ Expr ScriptParser::readAssert() {
return [=] { return [=] {
if (!e().getValue()) if (!e().getValue())
error(msg); errorOrWarn(msg);
return script->getDot(); return script->getDot();
}; };
} }
@ -1268,7 +1268,7 @@ Expr ScriptParser::readPrimary() {
return [=] { return cmd->size; }; return [=] { return cmd->size; };
} }
if (tok == "SIZEOF_HEADERS") if (tok == "SIZEOF_HEADERS")
return [=] { return elf::getHeaderSize(); }; return [=] { return getHeaderSize(); };
// Tok is the dot. // Tok is the dot.
if (tok == ".") if (tok == ".")
@ -1344,16 +1344,10 @@ void ScriptParser::readAnonymousDeclaration() {
std::vector<SymbolVersion> locals; std::vector<SymbolVersion> locals;
std::vector<SymbolVersion> globals; std::vector<SymbolVersion> globals;
std::tie(locals, globals) = readSymbols(); std::tie(locals, globals) = readSymbols();
for (const SymbolVersion &pat : locals)
for (SymbolVersion v : locals) { config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat);
if (v.name == "*") for (const SymbolVersion &pat : globals)
config->defaultSymbolVersion = VER_NDX_LOCAL; config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(pat);
else
config->versionScriptLocals.push_back(v);
}
for (SymbolVersion v : globals)
config->versionScriptGlobals.push_back(v);
expect(";"); expect(";");
} }
@ -1365,22 +1359,14 @@ void ScriptParser::readVersionDeclaration(StringRef verStr) {
std::vector<SymbolVersion> locals; std::vector<SymbolVersion> locals;
std::vector<SymbolVersion> globals; std::vector<SymbolVersion> globals;
std::tie(locals, globals) = readSymbols(); std::tie(locals, globals) = readSymbols();
for (const SymbolVersion &pat : locals)
for (SymbolVersion v : locals) { config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat);
if (v.name == "*")
config->defaultSymbolVersion = VER_NDX_LOCAL;
else
config->versionScriptLocals.push_back(v);
}
// Create a new version definition and add that to the global symbols. // Create a new version definition and add that to the global symbols.
VersionDefinition ver; VersionDefinition ver;
ver.name = verStr; ver.name = verStr;
ver.globals = globals; ver.patterns = globals;
ver.id = config->versionDefinitions.size();
// User-defined version number starts from 2 because 0 and 1 are
// reserved for VER_NDX_LOCAL and VER_NDX_GLOBAL, respectively.
ver.id = config->versionDefinitions.size() + 2;
config->versionDefinitions.push_back(ver); config->versionDefinitions.push_back(ver);
// Each version may have a parent version. For example, "Ver2" // Each version may have a parent version. For example, "Ver2"
@ -1525,18 +1511,19 @@ std::pair<uint32_t, uint32_t> ScriptParser::readMemoryAttributes() {
return {flags, negFlags}; return {flags, negFlags};
} }
void elf::readLinkerScript(MemoryBufferRef mb) { void readLinkerScript(MemoryBufferRef mb) {
ScriptParser(mb).readLinkerScript(); ScriptParser(mb).readLinkerScript();
} }
void elf::readVersionScript(MemoryBufferRef mb) { void readVersionScript(MemoryBufferRef mb) {
ScriptParser(mb).readVersionScript(); ScriptParser(mb).readVersionScript();
} }
void elf::readDynamicList(MemoryBufferRef mb) { void readDynamicList(MemoryBufferRef mb) { ScriptParser(mb).readDynamicList(); }
ScriptParser(mb).readDynamicList();
}
void elf::readDefsym(StringRef name, MemoryBufferRef mb) { void readDefsym(StringRef name, MemoryBufferRef mb) {
ScriptParser(mb).readDefsym(name); ScriptParser(mb).readDefsym(name);
} }
} // namespace elf
} // namespace lld

View File

@ -27,10 +27,9 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
SymbolTable *symtab;
SymbolTable *elf::symtab;
void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
// Swap symbols as instructed by -wrap. // Swap symbols as instructed by -wrap.
@ -71,21 +70,26 @@ Symbol *SymbolTable::insert(StringRef name) {
Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
symVector.push_back(sym); symVector.push_back(sym);
// *sym was not initialized by a constructor. Fields that may get referenced
// when it is a placeholder must be initialized here.
sym->setName(name); sym->setName(name);
sym->symbolKind = Symbol::PlaceholderKind; sym->symbolKind = Symbol::PlaceholderKind;
sym->versionId = config->defaultSymbolVersion; sym->versionId = VER_NDX_GLOBAL;
sym->visibility = STV_DEFAULT; sym->visibility = STV_DEFAULT;
sym->isUsedInRegularObj = false; sym->isUsedInRegularObj = false;
sym->exportDynamic = false; sym->exportDynamic = false;
sym->inDynamicList = false;
sym->canInline = true; sym->canInline = true;
sym->referenced = false;
sym->traced = false;
sym->scriptDefined = false; sym->scriptDefined = false;
sym->partition = 1; sym->partition = 1;
return sym; return sym;
} }
Symbol *SymbolTable::addSymbol(const Symbol &New) { Symbol *SymbolTable::addSymbol(const Symbol &newSym) {
Symbol *sym = symtab->insert(New.getName()); Symbol *sym = symtab->insert(newSym.getName());
sym->resolve(New); sym->resolve(newSym);
return sym; return sym;
} }
@ -118,10 +122,7 @@ StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
for (Symbol *sym : symVector) { for (Symbol *sym : symVector) {
if (!sym->isDefined() && !sym->isCommon()) if (!sym->isDefined() && !sym->isCommon())
continue; continue;
if (Optional<std::string> s = demangleItanium(sym->getName())) (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
(*demangledSyms)[*s].push_back(sym);
else
(*demangledSyms)[sym->getName()].push_back(sym);
} }
} }
return *demangledSyms; return *demangledSyms;
@ -162,12 +163,8 @@ void SymbolTable::handleDynamicList() {
else else
syms = findByVersion(ver); syms = findByVersion(ver);
for (Symbol *b : syms) { for (Symbol *sym : syms)
if (!config->shared) sym->inDynamicList = true;
b->exportDynamic = true;
else if (b->includeInDynsym())
b->isPreemptible = true;
}
} }
} }
@ -192,7 +189,7 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
return "VER_NDX_LOCAL"; return "VER_NDX_LOCAL";
if (ver == VER_NDX_GLOBAL) if (ver == VER_NDX_GLOBAL)
return "VER_NDX_GLOBAL"; return "VER_NDX_GLOBAL";
return ("version '" + config->versionDefinitions[ver - 2].name + "'").str(); return ("version '" + config->versionDefinitions[ver].name + "'").str();
}; };
// Assign the version. // Assign the version.
@ -203,8 +200,12 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
if (sym->getName().contains('@')) if (sym->getName().contains('@'))
continue; continue;
if (sym->versionId == config->defaultSymbolVersion) // If the version has not been assigned, verdefIndex is -1. Use an arbitrary
// number (0) to indicate the version has been assigned.
if (sym->verdefIndex == UINT32_C(-1)) {
sym->verdefIndex = 0;
sym->versionId = versionId; sym->versionId = versionId;
}
if (sym->versionId == versionId) if (sym->versionId == versionId)
continue; continue;
@ -214,15 +215,14 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
} }
void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) { void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
if (!ver.hasWildcard)
return;
// Exact matching takes precendence over fuzzy matching, // Exact matching takes precendence over fuzzy matching,
// so we set a version to a symbol only if no version has been assigned // so we set a version to a symbol only if no version has been assigned
// to the symbol. This behavior is compatible with GNU. // to the symbol. This behavior is compatible with GNU.
for (Symbol *b : findAllByVersion(ver)) for (Symbol *sym : findAllByVersion(ver))
if (b->versionId == config->defaultSymbolVersion) if (sym->verdefIndex == UINT32_C(-1)) {
b->versionId = versionId; sym->verdefIndex = 0;
sym->versionId = versionId;
}
} }
// This function processes version scripts by updating the versionId // This function processes version scripts by updating the versionId
@ -233,26 +233,24 @@ void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
void SymbolTable::scanVersionScript() { void SymbolTable::scanVersionScript() {
// First, we assign versions to exact matching symbols, // First, we assign versions to exact matching symbols,
// i.e. version definitions not containing any glob meta-characters. // i.e. version definitions not containing any glob meta-characters.
for (SymbolVersion &ver : config->versionScriptGlobals)
assignExactVersion(ver, VER_NDX_GLOBAL, "global");
for (SymbolVersion &ver : config->versionScriptLocals)
assignExactVersion(ver, VER_NDX_LOCAL, "local");
for (VersionDefinition &v : config->versionDefinitions) for (VersionDefinition &v : config->versionDefinitions)
for (SymbolVersion &ver : v.globals) for (SymbolVersion &pat : v.patterns)
assignExactVersion(ver, v.id, v.name); assignExactVersion(pat, v.id, v.name);
// Next, we assign versions to fuzzy matching symbols, // Next, assign versions to wildcards that are not "*". Note that because the
// i.e. version definitions containing glob meta-characters. // last match takes precedence over previous matches, we iterate over the
for (SymbolVersion &ver : config->versionScriptGlobals) // definitions in the reverse order.
assignWildcardVersion(ver, VER_NDX_GLOBAL);
for (SymbolVersion &ver : config->versionScriptLocals)
assignWildcardVersion(ver, VER_NDX_LOCAL);
// Note that because the last match takes precedence over previous matches,
// we iterate over the definitions in the reverse order.
for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) for (VersionDefinition &v : llvm::reverse(config->versionDefinitions))
for (SymbolVersion &ver : v.globals) for (SymbolVersion &pat : v.patterns)
assignWildcardVersion(ver, v.id); if (pat.hasWildcard && pat.name != "*")
assignWildcardVersion(pat, v.id);
// Then, assign versions to "*". In GNU linkers they have lower priority than
// other wildcards.
for (VersionDefinition &v : config->versionDefinitions)
for (SymbolVersion &pat : v.patterns)
if (pat.hasWildcard && pat.name == "*")
assignWildcardVersion(pat, v.id);
// Symbol themselves might know their versions because symbols // Symbol themselves might know their versions because symbols
// can contain versions in the form of <name>@<version>. // can contain versions in the form of <name>@<version>.
@ -262,7 +260,10 @@ void SymbolTable::scanVersionScript() {
// isPreemptible is false at this point. To correctly compute the binding of a // isPreemptible is false at this point. To correctly compute the binding of a
// Defined (which is used by includeInDynsym()), we need to know if it is // Defined (which is used by includeInDynsym()), we need to know if it is
// VER_NDX_LOCAL or not. If defaultSymbolVersion is VER_NDX_LOCAL, we should // VER_NDX_LOCAL or not. Compute symbol versions before handling
// compute symbol versions before handling --dynamic-list. // --dynamic-list.
handleDynamicList(); handleDynamicList();
} }
} // namespace elf
} // namespace lld

View File

@ -43,7 +43,7 @@ class SymbolTable {
Symbol *insert(StringRef name); Symbol *insert(StringRef name);
Symbol *addSymbol(const Symbol &New); Symbol *addSymbol(const Symbol &newSym);
void scanVersionScript(); void scanVersionScript();

View File

@ -23,9 +23,20 @@ using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld; namespace lld {
using namespace lld::elf; // Returns a symbol for an error message.
static std::string demangle(StringRef symName) {
if (elf::config->demangle)
return demangleItanium(symName);
return symName;
}
std::string toString(const elf::Symbol &b) { return demangle(b.getName()); }
std::string toELFString(const Archive::Symbol &b) {
return demangle(b.getName());
}
namespace elf {
Defined *ElfSym::bss; Defined *ElfSym::bss;
Defined *ElfSym::etext1; Defined *ElfSym::etext1;
Defined *ElfSym::etext2; Defined *ElfSym::etext2;
@ -42,20 +53,6 @@ Defined *ElfSym::relaIpltEnd;
Defined *ElfSym::riscvGlobalPointer; Defined *ElfSym::riscvGlobalPointer;
Defined *ElfSym::tlsModuleBase; Defined *ElfSym::tlsModuleBase;
// Returns a symbol for an error message.
static std::string demangle(StringRef symName) {
if (config->demangle)
if (Optional<std::string> s = demangleItanium(symName))
return *s;
return symName;
}
namespace lld {
std::string toString(const Symbol &b) { return demangle(b.getName()); }
std::string toELFString(const Archive::Symbol &b) {
return demangle(b.getName());
}
} // namespace lld
static uint64_t getSymVA(const Symbol &sym, int64_t &addend) { static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
switch (sym.kind()) { switch (sym.kind()) {
case Symbol::DefinedKind: { case Symbol::DefinedKind: {
@ -227,7 +224,7 @@ void Symbol::parseSymbolVersion() {
if (isDefault) if (isDefault)
verstr = verstr.substr(1); verstr = verstr.substr(1);
for (VersionDefinition &ver : config->versionDefinitions) { for (const VersionDefinition &ver : namedVersionDefs()) {
if (ver.name != verstr) if (ver.name != verstr)
continue; continue;
@ -276,9 +273,8 @@ MemoryBufferRef LazyArchive::getMemberBuffer() {
uint8_t Symbol::computeBinding() const { uint8_t Symbol::computeBinding() const {
if (config->relocatable) if (config->relocatable)
return binding; return binding;
if (visibility != STV_DEFAULT && visibility != STV_PROTECTED) if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
return STB_LOCAL; versionId == VER_NDX_LOCAL)
if (versionId == VER_NDX_LOCAL && isDefined() && !isPreemptible)
return STB_LOCAL; return STB_LOCAL;
if (!config->gnuUnique && binding == STB_GNU_UNIQUE) if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
return STB_GLOBAL; return STB_GLOBAL;
@ -296,11 +292,11 @@ bool Symbol::includeInDynsym() const {
if (isUndefWeak() && config->pie && sharedFiles.empty()) if (isUndefWeak() && config->pie && sharedFiles.empty())
return false; return false;
return isUndefined() || isShared() || exportDynamic; return isUndefined() || isShared() || exportDynamic || inDynamicList;
} }
// Print out a log message for --trace-symbol. // Print out a log message for --trace-symbol.
void elf::printTraceSymbol(const Symbol *sym) { void printTraceSymbol(const Symbol *sym) {
std::string s; std::string s;
if (sym->isUndefined()) if (sym->isUndefined())
s = ": reference to "; s = ": reference to ";
@ -316,7 +312,7 @@ void elf::printTraceSymbol(const Symbol *sym) {
message(toString(sym->file) + s + sym->getName()); message(toString(sym->file) + s + sym->getName());
} }
void elf::maybeWarnUnorderableSymbol(const Symbol *sym) { void maybeWarnUnorderableSymbol(const Symbol *sym) {
if (!config->warnSymbolOrdering) if (!config->warnSymbolOrdering)
return; return;
@ -492,17 +488,13 @@ void Symbol::resolveUndefined(const Undefined &other) {
if (dyn_cast_or_null<SharedFile>(other.file)) if (dyn_cast_or_null<SharedFile>(other.file))
return; return;
if (isUndefined()) { if (isUndefined() || isShared()) {
// The binding may "upgrade" from weak to non-weak. // The binding will be weak if there is at least one reference and all are
if (other.binding != STB_WEAK) // weak. The binding has one opportunity to change to weak: if the first
// reference is weak.
if (other.binding != STB_WEAK || !referenced)
binding = other.binding; binding = other.binding;
} else if (auto *s = dyn_cast<SharedSymbol>(this)) { referenced = true;
// The binding of a SharedSymbol will be weak if there is at least one
// reference and all are weak. The binding has one opportunity to change to
// weak: if the first reference is weak.
if (other.binding != STB_WEAK || !s->referenced)
binding = other.binding;
s->referenced = true;
} }
} }
@ -560,7 +552,7 @@ int Symbol::compare(const Symbol *other) const {
auto *oldSym = cast<Defined>(this); auto *oldSym = cast<Defined>(this);
auto *newSym = cast<Defined>(other); auto *newSym = cast<Defined>(other);
if (other->file && isa<BitcodeFile>(other->file)) if (dyn_cast_or_null<BitcodeFile>(other->file))
return 0; return 0;
if (!oldSym->section && !newSym->section && oldSym->value == newSym->value && if (!oldSym->section && !newSym->section && oldSym->value == newSym->value &&
@ -658,6 +650,9 @@ void Symbol::resolveShared(const SharedSymbol &other) {
uint8_t bind = binding; uint8_t bind = binding;
replace(other); replace(other);
binding = bind; binding = bind;
cast<SharedSymbol>(this)->referenced = true; referenced = true;
} }
} }
} // namespace elf
} // namespace lld

View File

@ -21,6 +21,13 @@
#include "llvm/Object/ELF.h" #include "llvm/Object/ELF.h"
namespace lld { namespace lld {
std::string toString(const elf::Symbol &);
// 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 toELFString(const llvm::object::Archive::Symbol &);
namespace elf { namespace elf {
class CommonSymbol; class CommonSymbol;
class Defined; class Defined;
@ -30,16 +37,6 @@ class LazyObject;
class SharedSymbol; class SharedSymbol;
class Symbol; class Symbol;
class Undefined; class Undefined;
} // namespace elf
std::string toString(const elf::Symbol &);
// 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 toELFString(const elf::Archive::Symbol &);
namespace elf {
// This is a StringRef-like container that doesn't run strlen(). // This is a StringRef-like container that doesn't run strlen().
// //
@ -108,29 +105,43 @@ class Symbol {
// Symbol visibility. This is the computed minimum visibility of all // Symbol visibility. This is the computed minimum visibility of all
// observed non-DSO symbols. // observed non-DSO symbols.
uint8_t visibility : 2; unsigned visibility : 2;
// True if the symbol was used for linking and thus need to be added to the // True if the symbol was used for linking and thus need to be added to the
// output file's symbol table. This is true for all symbols except for // output file's symbol table. This is true for all symbols except for
// unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that // unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
// are unreferenced except by other bitcode objects. // are unreferenced except by other bitcode objects.
uint8_t isUsedInRegularObj : 1; unsigned isUsedInRegularObj : 1;
// If this flag is true and the symbol has protected or default visibility, it // Used by a Defined symbol with protected or default visibility, to record
// will appear in .dynsym. This flag is set by interposable DSO symbols in // whether it is required to be exported into .dynsym. This is set when any of
// executables, by most symbols in DSOs and executables built with // the following conditions hold:
// --export-dynamic, and by dynamic lists. //
uint8_t exportDynamic : 1; // - If there is an interposable symbol from a DSO.
// - If -shared or --export-dynamic is specified, any symbol in an object
// file/bitcode sets this property, unless suppressed by LTO
// canBeOmittedFromSymbolTable().
unsigned exportDynamic : 1;
// True if the symbol is in the --dynamic-list file. A Defined symbol with
// protected or default visibility with this property is required to be
// exported into .dynsym.
unsigned inDynamicList : 1;
// False if LTO shouldn't inline whatever this symbol points to. If a symbol // False if LTO shouldn't inline whatever this symbol points to. If a symbol
// is overwritten after LTO, LTO shouldn't inline the symbol because it // is overwritten after LTO, LTO shouldn't inline the symbol because it
// doesn't know the final contents of the symbol. // doesn't know the final contents of the symbol.
uint8_t canInline : 1; unsigned canInline : 1;
// Used by Undefined and SharedSymbol to track if there has been at least one
// undefined reference to the symbol. The binding may change to STB_WEAK if
// the first undefined reference from a non-shared object is weak.
unsigned referenced : 1;
// True if this symbol is specified by --trace-symbol option. // True if this symbol is specified by --trace-symbol option.
uint8_t traced : 1; unsigned traced : 1;
inline void replace(const Symbol &New); inline void replace(const Symbol &newSym);
bool includeInDynsym() const; bool includeInDynsym() const;
uint8_t computeBinding() const; uint8_t computeBinding() const;
@ -228,36 +239,37 @@ class Symbol {
: file(file), nameData(name.data), nameSize(name.size), binding(binding), : file(file), nameData(name.data), nameSize(name.size), binding(binding),
type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3), type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3),
isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind), isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind),
exportDynamic(isExportDynamic(k, visibility)), canInline(false), exportDynamic(isExportDynamic(k, visibility)), inDynamicList(false),
traced(false), needsPltAddr(false), isInIplt(false), gotInIgot(false), canInline(false), referenced(false), traced(false), needsPltAddr(false),
isPreemptible(false), used(!config->gcSections), needsTocRestore(false), isInIplt(false), gotInIgot(false), isPreemptible(false),
used(!config->gcSections), needsTocRestore(false),
scriptDefined(false) {} scriptDefined(false) {}
public: public:
// True the symbol should point to its PLT entry. // True the symbol should point to its PLT entry.
// For SharedSymbol only. // For SharedSymbol only.
uint8_t needsPltAddr : 1; unsigned needsPltAddr : 1;
// True if this symbol is in the Iplt sub-section of the Plt and the Igot // True if this symbol is in the Iplt sub-section of the Plt and the Igot
// sub-section of the .got.plt or .got. // sub-section of the .got.plt or .got.
uint8_t isInIplt : 1; unsigned isInIplt : 1;
// True if this symbol needs a GOT entry and its GOT entry is actually in // True if this symbol needs a GOT entry and its GOT entry is actually in
// Igot. This will be true only for certain non-preemptible ifuncs. // Igot. This will be true only for certain non-preemptible ifuncs.
uint8_t gotInIgot : 1; unsigned gotInIgot : 1;
// True if this symbol is preemptible at load time. // True if this symbol is preemptible at load time.
uint8_t isPreemptible : 1; unsigned isPreemptible : 1;
// True if an undefined or shared symbol is used from a live section. // True if an undefined or shared symbol is used from a live section.
uint8_t used : 1; unsigned used : 1;
// True if a call to this symbol needs to be followed by a restore of the // True if a call to this symbol needs to be followed by a restore of the
// PPC64 toc pointer. // PPC64 toc pointer.
uint8_t needsTocRestore : 1; unsigned needsTocRestore : 1;
// True if this symbol is defined by a linker script. // True if this symbol is defined by a linker script.
uint8_t scriptDefined : 1; unsigned scriptDefined : 1;
// The partition whose dynamic symbol table contains this symbol's definition. // The partition whose dynamic symbol table contains this symbol's definition.
uint8_t partition = 1; uint8_t partition = 1;
@ -367,11 +379,6 @@ class SharedSymbol : public Symbol {
uint64_t value; // st_value uint64_t value; // st_value
uint64_t size; // st_size uint64_t size; // st_size
uint32_t alignment; uint32_t alignment;
// This is true if there has been at least one undefined reference to the
// symbol. The binding may change to STB_WEAK if the first undefined reference
// is weak.
bool referenced = false;
}; };
// LazyArchive and LazyObject represent a symbols that is not yet in the link, // LazyArchive and LazyObject represent a symbols that is not yet in the link,
@ -511,7 +518,7 @@ size_t Symbol::getSymbolSize() const {
// replace() replaces "this" object with a given symbol by memcpy'ing // replace() replaces "this" object with a given symbol by memcpy'ing
// it over to "this". This function is called as a result of name // it over to "this". This function is called as a result of name
// resolution, e.g. to replace an undefind symbol with a defined symbol. // resolution, e.g. to replace an undefind symbol with a defined symbol.
void Symbol::replace(const Symbol &New) { void Symbol::replace(const Symbol &newSym) {
using llvm::ELF::STT_TLS; using llvm::ELF::STT_TLS;
// Symbols representing thread-local variables must be referenced by // Symbols representing thread-local variables must be referenced by
@ -519,22 +526,23 @@ void Symbol::replace(const Symbol &New) {
// non-TLS relocations, so there's a clear distinction between TLS // non-TLS relocations, so there's a clear distinction between TLS
// and non-TLS symbols. It is an error if the same symbol is defined // and non-TLS symbols. It is an error if the same symbol is defined
// as a TLS symbol in one file and as a non-TLS symbol in other file. // as a TLS symbol in one file and as a non-TLS symbol in other file.
if (symbolKind != PlaceholderKind && !isLazy() && !New.isLazy()) { if (symbolKind != PlaceholderKind && !isLazy() && !newSym.isLazy() &&
bool tlsMismatch = (type == STT_TLS && New.type != STT_TLS) || (type == STT_TLS) != (newSym.type == STT_TLS))
(type != STT_TLS && New.type == STT_TLS); error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
if (tlsMismatch) toString(newSym.file) + "\n>>> defined in " + toString(file));
error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
toString(New.file) + "\n>>> defined in " + toString(file));
}
Symbol old = *this; Symbol old = *this;
memcpy(this, &New, New.getSymbolSize()); memcpy(this, &newSym, newSym.getSymbolSize());
// old may be a placeholder. The referenced fields must be initialized in
// SymbolTable::insert.
versionId = old.versionId; versionId = old.versionId;
visibility = old.visibility; visibility = old.visibility;
isUsedInRegularObj = old.isUsedInRegularObj; isUsedInRegularObj = old.isUsedInRegularObj;
exportDynamic = old.exportDynamic; exportDynamic = old.exportDynamic;
inDynamicList = old.inDynamicList;
canInline = old.canInline; canInline = old.canInline;
referenced = old.referenced;
traced = old.traced; traced = old.traced;
isPreemptible = old.isPreemptible; isPreemptible = old.isPreemptible;
scriptDefined = old.scriptDefined; scriptDefined = old.scriptDefined;

View File

@ -45,13 +45,12 @@ using namespace llvm::ELF;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::support; using namespace llvm::support;
using namespace lld;
using namespace lld::elf;
using llvm::support::endian::read32le; using llvm::support::endian::read32le;
using llvm::support::endian::write32le; using llvm::support::endian::write32le;
using llvm::support::endian::write64le; using llvm::support::endian::write64le;
namespace lld {
namespace elf {
constexpr size_t MergeNoTailSection::numShards; constexpr size_t MergeNoTailSection::numShards;
static uint64_t readUint(uint8_t *buf) { static uint64_t readUint(uint8_t *buf) {
@ -82,7 +81,7 @@ static ArrayRef<uint8_t> getVersion() {
// With this feature, you can identify LLD-generated binaries easily // With this feature, you can identify LLD-generated binaries easily
// by "readelf --string-dump .comment <file>". // by "readelf --string-dump .comment <file>".
// The returned object is a mergeable string section. // The returned object is a mergeable string section.
MergeInputSection *elf::createCommentSection() { MergeInputSection *createCommentSection() {
return make<MergeInputSection>(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1, return make<MergeInputSection>(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1,
getVersion(), ".comment"); getVersion(), ".comment");
} }
@ -138,7 +137,7 @@ MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
flags.ases |= s->ases; flags.ases |= s->ases;
flags.flags1 |= s->flags1; flags.flags1 |= s->flags1;
flags.flags2 |= s->flags2; flags.flags2 |= s->flags2;
flags.fp_abi = elf::getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename); flags.fp_abi = getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename);
}; };
if (create) if (create)
@ -252,19 +251,17 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
return make<MipsReginfoSection<ELFT>>(reginfo); return make<MipsReginfoSection<ELFT>>(reginfo);
} }
InputSection *elf::createInterpSection() { InputSection *createInterpSection() {
// StringSaver guarantees that the returned string ends with '\0'. // StringSaver guarantees that the returned string ends with '\0'.
StringRef s = saver.save(config->dynamicLinker); StringRef s = saver.save(config->dynamicLinker);
ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1}; ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
auto *sec = make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents, return make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents,
".interp"); ".interp");
sec->markLive();
return sec;
} }
Defined *elf::addSyntheticLocal(StringRef name, uint8_t type, uint64_t value, Defined *addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
uint64_t size, InputSectionBase &section) { uint64_t size, InputSectionBase &section) {
auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type, auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type,
value, size, &section); value, size, &section);
if (in.symTab) if (in.symTab)
@ -402,7 +399,7 @@ bool EhFrameSection::isFdeLive(EhSectionPiece &fde, ArrayRef<RelTy> rels) {
// a list of FDEs. This function searches an existing CIE or create a new // a list of FDEs. This function searches an existing CIE or create a new
// one and associates FDEs to the CIE. // one and associates FDEs to the CIE.
template <class ELFT, class RelTy> template <class ELFT, class RelTy>
void EhFrameSection::addSectionAux(EhInputSection *sec, ArrayRef<RelTy> rels) { void EhFrameSection::addRecords(EhInputSection *sec, ArrayRef<RelTy> rels) {
offsetToCie.clear(); offsetToCie.clear();
for (EhSectionPiece &piece : sec->pieces) { for (EhSectionPiece &piece : sec->pieces) {
// The empty record is the end marker. // The empty record is the end marker.
@ -428,8 +425,17 @@ void EhFrameSection::addSectionAux(EhInputSection *sec, ArrayRef<RelTy> rels) {
} }
} }
template <class ELFT> void EhFrameSection::addSection(InputSectionBase *c) { template <class ELFT>
auto *sec = cast<EhInputSection>(c); void EhFrameSection::addSectionAux(EhInputSection *sec) {
if (!sec->isLive())
return;
if (sec->areRelocsRela)
addRecords<ELFT>(sec, sec->template relas<ELFT>());
else
addRecords<ELFT>(sec, sec->template rels<ELFT>());
}
void EhFrameSection::addSection(EhInputSection *sec) {
sec->parent = this; sec->parent = this;
alignment = std::max(alignment, sec->alignment); alignment = std::max(alignment, sec->alignment);
@ -437,14 +443,6 @@ template <class ELFT> void EhFrameSection::addSection(InputSectionBase *c) {
for (auto *ds : sec->dependentSections) for (auto *ds : sec->dependentSections)
dependentSections.push_back(ds); dependentSections.push_back(ds);
if (sec->pieces.empty())
return;
if (sec->areRelocsRela)
addSectionAux<ELFT>(sec, sec->template relas<ELFT>());
else
addSectionAux<ELFT>(sec, sec->template rels<ELFT>());
} }
static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) { static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
@ -461,6 +459,28 @@ static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
void EhFrameSection::finalizeContents() { void EhFrameSection::finalizeContents() {
assert(!this->size); // Not finalized. assert(!this->size); // Not finalized.
switch (config->ekind) {
case ELFNoneKind:
llvm_unreachable("invalid ekind");
case ELF32LEKind:
for (EhInputSection *sec : sections)
addSectionAux<ELF32LE>(sec);
break;
case ELF32BEKind:
for (EhInputSection *sec : sections)
addSectionAux<ELF32BE>(sec);
break;
case ELF64LEKind:
for (EhInputSection *sec : sections)
addSectionAux<ELF64LE>(sec);
break;
case ELF64BEKind:
for (EhInputSection *sec : sections)
addSectionAux<ELF64BE>(sec);
break;
}
size_t off = 0; size_t off = 0;
for (CieRecord *rec : cieRecords) { for (CieRecord *rec : cieRecords) {
rec->cie->outputOff = off; rec->cie->outputOff = off;
@ -1162,10 +1182,12 @@ void StringTableSection::writeTo(uint8_t *buf) {
} }
} }
// Returns the number of version definition entries. Because the first entry // Returns the number of entries in .gnu.version_d: the number of
// is for the version definition itself, it is the number of versioned symbols // non-VER_NDX_LOCAL-non-VER_NDX_GLOBAL definitions, plus 1.
// plus one. Note that we don't support multiple versions yet. // Note that we don't support vd_cnt > 1 yet.
static unsigned getVerDefNum() { return config->versionDefinitions.size() + 1; } static unsigned getVerDefNum() {
return namedVersionDefs().size() + 1;
}
template <class ELFT> template <class ELFT>
DynamicSection<ELFT>::DynamicSection() DynamicSection<ELFT>::DynamicSection()
@ -1218,6 +1240,25 @@ void DynamicSection<ELFT>::addSym(int32_t tag, Symbol *sym) {
entries.push_back({tag, [=] { return sym->getVA(); }}); entries.push_back({tag, [=] { return sym->getVA(); }});
} }
// The output section .rela.dyn may include these synthetic sections:
//
// - part.relaDyn
// - in.relaIplt: this is included if in.relaIplt is named .rela.dyn
// - in.relaPlt: this is included if a linker script places .rela.plt inside
// .rela.dyn
//
// DT_RELASZ is the total size of the included sections.
static std::function<uint64_t()> addRelaSz(RelocationBaseSection *relaDyn) {
return [=]() {
size_t size = relaDyn->getSize();
if (in.relaIplt->getParent() == relaDyn->getParent())
size += in.relaIplt->getSize();
if (in.relaPlt->getParent() == relaDyn->getParent())
size += in.relaPlt->getSize();
return size;
};
}
// A Linker script may assign the RELA relocation sections to the same // A Linker script may assign the RELA relocation sections to the same
// output section. When this occurs we cannot just use the OutputSection // output section. When this occurs we cannot just use the OutputSection
// Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to // Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to
@ -1232,7 +1273,7 @@ static uint64_t addPltRelSz() {
// Add remaining entries to complete .dynamic contents. // Add remaining entries to complete .dynamic contents.
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() { template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
elf::Partition &part = getPartition(); Partition &part = getPartition();
bool isMain = part.name.empty(); bool isMain = part.name.empty();
for (StringRef s : config->filterList) for (StringRef s : config->filterList)
@ -1306,9 +1347,11 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
if (OutputSection *sec = part.dynStrTab->getParent()) if (OutputSection *sec = part.dynStrTab->getParent())
this->link = sec->sectionIndex; this->link = sec->sectionIndex;
if (part.relaDyn->isNeeded()) { if (part.relaDyn->isNeeded() ||
(in.relaIplt->isNeeded() &&
part.relaDyn->getParent() == in.relaIplt->getParent())) {
addInSec(part.relaDyn->dynamicTag, part.relaDyn); addInSec(part.relaDyn->dynamicTag, part.relaDyn);
addSize(part.relaDyn->sizeDynamicTag, part.relaDyn->getParent()); entries.push_back({part.relaDyn->sizeDynamicTag, addRelaSz(part.relaDyn)});
bool isRela = config->isRela; bool isRela = config->isRela;
addInt(isRela ? DT_RELAENT : DT_RELENT, addInt(isRela ? DT_RELAENT : DT_RELENT,
@ -1679,6 +1722,56 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
relativeGroups.emplace_back(std::move(group)); relativeGroups.emplace_back(std::move(group));
} }
// For non-relative relocations, we would like to:
// 1. Have relocations with the same symbol offset to be consecutive, so
// that the runtime linker can speed-up symbol lookup by implementing an
// 1-entry cache.
// 2. Group relocations by r_info to reduce the size of the relocation
// section.
// Since the symbol offset is the high bits in r_info, sorting by r_info
// allows us to do both.
//
// For Rela, we also want to sort by r_addend when r_info is the same. This
// enables us to group by r_addend as well.
llvm::stable_sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
if (a.r_info != b.r_info)
return a.r_info < b.r_info;
if (config->isRela)
return a.r_addend < b.r_addend;
return false;
});
// Group relocations with the same r_info. Note that each group emits a group
// header and that may make the relocation section larger. It is hard to
// estimate the size of a group header as the encoded size of that varies
// based on r_info. However, we can approximate this trade-off by the number
// of values encoded. Each group header contains 3 values, and each relocation
// in a group encodes one less value, as compared to when it is not grouped.
// Therefore, we only group relocations if there are 3 or more of them with
// the same r_info.
//
// For Rela, the addend for most non-relative relocations is zero, and thus we
// can usually get a smaller relocation section if we group relocations with 0
// addend as well.
std::vector<Elf_Rela> ungroupedNonRelatives;
std::vector<std::vector<Elf_Rela>> nonRelativeGroups;
for (auto i = nonRelatives.begin(), e = nonRelatives.end(); i != e;) {
auto j = i + 1;
while (j != e && i->r_info == j->r_info &&
(!config->isRela || i->r_addend == j->r_addend))
++j;
if (j - i < 3 || (config->isRela && i->r_addend != 0))
ungroupedNonRelatives.insert(ungroupedNonRelatives.end(), i, j);
else
nonRelativeGroups.emplace_back(i, j);
i = j;
}
// Sort ungrouped relocations by offset to minimize the encoded length.
llvm::sort(ungroupedNonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
return a.r_offset < b.r_offset;
});
unsigned hasAddendIfRela = unsigned hasAddendIfRela =
config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0; config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
@ -1733,14 +1826,23 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
} }
} }
// Finally the non-relative relocations. // Grouped non-relatives.
llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) { for (ArrayRef<Elf_Rela> g : nonRelativeGroups) {
return a.r_offset < b.r_offset; add(g.size());
}); add(RELOCATION_GROUPED_BY_INFO_FLAG);
if (!nonRelatives.empty()) { add(g[0].r_info);
add(nonRelatives.size()); for (const Elf_Rela &r : g) {
add(r.r_offset - offset);
offset = r.r_offset;
}
addend = 0;
}
// Finally the ungrouped non-relative relocations.
if (!ungroupedNonRelatives.empty()) {
add(ungroupedNonRelatives.size());
add(hasAddendIfRela); add(hasAddendIfRela);
for (Elf_Rela &r : nonRelatives) { for (Elf_Rela &r : ungroupedNonRelatives) {
add(r.r_offset - offset); add(r.r_offset - offset);
offset = r.r_offset; offset = r.r_offset;
add(r.r_info); add(r.r_info);
@ -1852,6 +1954,14 @@ template <class ELFT> bool RelrSection<ELFT>::updateAllocSize() {
} }
} }
// Don't allow the section to shrink; otherwise the size of the section can
// oscillate infinitely. Trailing 1s do not decode to more relocations.
if (relrRelocs.size() < oldSize) {
log(".relr.dyn needs " + Twine(oldSize - relrRelocs.size()) +
" padding word(s)");
relrRelocs.resize(oldSize, Elf_Relr(1));
}
return relrRelocs.size() != oldSize; return relrRelocs.size() != oldSize;
} }
@ -2452,6 +2562,10 @@ readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
uint32_t cuIdx = 0; uint32_t cuIdx = 0;
for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) { for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) {
if (Error e = cu->tryExtractDIEsIfNeeded(false)) {
error(toString(sec) + ": " + toString(std::move(e)));
return {};
}
Expected<DWARFAddressRangesVector> ranges = cu->collectAddressRanges(); Expected<DWARFAddressRangesVector> ranges = cu->collectAddressRanges();
if (!ranges) { if (!ranges) {
error(toString(sec) + ": " + toString(ranges.takeError())); error(toString(sec) + ": " + toString(ranges.takeError()));
@ -2481,9 +2595,9 @@ readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
template <class ELFT> template <class ELFT>
static std::vector<GdbIndexSection::NameAttrEntry> static std::vector<GdbIndexSection::NameAttrEntry>
readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj, readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
const std::vector<GdbIndexSection::CuEntry> &cUs) { const std::vector<GdbIndexSection::CuEntry> &cus) {
const DWARFSection &pubNames = obj.getGnuPubNamesSection(); const DWARFSection &pubNames = obj.getGnuPubnamesSection();
const DWARFSection &pubTypes = obj.getGnuPubTypesSection(); const DWARFSection &pubTypes = obj.getGnuPubtypesSection();
std::vector<GdbIndexSection::NameAttrEntry> ret; std::vector<GdbIndexSection::NameAttrEntry> ret;
for (const DWARFSection *pub : {&pubNames, &pubTypes}) { for (const DWARFSection *pub : {&pubNames, &pubTypes}) {
@ -2493,12 +2607,11 @@ readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
// don't know how many compilation units precede this object to compute // don't know how many compilation units precede this object to compute
// cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add // cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add
// the number of preceding compilation units later. // the number of preceding compilation units later.
uint32_t i = uint32_t i = llvm::partition_point(cus,
lower_bound(cUs, set.Offset, [&](GdbIndexSection::CuEntry cu) {
[](GdbIndexSection::CuEntry cu, uint32_t offset) { return cu.cuOffset < set.Offset;
return cu.cuOffset < offset; }) -
}) - cus.begin();
cUs.begin();
for (const DWARFDebugPubTable::Entry &ent : set.Entries) for (const DWARFDebugPubTable::Entry &ent : set.Entries)
ret.push_back({{ent.Name, computeGdbHash(ent.Name)}, ret.push_back({{ent.Name, computeGdbHash(ent.Name)},
(ent.Descriptor.toBits() << 24) | i}); (ent.Descriptor.toBits() << 24) | i});
@ -2603,7 +2716,7 @@ template <class ELFT> GdbIndexSection *GdbIndexSection::create() {
parallelForEachN(0, sections.size(), [&](size_t i) { parallelForEachN(0, sections.size(), [&](size_t i) {
ObjFile<ELFT> *file = sections[i]->getFile<ELFT>(); ObjFile<ELFT> *file = sections[i]->getFile<ELFT>();
DWARFContext dwarf(make_unique<LLDDwarfObj<ELFT>>(file)); DWARFContext dwarf(std::make_unique<LLDDwarfObj<ELFT>>(file));
chunks[i].sec = sections[i]; chunks[i].sec = sections[i];
chunks[i].compilationUnits = readCuList(dwarf); chunks[i].compilationUnits = readCuList(dwarf);
@ -2750,7 +2863,7 @@ StringRef VersionDefinitionSection::getFileDefName() {
void VersionDefinitionSection::finalizeContents() { void VersionDefinitionSection::finalizeContents() {
fileDefNameOff = getPartition().dynStrTab->addString(getFileDefName()); fileDefNameOff = getPartition().dynStrTab->addString(getFileDefName());
for (VersionDefinition &v : config->versionDefinitions) for (const VersionDefinition &v : namedVersionDefs())
verDefNameOffs.push_back(getPartition().dynStrTab->addString(v.name)); verDefNameOffs.push_back(getPartition().dynStrTab->addString(v.name));
if (OutputSection *sec = getPartition().dynStrTab->getParent()) if (OutputSection *sec = getPartition().dynStrTab->getParent())
@ -2784,7 +2897,7 @@ void VersionDefinitionSection::writeTo(uint8_t *buf) {
writeOne(buf, 1, getFileDefName(), fileDefNameOff); writeOne(buf, 1, getFileDefName(), fileDefNameOff);
auto nameOffIt = verDefNameOffs.begin(); auto nameOffIt = verDefNameOffs.begin();
for (VersionDefinition &v : config->versionDefinitions) { for (const VersionDefinition &v : namedVersionDefs()) {
buf += EntrySize; buf += EntrySize;
writeOne(buf, v.id, v.name, *nameOffIt++); writeOne(buf, v.id, v.name, *nameOffIt++);
} }
@ -2826,7 +2939,7 @@ bool VersionTableSection::isNeeded() const {
return getPartition().verDef || getPartition().verNeed->isNeeded(); return getPartition().verDef || getPartition().verNeed->isNeeded();
} }
void elf::addVerneed(Symbol *ss) { void addVerneed(Symbol *ss) {
auto &file = cast<SharedFile>(*ss->file); auto &file = cast<SharedFile>(*ss->file);
if (ss->verdefIndex == VER_NDX_GLOBAL) { if (ss->verdefIndex == VER_NDX_GLOBAL) {
ss->versionId = VER_NDX_GLOBAL; ss->versionId = VER_NDX_GLOBAL;
@ -3009,17 +3122,16 @@ void MergeNoTailSection::finalizeContents() {
}); });
} }
static MergeSyntheticSection *createMergeSynthetic(StringRef name, MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
uint32_t type, uint64_t flags,
uint64_t flags, uint32_t alignment) {
uint32_t alignment) {
bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2; bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2;
if (shouldTailMerge) if (shouldTailMerge)
return make<MergeTailSection>(name, type, flags, alignment); return make<MergeTailSection>(name, type, flags, alignment);
return make<MergeNoTailSection>(name, type, flags, alignment); return make<MergeNoTailSection>(name, type, flags, alignment);
} }
template <class ELFT> void elf::splitSections() { template <class ELFT> void splitSections() {
// splitIntoPieces needs to be called on each MergeInputSection // splitIntoPieces needs to be called on each MergeInputSection
// before calling finalizeContents(). // before calling finalizeContents().
parallelForEach(inputSections, [](InputSectionBase *sec) { parallelForEach(inputSections, [](InputSectionBase *sec) {
@ -3030,63 +3142,6 @@ template <class ELFT> void elf::splitSections() {
}); });
} }
// This function scans over the inputsections to create mergeable
// synthetic sections.
//
// It removes MergeInputSections from the input section array and adds
// new synthetic sections at the location of the first input section
// that it replaces. It then finalizes each synthetic section in order
// to compute an output offset for each piece of each input section.
void elf::mergeSections() {
std::vector<MergeSyntheticSection *> mergeSections;
for (InputSectionBase *&s : inputSections) {
MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
if (!ms)
continue;
// We do not want to handle sections that are not alive, so just remove
// them instead of trying to merge.
if (!ms->isLive()) {
s = nullptr;
continue;
}
StringRef outsecName = getOutputSectionName(ms);
auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) {
// While we could create a single synthetic section for two different
// values of Entsize, it is better to take Entsize into consideration.
//
// With a single synthetic section no two pieces with different Entsize
// could be equal, so we may as well have two sections.
//
// Using Entsize in here also allows us to propagate it to the synthetic
// section.
//
// SHF_STRINGS section with different alignments should not be merged.
return sec->name == outsecName && sec->flags == ms->flags &&
sec->entsize == ms->entsize &&
(sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
});
if (i == mergeSections.end()) {
MergeSyntheticSection *syn =
createMergeSynthetic(outsecName, ms->type, ms->flags, ms->alignment);
mergeSections.push_back(syn);
i = std::prev(mergeSections.end());
s = syn;
syn->entsize = ms->entsize;
} else {
s = nullptr;
}
(*i)->addSection(ms);
}
for (auto *ms : mergeSections)
ms->finalizeContents();
std::vector<InputSectionBase *> &v = inputSections;
v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
}
MipsRldMapSection::MipsRldMapSection() MipsRldMapSection::MipsRldMapSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize, : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
".rld_map") {} ".rld_map") {}
@ -3102,17 +3157,23 @@ static InputSection *findExidxSection(InputSection *isec) {
return nullptr; return nullptr;
} }
static bool isValidExidxSectionDep(InputSection *isec) {
return (isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) &&
isec->getSize() > 0;
}
bool ARMExidxSyntheticSection::addSection(InputSection *isec) { bool ARMExidxSyntheticSection::addSection(InputSection *isec) {
if (isec->type == SHT_ARM_EXIDX) { if (isec->type == SHT_ARM_EXIDX) {
exidxSections.push_back(isec); if (InputSection* dep = isec->getLinkOrderDep())
return true; if (isValidExidxSectionDep(dep)) {
exidxSections.push_back(isec);
return true;
}
return false;
} }
if ((isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) && if (isValidExidxSectionDep(isec)) {
isec->getSize() > 0) {
executableSections.push_back(isec); executableSections.push_back(isec);
if (empty && findExidxSection(isec))
empty = false;
return false; return false;
} }
@ -3182,17 +3243,14 @@ static bool isDuplicateArmExidxSec(InputSection *prev, InputSection *cur) {
// with the highest address and any InputSections that have mergeable // with the highest address and any InputSections that have mergeable
// .ARM.exidx table entries are removed from it. // .ARM.exidx table entries are removed from it.
void ARMExidxSyntheticSection::finalizeContents() { void ARMExidxSyntheticSection::finalizeContents() {
if (script->hasSectionsCommand) { // The executableSections and exidxSections that we use to derive the final
// The executableSections and exidxSections that we use to derive the // contents of this SyntheticSection are populated before
// final contents of this SyntheticSection are populated before the // processSectionCommands() and ICF. A /DISCARD/ entry in SECTIONS command or
// linker script assigns InputSections to OutputSections. The linker script // ICF may remove executable InputSections and their dependent .ARM.exidx
// SECTIONS command may have a /DISCARD/ entry that removes executable // section that we recorded earlier.
// InputSections and their dependent .ARM.exidx section that we recorded auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); };
// earlier. llvm::erase_if(executableSections, isDiscarded);
auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); }; llvm::erase_if(exidxSections, isDiscarded);
llvm::erase_if(executableSections, isDiscarded);
llvm::erase_if(exidxSections, isDiscarded);
}
// Sort the executable sections that may or may not have associated // Sort the executable sections that may or may not have associated
// .ARM.exidx sections by order of ascending address. This requires the // .ARM.exidx sections by order of ascending address. This requires the
@ -3282,6 +3340,12 @@ void ARMExidxSyntheticSection::writeTo(uint8_t *buf) {
assert(size == offset + 8); assert(size == offset + 8);
} }
bool ARMExidxSyntheticSection::isNeeded() const {
return llvm::find_if(exidxSections, [](InputSection *isec) {
return isec->isLive();
}) != exidxSections.end();
}
bool ARMExidxSyntheticSection::classof(const SectionBase *d) { bool ARMExidxSyntheticSection::classof(const SectionBase *d) {
return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX; return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX;
} }
@ -3401,23 +3465,6 @@ bool PPC64LongBranchTargetSection::isNeeded() const {
return !finalized || !entries.empty(); return !finalized || !entries.empty();
} }
RISCVSdataSection::RISCVSdataSection()
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 1, ".sdata") {}
bool RISCVSdataSection::isNeeded() const {
if (!ElfSym::riscvGlobalPointer)
return false;
// __global_pointer$ is defined relative to .sdata . If the section does not
// exist, create a dummy one.
for (BaseCommand *base : getParent()->sectionCommands)
if (auto *isd = dyn_cast<InputSectionDescription>(base))
for (InputSection *isec : isd->sections)
if (isec != this)
return false;
return true;
}
static uint8_t getAbiVersion() { static uint8_t getAbiVersion() {
// MIPS non-PIC executable gets ABI version 1. // MIPS non-PIC executable gets ABI version 1.
if (config->emachine == EM_MIPS) { if (config->emachine == EM_MIPS) {
@ -3438,7 +3485,7 @@ static uint8_t getAbiVersion() {
return 0; return 0;
} }
template <typename ELFT> void elf::writeEhdr(uint8_t *buf, Partition &part) { template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part) {
// For executable segments, the trap instructions are written before writing // For executable segments, the trap instructions are written before writing
// the header. Setting Elf header bytes to zero ensures that any unused bytes // the header. Setting Elf header bytes to zero ensures that any unused bytes
// in header are zero-cleared, instead of having trap instructions. // in header are zero-cleared, instead of having trap instructions.
@ -3464,7 +3511,7 @@ template <typename ELFT> void elf::writeEhdr(uint8_t *buf, Partition &part) {
} }
} }
template <typename ELFT> void elf::writePhdrs(uint8_t *buf, Partition &part) { template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part) {
// Write the program header table. // Write the program header table.
auto *hBuf = reinterpret_cast<typename ELFT::Phdr *>(buf); auto *hBuf = reinterpret_cast<typename ELFT::Phdr *>(buf);
for (PhdrEntry *p : part.phdrs) { for (PhdrEntry *p : part.phdrs) {
@ -3539,92 +3586,90 @@ void PartitionIndexSection::writeTo(uint8_t *buf) {
} }
} }
InStruct elf::in; InStruct in;
std::vector<Partition> elf::partitions; std::vector<Partition> partitions;
Partition *elf::mainPart; Partition *mainPart;
template GdbIndexSection *GdbIndexSection::create<ELF32LE>(); template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
template GdbIndexSection *GdbIndexSection::create<ELF32BE>(); template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
template GdbIndexSection *GdbIndexSection::create<ELF64LE>(); template GdbIndexSection *GdbIndexSection::create<ELF64LE>();
template GdbIndexSection *GdbIndexSection::create<ELF64BE>(); template GdbIndexSection *GdbIndexSection::create<ELF64BE>();
template void elf::splitSections<ELF32LE>(); template void splitSections<ELF32LE>();
template void elf::splitSections<ELF32BE>(); template void splitSections<ELF32BE>();
template void elf::splitSections<ELF64LE>(); template void splitSections<ELF64LE>();
template void elf::splitSections<ELF64BE>(); template void splitSections<ELF64BE>();
template void EhFrameSection::addSection<ELF32LE>(InputSectionBase *);
template void EhFrameSection::addSection<ELF32BE>(InputSectionBase *);
template void EhFrameSection::addSection<ELF64LE>(InputSectionBase *);
template void EhFrameSection::addSection<ELF64BE>(InputSectionBase *);
template void PltSection::addEntry<ELF32LE>(Symbol &Sym); template void PltSection::addEntry<ELF32LE>(Symbol &Sym);
template void PltSection::addEntry<ELF32BE>(Symbol &Sym); template void PltSection::addEntry<ELF32BE>(Symbol &Sym);
template void PltSection::addEntry<ELF64LE>(Symbol &Sym); template void PltSection::addEntry<ELF64LE>(Symbol &Sym);
template void PltSection::addEntry<ELF64BE>(Symbol &Sym); template void PltSection::addEntry<ELF64BE>(Symbol &Sym);
template class elf::MipsAbiFlagsSection<ELF32LE>; template class MipsAbiFlagsSection<ELF32LE>;
template class elf::MipsAbiFlagsSection<ELF32BE>; template class MipsAbiFlagsSection<ELF32BE>;
template class elf::MipsAbiFlagsSection<ELF64LE>; template class MipsAbiFlagsSection<ELF64LE>;
template class elf::MipsAbiFlagsSection<ELF64BE>; template class MipsAbiFlagsSection<ELF64BE>;
template class elf::MipsOptionsSection<ELF32LE>; template class MipsOptionsSection<ELF32LE>;
template class elf::MipsOptionsSection<ELF32BE>; template class MipsOptionsSection<ELF32BE>;
template class elf::MipsOptionsSection<ELF64LE>; template class MipsOptionsSection<ELF64LE>;
template class elf::MipsOptionsSection<ELF64BE>; template class MipsOptionsSection<ELF64BE>;
template class elf::MipsReginfoSection<ELF32LE>; template class MipsReginfoSection<ELF32LE>;
template class elf::MipsReginfoSection<ELF32BE>; template class MipsReginfoSection<ELF32BE>;
template class elf::MipsReginfoSection<ELF64LE>; template class MipsReginfoSection<ELF64LE>;
template class elf::MipsReginfoSection<ELF64BE>; template class MipsReginfoSection<ELF64BE>;
template class elf::DynamicSection<ELF32LE>; template class DynamicSection<ELF32LE>;
template class elf::DynamicSection<ELF32BE>; template class DynamicSection<ELF32BE>;
template class elf::DynamicSection<ELF64LE>; template class DynamicSection<ELF64LE>;
template class elf::DynamicSection<ELF64BE>; template class DynamicSection<ELF64BE>;
template class elf::RelocationSection<ELF32LE>; template class RelocationSection<ELF32LE>;
template class elf::RelocationSection<ELF32BE>; template class RelocationSection<ELF32BE>;
template class elf::RelocationSection<ELF64LE>; template class RelocationSection<ELF64LE>;
template class elf::RelocationSection<ELF64BE>; template class RelocationSection<ELF64BE>;
template class elf::AndroidPackedRelocationSection<ELF32LE>; template class AndroidPackedRelocationSection<ELF32LE>;
template class elf::AndroidPackedRelocationSection<ELF32BE>; template class AndroidPackedRelocationSection<ELF32BE>;
template class elf::AndroidPackedRelocationSection<ELF64LE>; template class AndroidPackedRelocationSection<ELF64LE>;
template class elf::AndroidPackedRelocationSection<ELF64BE>; template class AndroidPackedRelocationSection<ELF64BE>;
template class elf::RelrSection<ELF32LE>; template class RelrSection<ELF32LE>;
template class elf::RelrSection<ELF32BE>; template class RelrSection<ELF32BE>;
template class elf::RelrSection<ELF64LE>; template class RelrSection<ELF64LE>;
template class elf::RelrSection<ELF64BE>; template class RelrSection<ELF64BE>;
template class elf::SymbolTableSection<ELF32LE>; template class SymbolTableSection<ELF32LE>;
template class elf::SymbolTableSection<ELF32BE>; template class SymbolTableSection<ELF32BE>;
template class elf::SymbolTableSection<ELF64LE>; template class SymbolTableSection<ELF64LE>;
template class elf::SymbolTableSection<ELF64BE>; template class SymbolTableSection<ELF64BE>;
template class elf::VersionNeedSection<ELF32LE>; template class VersionNeedSection<ELF32LE>;
template class elf::VersionNeedSection<ELF32BE>; template class VersionNeedSection<ELF32BE>;
template class elf::VersionNeedSection<ELF64LE>; template class VersionNeedSection<ELF64LE>;
template class elf::VersionNeedSection<ELF64BE>; template class VersionNeedSection<ELF64BE>;
template void elf::writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part); template void writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part);
template void elf::writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part); template void writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part);
template void elf::writeEhdr<ELF64LE>(uint8_t *Buf, Partition &Part); template void writeEhdr<ELF64LE>(uint8_t *Buf, Partition &Part);
template void elf::writeEhdr<ELF64BE>(uint8_t *Buf, Partition &Part); template void writeEhdr<ELF64BE>(uint8_t *Buf, Partition &Part);
template void elf::writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part); template void writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part);
template void elf::writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part); template void writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
template void elf::writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part); template void writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
template void elf::writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part); template void writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
template class elf::PartitionElfHeaderSection<ELF32LE>; template class PartitionElfHeaderSection<ELF32LE>;
template class elf::PartitionElfHeaderSection<ELF32BE>; template class PartitionElfHeaderSection<ELF32BE>;
template class elf::PartitionElfHeaderSection<ELF64LE>; template class PartitionElfHeaderSection<ELF64LE>;
template class elf::PartitionElfHeaderSection<ELF64BE>; template class PartitionElfHeaderSection<ELF64BE>;
template class elf::PartitionProgramHeadersSection<ELF32LE>; template class PartitionProgramHeadersSection<ELF32LE>;
template class elf::PartitionProgramHeadersSection<ELF32BE>; template class PartitionProgramHeadersSection<ELF32BE>;
template class elf::PartitionProgramHeadersSection<ELF64LE>; template class PartitionProgramHeadersSection<ELF64LE>;
template class elf::PartitionProgramHeadersSection<ELF64BE>; template class PartitionProgramHeadersSection<ELF64BE>;
} // namespace elf
} // namespace lld

View File

@ -76,7 +76,7 @@ class EhFrameSection final : public SyntheticSection {
return SyntheticSection::classof(d) && d->name == ".eh_frame"; return SyntheticSection::classof(d) && d->name == ".eh_frame";
} }
template <class ELFT> void addSection(InputSectionBase *s); void addSection(EhInputSection *sec);
std::vector<EhInputSection *> sections; std::vector<EhInputSection *> sections;
size_t numFdes = 0; size_t numFdes = 0;
@ -97,7 +97,9 @@ class EhFrameSection final : public SyntheticSection {
uint64_t size = 0; uint64_t size = 0;
template <class ELFT, class RelTy> template <class ELFT, class RelTy>
void addSectionAux(EhInputSection *s, llvm::ArrayRef<RelTy> rels); void addRecords(EhInputSection *s, llvm::ArrayRef<RelTy> rels);
template <class ELFT>
void addSectionAux(EhInputSection *s);
template <class ELFT, class RelTy> template <class ELFT, class RelTy>
CieRecord *addCie(EhSectionPiece &piece, ArrayRef<RelTy> rels); CieRecord *addCie(EhSectionPiece &piece, ArrayRef<RelTy> rels);
@ -992,7 +994,7 @@ class ARMExidxSyntheticSection : public SyntheticSection {
size_t getSize() const override { return size; } size_t getSize() const override { return size; }
void writeTo(uint8_t *buf) override; void writeTo(uint8_t *buf) override;
bool isNeeded() const override { return !empty; } bool isNeeded() const override;
// Sort and remove duplicate entries. // Sort and remove duplicate entries.
void finalizeContents() override; void finalizeContents() override;
InputSection *getLinkOrderDep() const; InputSection *getLinkOrderDep() const;
@ -1006,9 +1008,6 @@ class ARMExidxSyntheticSection : public SyntheticSection {
private: private:
size_t size; size_t size;
// Empty if ExecutableSections contains no dependent .ARM.exidx sections.
bool empty = true;
// Instead of storing pointers to the .ARM.exidx InputSections from // Instead of storing pointers to the .ARM.exidx InputSections from
// InputObjects, we store pointers to the executable sections that need // InputObjects, we store pointers to the executable sections that need
// .ARM.exidx sections. We can then use the dependentSections of these to // .ARM.exidx sections. We can then use the dependentSections of these to
@ -1098,19 +1097,11 @@ class PartitionIndexSection : public SyntheticSection {
void writeTo(uint8_t *buf) override; void writeTo(uint8_t *buf) override;
}; };
// Create a dummy .sdata for __global_pointer$ if .sdata does not exist.
class RISCVSdataSection final : public SyntheticSection {
public:
RISCVSdataSection();
size_t getSize() const override { return 0; }
bool isNeeded() const override;
void writeTo(uint8_t *buf) override {}
};
InputSection *createInterpSection(); InputSection *createInterpSection();
MergeInputSection *createCommentSection(); MergeInputSection *createCommentSection();
MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
uint64_t flags, uint32_t alignment);
template <class ELFT> void splitSections(); template <class ELFT> void splitSections();
void mergeSections();
template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part); template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part);
template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part); template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part);
@ -1171,7 +1162,6 @@ struct InStruct {
PltSection *plt; PltSection *plt;
PltSection *iplt; PltSection *iplt;
PPC32Got2Section *ppc32Got2; PPC32Got2Section *ppc32Got2;
RISCVSdataSection *riscvSdata;
RelocationBaseSection *relaPlt; RelocationBaseSection *relaPlt;
RelocationBaseSection *relaIplt; RelocationBaseSection *relaIplt;
StringTableSection *shStrTab; StringTableSection *shStrTab;

View File

@ -34,19 +34,19 @@
using namespace llvm; using namespace llvm;
using namespace llvm::object; using namespace llvm::object;
using namespace llvm::ELF; using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
const TargetInfo *elf::target; namespace lld {
std::string toString(elf::RelType type) {
std::string lld::toString(RelType type) {
StringRef s = getELFRelocationTypeName(elf::config->emachine, type); StringRef s = getELFRelocationTypeName(elf::config->emachine, type);
if (s == "Unknown") if (s == "Unknown")
return ("Unknown (" + Twine(type) + ")").str(); return ("Unknown (" + Twine(type) + ")").str();
return s; return s;
} }
TargetInfo *elf::getTarget() { namespace elf {
const TargetInfo *target;
TargetInfo *getTarget() {
switch (config->emachine) { switch (config->emachine) {
case EM_386: case EM_386:
case EM_IAMCU: case EM_IAMCU:
@ -91,6 +91,9 @@ TargetInfo *elf::getTarget() {
} }
template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) { template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
if (!Out::bufferStart)
return {};
for (InputSectionBase *d : inputSections) { for (InputSectionBase *d : inputSections) {
auto *isec = cast<InputSection>(d); auto *isec = cast<InputSection>(d);
if (!isec->getParent()) if (!isec->getParent())
@ -103,7 +106,7 @@ template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
return {}; return {};
} }
ErrorPlace elf::getErrorPlace(const uint8_t *loc) { ErrorPlace getErrorPlace(const uint8_t *loc) {
switch (config->ekind) { switch (config->ekind) {
case ELF32LEKind: case ELF32LEKind:
return getErrPlace<ELF32LE>(loc); return getErrPlace<ELF32LE>(loc);
@ -179,3 +182,6 @@ uint64_t TargetInfo::getImageBase() const {
return *config->imageBase; return *config->imageBase;
return config->isPic ? 0 : defaultImageBase; return config->isPic ? 0 : defaultImageBase;
} }
} // namespace elf
} // namespace lld

View File

@ -8,6 +8,7 @@
#include "Writer.h" #include "Writer.h"
#include "AArch64ErrataFix.h" #include "AArch64ErrataFix.h"
#include "ARMErrataFix.h"
#include "CallGraphSort.h" #include "CallGraphSort.h"
#include "Config.h" #include "Config.h"
#include "LinkerScript.h" #include "LinkerScript.h"
@ -35,9 +36,8 @@ using namespace llvm::object;
using namespace llvm::support; using namespace llvm::support;
using namespace llvm::support::endian; using namespace llvm::support::endian;
using namespace lld; namespace lld {
using namespace lld::elf; namespace elf {
namespace { namespace {
// The writer writes a SymbolTable result to a file. // The writer writes a SymbolTable result to a file.
template <class ELFT> class Writer { template <class ELFT> class Writer {
@ -62,7 +62,6 @@ template <class ELFT> class Writer {
void setReservedSymbolSections(); void setReservedSymbolSections();
std::vector<PhdrEntry *> createPhdrs(Partition &part); std::vector<PhdrEntry *> createPhdrs(Partition &part);
void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrEntry);
void addPhdrForSection(Partition &part, unsigned shType, unsigned pType, void addPhdrForSection(Partition &part, unsigned shType, unsigned pType,
unsigned pFlags); unsigned pFlags);
void assignFileOffsets(); void assignFileOffsets();
@ -92,7 +91,7 @@ static bool isSectionPrefix(StringRef prefix, StringRef name) {
return name.startswith(prefix) || name == prefix.drop_back(); return name.startswith(prefix) || name == prefix.drop_back();
} }
StringRef elf::getOutputSectionName(const InputSectionBase *s) { StringRef getOutputSectionName(const InputSectionBase *s) {
if (config->relocatable) if (config->relocatable)
return s->name; return s->name;
@ -140,10 +139,9 @@ static bool needsInterpSection() {
script->needsInterpSection(); script->needsInterpSection();
} }
template <class ELFT> void elf::writeResult() { Writer<ELFT>().run(); } template <class ELFT> void writeResult() { Writer<ELFT>().run(); }
template <class ELFT> static void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
void Writer<ELFT>::removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
llvm::erase_if(phdrs, [&](const PhdrEntry *p) { llvm::erase_if(phdrs, [&](const PhdrEntry *p) {
if (p->p_type != PT_LOAD) if (p->p_type != PT_LOAD)
return false; return false;
@ -154,7 +152,7 @@ void Writer<ELFT>::removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
}); });
} }
template <class ELFT> static void copySectionsIntoPartitions() { void copySectionsIntoPartitions() {
std::vector<InputSectionBase *> newSections; std::vector<InputSectionBase *> newSections;
for (unsigned part = 2; part != partitions.size() + 1; ++part) { for (unsigned part = 2; part != partitions.size() + 1; ++part) {
for (InputSectionBase *s : inputSections) { for (InputSectionBase *s : inputSections) {
@ -176,7 +174,7 @@ template <class ELFT> static void copySectionsIntoPartitions() {
newSections.end()); newSections.end());
} }
template <class ELFT> static void combineEhSections() { void combineEhSections() {
for (InputSectionBase *&s : inputSections) { for (InputSectionBase *&s : inputSections) {
// Ignore dead sections and the partition end marker (.part.end), // Ignore dead sections and the partition end marker (.part.end),
// whose partition number is out of bounds. // whose partition number is out of bounds.
@ -185,7 +183,7 @@ template <class ELFT> static void combineEhSections() {
Partition &part = s->getPartition(); Partition &part = s->getPartition();
if (auto *es = dyn_cast<EhInputSection>(s)) { if (auto *es = dyn_cast<EhInputSection>(s)) {
part.ehFrame->addSection<ELFT>(es); part.ehFrame->addSection(es);
s = nullptr; s = nullptr;
} else if (s->kind() == SectionBase::Regular && part.armExidx && } else if (s->kind() == SectionBase::Regular && part.armExidx &&
part.armExidx->addSection(cast<InputSection>(s))) { part.armExidx->addSection(cast<InputSection>(s))) {
@ -217,7 +215,7 @@ static Defined *addAbsolute(StringRef name) {
// The linker is expected to define some symbols depending on // The linker is expected to define some symbols depending on
// the linking result. This function defines such symbols. // the linking result. This function defines such symbols.
void elf::addReservedSymbols() { void addReservedSymbols() {
if (config->emachine == EM_MIPS) { if (config->emachine == EM_MIPS) {
// Define _gp for MIPS. st_value of _gp symbol will be updated by Writer // Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
// so that it points to an absolute address which by default is relative // so that it points to an absolute address which by default is relative
@ -310,13 +308,23 @@ static OutputSection *findSection(StringRef name, unsigned partition = 1) {
return nullptr; return nullptr;
} }
// Initialize Out members. template <class ELFT> void createSyntheticSections() {
template <class ELFT> static void createSyntheticSections() {
// Initialize all pointers with NULL. This is needed because // Initialize all pointers with NULL. This is needed because
// you can call lld::elf::main more than once as a library. // you can call lld::elf::main more than once as a library.
memset(&Out::first, 0, sizeof(Out)); memset(&Out::first, 0, sizeof(Out));
auto add = [](InputSectionBase *sec) { inputSections.push_back(sec); }; // Add the .interp section first because it is not a SyntheticSection.
// The removeUnusedSyntheticSections() function relies on the
// SyntheticSections coming last.
if (needsInterpSection()) {
for (size_t i = 1; i <= partitions.size(); ++i) {
InputSection *sec = createInterpSection();
sec->partition = i;
inputSections.push_back(sec);
}
}
auto add = [](SyntheticSection *sec) { inputSections.push_back(sec); };
in.shStrTab = make<StringTableSection>(".shstrtab", false); in.shStrTab = make<StringTableSection>(".shstrtab", false);
@ -355,8 +363,10 @@ template <class ELFT> static void createSyntheticSections() {
add(sec); add(sec);
} }
StringRef relaDynName = config->isRela ? ".rela.dyn" : ".rel.dyn";
for (Partition &part : partitions) { for (Partition &part : partitions) {
auto add = [&](InputSectionBase *sec) { auto add = [&](SyntheticSection *sec) {
sec->partition = part.getNumber(); sec->partition = part.getNumber();
inputSections.push_back(sec); inputSections.push_back(sec);
}; };
@ -378,16 +388,11 @@ template <class ELFT> static void createSyntheticSections() {
part.dynStrTab = make<StringTableSection>(".dynstr", true); part.dynStrTab = make<StringTableSection>(".dynstr", true);
part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab); part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
part.dynamic = make<DynamicSection<ELFT>>(); part.dynamic = make<DynamicSection<ELFT>>();
if (config->androidPackDynRelocs) { if (config->androidPackDynRelocs)
part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>( part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>(relaDynName);
config->isRela ? ".rela.dyn" : ".rel.dyn"); else
} else { part.relaDyn =
part.relaDyn = make<RelocationSection<ELFT>>( make<RelocationSection<ELFT>>(relaDynName, config->zCombreloc);
config->isRela ? ".rela.dyn" : ".rel.dyn", config->zCombreloc);
}
if (needsInterpSection())
add(createInterpSection());
if (config->hasDynSymTab) { if (config->hasDynSymTab) {
part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab); part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
@ -396,7 +401,7 @@ template <class ELFT> static void createSyntheticSections() {
part.verSym = make<VersionTableSection>(); part.verSym = make<VersionTableSection>();
add(part.verSym); add(part.verSym);
if (!config->versionDefinitions.empty()) { if (!namedVersionDefs().empty()) {
part.verDef = make<VersionDefinitionSection>(); part.verDef = make<VersionDefinitionSection>();
add(part.verDef); add(part.verDef);
} }
@ -476,11 +481,6 @@ template <class ELFT> static void createSyntheticSections() {
add(in.ppc64LongBranchTarget); add(in.ppc64LongBranchTarget);
} }
if (config->emachine == EM_RISCV) {
in.riscvSdata = make<RISCVSdataSection>();
add(in.riscvSdata);
}
in.gotPlt = make<GotPltSection>(); in.gotPlt = make<GotPltSection>();
add(in.gotPlt); add(in.gotPlt);
in.igotPlt = make<IgotPltSection>(); in.igotPlt = make<IgotPltSection>();
@ -504,16 +504,14 @@ template <class ELFT> static void createSyntheticSections() {
config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false); config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false);
add(in.relaPlt); add(in.relaPlt);
// The relaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure // The relaIplt immediately follows .rel[a].dyn to ensure that the IRelative
// that the IRelative relocations are processed last by the dynamic loader. // relocations are processed last by the dynamic loader. We cannot place the
// We cannot place the iplt section in .rel.dyn when Android relocation // iplt section in .rel.dyn when Android relocation packing is enabled because
// packing is enabled because that would cause a section type mismatch. // that would cause a section type mismatch. However, because the Android
// However, because the Android dynamic loader reads .rel.plt after .rel.dyn, // dynamic loader reads .rel.plt after .rel.dyn, we can get the desired
// we can get the desired behaviour by placing the iplt section in .rel.plt. // behaviour by placing the iplt section in .rel.plt.
in.relaIplt = make<RelocationSection<ELFT>>( in.relaIplt = make<RelocationSection<ELFT>>(
(config->emachine == EM_ARM && !config->androidPackDynRelocs) config->androidPackDynRelocs ? in.relaPlt->name : relaDynName,
? ".rel.dyn"
: in.relaPlt->name,
/*sort=*/false); /*sort=*/false);
add(in.relaIplt); add(in.relaIplt);
@ -544,29 +542,6 @@ template <class ELFT> static void createSyntheticSections() {
// The main function of the writer. // The main function of the writer.
template <class ELFT> void Writer<ELFT>::run() { template <class ELFT> void Writer<ELFT>::run() {
// Make copies of any input sections that need to be copied into each
// partition.
copySectionsIntoPartitions<ELFT>();
// Create linker-synthesized sections such as .got or .plt.
// Such sections are of type input section.
createSyntheticSections<ELFT>();
// Some input sections that are used for exception handling need to be moved
// into synthetic sections. Do that now so that they aren't assigned to
// output sections in the usual way.
if (!config->relocatable)
combineEhSections<ELFT>();
// We want to process linker script commands. When SECTIONS command
// is given we let it create sections.
script->processSectionCommands();
// Linker scripts controls how input sections are assigned to output sections.
// Input sections that were not handled by scripts are called "orphans", and
// they are assigned to output sections by the default rule. Process that.
script->addOrphanSections();
if (config->discard != DiscardPolicy::All) if (config->discard != DiscardPolicy::All)
copyLocalSymbols(); copyLocalSymbols();
@ -582,15 +557,14 @@ template <class ELFT> void Writer<ELFT>::run() {
if (errorCount()) if (errorCount())
return; return;
script->assignAddresses();
// If -compressed-debug-sections is specified, we need to compress // If -compressed-debug-sections is specified, we need to compress
// .debug_* sections. Do it right now because it changes the size of // .debug_* sections. Do it right now because it changes the size of
// output sections. // output sections.
for (OutputSection *sec : outputSections) for (OutputSection *sec : outputSections)
sec->maybeCompress<ELFT>(); sec->maybeCompress<ELFT>();
script->allocateHeaders(mainPart->phdrs); if (script->hasSectionsCommand)
script->allocateHeaders(mainPart->phdrs);
// Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a // Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
// 0 sized region. This has to be done late since only after assignAddresses // 0 sized region. This has to be done late since only after assignAddresses
@ -622,7 +596,8 @@ template <class ELFT> void Writer<ELFT>::run() {
return; return;
if (!config->oFormatBinary) { if (!config->oFormatBinary) {
writeTrapInstr(); if (config->zSeparate != SeparateSegmentKind::None)
writeTrapInstr();
writeHeader(); writeHeader();
writeSections(); writeSections();
} else { } else {
@ -738,7 +713,7 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
}); });
if (i == sec->sectionCommands.end()) if (i == sec->sectionCommands.end())
continue; continue;
InputSection *isec = cast<InputSectionDescription>(*i)->sections[0]; InputSectionBase *isec = cast<InputSectionDescription>(*i)->sections[0];
// Relocations are not using REL[A] section symbols. // Relocations are not using REL[A] section symbols.
if (isec->type == SHT_REL || isec->type == SHT_RELA) if (isec->type == SHT_REL || isec->type == SHT_RELA)
@ -1070,7 +1045,7 @@ template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
ElfSym::globalOffsetTable->section = gotSection; ElfSym::globalOffsetTable->section = gotSection;
} }
// .rela_iplt_{start,end} mark the start and the end of .rela.plt section. // .rela_iplt_{start,end} mark the start and the end of in.relaIplt.
if (ElfSym::relaIpltStart && in.relaIplt->isNeeded()) { if (ElfSym::relaIpltStart && in.relaIplt->isNeeded()) {
ElfSym::relaIpltStart->section = in.relaIplt; ElfSym::relaIpltStart->section = in.relaIplt;
ElfSym::relaIpltEnd->section = in.relaIplt; ElfSym::relaIpltEnd->section = in.relaIplt;
@ -1298,10 +1273,7 @@ sortISDBySectionOrder(InputSectionDescription *isd,
} }
orderedSections.push_back({isec, i->second}); orderedSections.push_back({isec, i->second});
} }
llvm::sort(orderedSections, [&](std::pair<InputSection *, int> a, llvm::sort(orderedSections, llvm::less_second());
std::pair<InputSection *, int> b) {
return a.second < b.second;
});
// Find an insertion point for the ordered section list in the unordered // Find an insertion point for the ordered section list in the unordered
// section list. On targets with limited-range branches, this is the mid-point // section list. On targets with limited-range branches, this is the mid-point
@ -1536,6 +1508,12 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
if (!(sec->flags & SHF_LINK_ORDER)) if (!(sec->flags & SHF_LINK_ORDER))
continue; continue;
// The ARM.exidx section use SHF_LINK_ORDER, but we have consolidated
// this processing inside the ARMExidxsyntheticsection::finalizeContents().
if (!config->relocatable && config->emachine == EM_ARM &&
sec->type == SHT_ARM_EXIDX)
continue;
// Link order may be distributed across several InputSectionDescriptions // Link order may be distributed across several InputSectionDescriptions
// but sort must consider them all at once. // but sort must consider them all at once.
std::vector<InputSection **> scriptSections; std::vector<InputSection **> scriptSections;
@ -1545,14 +1523,16 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
for (InputSection *&isec : isd->sections) { for (InputSection *&isec : isd->sections) {
scriptSections.push_back(&isec); scriptSections.push_back(&isec);
sections.push_back(isec); sections.push_back(isec);
InputSection *link = isec->getLinkOrderDep();
if (!link->getParent())
error(toString(isec) + ": sh_link points to discarded section " +
toString(link));
} }
} }
} }
// The ARM.exidx section use SHF_LINK_ORDER, but we have consolidated if (errorCount())
// this processing inside the ARMExidxsyntheticsection::finalizeContents().
if (!config->relocatable && config->emachine == EM_ARM &&
sec->type == SHT_ARM_EXIDX)
continue; continue;
llvm::stable_sort(sections, compareByFilePosition); llvm::stable_sort(sections, compareByFilePosition);
@ -1569,21 +1549,30 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() { template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
ThunkCreator tc; ThunkCreator tc;
AArch64Err843419Patcher a64p; AArch64Err843419Patcher a64p;
ARMErr657417Patcher a32p;
script->assignAddresses();
// For some targets, like x86, this loop iterates only once. int assignPasses = 0;
for (;;) { for (;;) {
bool changed = false; bool changed = target->needsThunks && tc.createThunks(outputSections);
script->assignAddresses(); // With Thunk Size much smaller than branch range we expect to
// converge quickly; if we get to 10 something has gone wrong.
if (target->needsThunks) if (changed && tc.pass >= 10) {
changed |= tc.createThunks(outputSections); error("thunk creation not converged");
break;
}
if (config->fixCortexA53Errata843419) { if (config->fixCortexA53Errata843419) {
if (changed) if (changed)
script->assignAddresses(); script->assignAddresses();
changed |= a64p.createFixes(); changed |= a64p.createFixes();
} }
if (config->fixCortexA8) {
if (changed)
script->assignAddresses();
changed |= a32p.createFixes();
}
if (in.mipsGot) if (in.mipsGot)
in.mipsGot->updateAllocSize(); in.mipsGot->updateAllocSize();
@ -1594,8 +1583,19 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
changed |= part.relrDyn->updateAllocSize(); changed |= part.relrDyn->updateAllocSize();
} }
if (!changed) const Defined *changedSym = script->assignAddresses();
return; if (!changed) {
// Some symbols may be dependent on section addresses. When we break the
// loop, the symbol values are finalized because a previous
// assignAddresses() finalized section addresses.
if (!changedSym)
break;
if (++assignPasses == 5) {
errorOrWarn("assignment to symbol " + toString(*changedSym) +
" does not converge");
break;
}
}
} }
} }
@ -1655,13 +1655,13 @@ static bool computeIsPreemptible(const Symbol &b) {
if (!b.isDefined()) if (!b.isDefined())
return true; return true;
// If we have a dynamic list it specifies which local symbols are preemptible.
if (config->hasDynamicList)
return false;
if (!config->shared) if (!config->shared)
return false; return false;
// If the dynamic list is present, it specifies preemptable symbols in a DSO.
if (config->hasDynamicList)
return b.inDynamicList;
// -Bsymbolic means that definitions are not preempted. // -Bsymbolic means that definitions are not preempted.
if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc())) if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc()))
return false; return false;
@ -1696,12 +1696,16 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// Define __rel[a]_iplt_{start,end} symbols if needed. // Define __rel[a]_iplt_{start,end} symbols if needed.
addRelIpltSymbols(); addRelIpltSymbols();
// RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800 if not defined. // RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800. This symbol
// This symbol should only be defined in an executable. // should only be defined in an executable. If .sdata does not exist, its
if (config->emachine == EM_RISCV && !config->shared) // value/section does not matter but it has to be relative, so set its
// st_shndx arbitrarily to 1 (Out::elfHeader).
if (config->emachine == EM_RISCV && !config->shared) {
OutputSection *sec = findSection(".sdata");
ElfSym::riscvGlobalPointer = ElfSym::riscvGlobalPointer =
addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800, addOptionalRegular("__global_pointer$", sec ? sec : Out::elfHeader,
STV_DEFAULT, STB_GLOBAL); 0x800, STV_DEFAULT, STB_GLOBAL);
}
if (config->emachine == EM_X86_64) { if (config->emachine == EM_X86_64) {
// On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a // On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
@ -1730,20 +1734,22 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
for (Partition &part : partitions) for (Partition &part : partitions)
finalizeSynthetic(part.ehFrame); finalizeSynthetic(part.ehFrame);
symtab->forEachSymbol([](Symbol *s) { symtab->forEachSymbol(
if (!s->isPreemptible) [](Symbol *s) { s->isPreemptible = computeIsPreemptible(*s); });
s->isPreemptible = computeIsPreemptible(*s);
}); // Change values of linker-script-defined symbols from placeholders (assigned
// by declareSymbols) to actual definitions.
script->processSymbolAssignments();
// Scan relocations. This must be done after every symbol is declared so that // Scan relocations. This must be done after every symbol is declared so that
// we can correctly decide if a dynamic relocation is needed. // we can correctly decide if a dynamic relocation is needed. This is called
// after processSymbolAssignments() because it needs to know whether a
// linker-script-defined symbol is absolute.
if (!config->relocatable) { if (!config->relocatable) {
forEachRelSec(scanRelocations<ELFT>); forEachRelSec(scanRelocations<ELFT>);
reportUndefinedSymbols<ELFT>(); reportUndefinedSymbols<ELFT>();
} }
addIRelativeRelocs();
if (in.plt && in.plt->isNeeded()) if (in.plt && in.plt->isNeeded())
in.plt->addSymbols(); in.plt->addSymbols();
if (in.iplt && in.iplt->isNeeded()) if (in.iplt && in.iplt->isNeeded())
@ -1880,7 +1886,6 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
finalizeSynthetic(in.plt); finalizeSynthetic(in.plt);
finalizeSynthetic(in.iplt); finalizeSynthetic(in.iplt);
finalizeSynthetic(in.ppc32Got2); finalizeSynthetic(in.ppc32Got2);
finalizeSynthetic(in.riscvSdata);
finalizeSynthetic(in.partIndex); finalizeSynthetic(in.partIndex);
// Dynamic section must be the last one in this list and dynamic // Dynamic section must be the last one in this list and dynamic
@ -1905,6 +1910,8 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
// SHFLinkOrder processing must be processed after relative section placements are // SHFLinkOrder processing must be processed after relative section placements are
// known but before addresses are allocated. // known but before addresses are allocated.
resolveShfLinkOrder(); resolveShfLinkOrder();
if (errorCount())
return;
// This is used to: // This is used to:
// 1) Create "thunks": // 1) Create "thunks":
@ -2049,27 +2056,32 @@ std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs(Partition &part) {
unsigned partNo = part.getNumber(); unsigned partNo = part.getNumber();
bool isMain = partNo == 1; bool isMain = partNo == 1;
// The first phdr entry is PT_PHDR which describes the program header itself.
if (isMain)
addHdr(PT_PHDR, PF_R)->add(Out::programHeaders);
else
addHdr(PT_PHDR, PF_R)->add(part.programHeaders->getParent());
// PT_INTERP must be the second entry if exists.
if (OutputSection *cmd = findSection(".interp", partNo))
addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd);
// Add the first PT_LOAD segment for regular output sections. // Add the first PT_LOAD segment for regular output sections.
uint64_t flags = computeFlags(PF_R); uint64_t flags = computeFlags(PF_R);
PhdrEntry *load = nullptr; PhdrEntry *load = nullptr;
// Add the headers. We will remove them if they don't fit. // nmagic or omagic output does not have PT_PHDR, PT_INTERP, or the readonly
// In the other partitions the headers are ordinary sections, so they don't // PT_LOAD.
// need to be added here. if (!config->nmagic && !config->omagic) {
if (isMain) { // The first phdr entry is PT_PHDR which describes the program header
load = addHdr(PT_LOAD, flags); // itself.
load->add(Out::elfHeader); if (isMain)
load->add(Out::programHeaders); addHdr(PT_PHDR, PF_R)->add(Out::programHeaders);
else
addHdr(PT_PHDR, PF_R)->add(part.programHeaders->getParent());
// PT_INTERP must be the second entry if exists.
if (OutputSection *cmd = findSection(".interp", partNo))
addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd);
// Add the headers. We will remove them if they don't fit.
// In the other partitions the headers are ordinary sections, so they don't
// need to be added here.
if (isMain) {
load = addHdr(PT_LOAD, flags);
load->add(Out::elfHeader);
load->add(Out::programHeaders);
}
} }
// PT_GNU_RELRO includes all sections that should be marked as // PT_GNU_RELRO includes all sections that should be marked as
@ -2208,21 +2220,68 @@ void Writer<ELFT>::addPhdrForSection(Partition &part, unsigned shType,
part.phdrs.push_back(entry); part.phdrs.push_back(entry);
} }
// The first section of each PT_LOAD, the first section in PT_GNU_RELRO and the // Place the first section of each PT_LOAD to a different page (of maxPageSize).
// first section after PT_GNU_RELRO have to be page aligned so that the dynamic // This is achieved by assigning an alignment expression to addrExpr of each
// linker can set the permissions. // such section.
template <class ELFT> void Writer<ELFT>::fixSectionAlignments() { template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
auto pageAlign = [](OutputSection *cmd) { const PhdrEntry *prev;
if (cmd && !cmd->addrExpr) auto pageAlign = [&](const PhdrEntry *p) {
cmd->addrExpr = [=] { OutputSection *cmd = p->firstSec;
return alignTo(script->getDot(), config->maxPageSize); if (cmd && !cmd->addrExpr) {
}; // Prefer advancing to align(dot, maxPageSize) + dot%maxPageSize to avoid
// padding in the file contents.
//
// When -z separate-code is used we must not have any overlap in pages
// between an executable segment and a non-executable segment. We align to
// the next maximum page size boundary on transitions between executable
// and non-executable segments.
//
// SHT_LLVM_PART_EHDR marks the start of a partition. The partition
// sections will be extracted to a separate file. Align to the next
// maximum page size boundary so that we can find the ELF header at the
// start. We cannot benefit from overlapping p_offset ranges with the
// previous segment anyway.
if (config->zSeparate == SeparateSegmentKind::Loadable ||
(config->zSeparate == SeparateSegmentKind::Code && prev &&
(prev->p_flags & PF_X) != (p->p_flags & PF_X)) ||
cmd->type == SHT_LLVM_PART_EHDR)
cmd->addrExpr = [] {
return alignTo(script->getDot(), config->maxPageSize);
};
// PT_TLS is at the start of the first RW PT_LOAD. If `p` includes PT_TLS,
// it must be the RW. Align to p_align(PT_TLS) to make sure
// p_vaddr(PT_LOAD)%p_align(PT_LOAD) = 0. Otherwise, if
// sh_addralign(.tdata) < sh_addralign(.tbss), we will set p_align(PT_TLS)
// to sh_addralign(.tbss), while p_vaddr(PT_TLS)=p_vaddr(PT_LOAD) may not
// be congruent to 0 modulo p_align(PT_TLS).
//
// Technically this is not required, but as of 2019, some dynamic loaders
// don't handle p_vaddr%p_align != 0 correctly, e.g. glibc (i386 and
// x86-64) doesn't make runtime address congruent to p_vaddr modulo
// p_align for dynamic TLS blocks (PR/24606), FreeBSD rtld has the same
// bug, musl (TLS Variant 1 architectures) before 1.1.23 handled TLS
// blocks correctly. We need to keep the workaround for a while.
else if (Out::tlsPhdr && Out::tlsPhdr->firstSec == p->firstSec)
cmd->addrExpr = [] {
return alignTo(script->getDot(), config->maxPageSize) +
alignTo(script->getDot() % config->maxPageSize,
Out::tlsPhdr->p_align);
};
else
cmd->addrExpr = [] {
return alignTo(script->getDot(), config->maxPageSize) +
script->getDot() % config->maxPageSize;
};
}
}; };
for (Partition &part : partitions) { for (Partition &part : partitions) {
prev = nullptr;
for (const PhdrEntry *p : part.phdrs) for (const PhdrEntry *p : part.phdrs)
if (p->p_type == PT_LOAD && p->firstSec) if (p->p_type == PT_LOAD && p->firstSec) {
pageAlign(p->firstSec); pageAlign(p);
prev = p;
}
} }
} }
@ -2231,12 +2290,9 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
// load executables without any address adjustment. // load executables without any address adjustment.
static uint64_t computeFileOffset(OutputSection *os, uint64_t off) { static uint64_t computeFileOffset(OutputSection *os, uint64_t off) {
// The first section in a PT_LOAD has to have congruent offset and address // The first section in a PT_LOAD has to have congruent offset and address
// module the page size. // modulo the maximum page size.
if (os->ptLoad && os->ptLoad->firstSec == os) { if (os->ptLoad && os->ptLoad->firstSec == os)
uint64_t alignment = return alignTo(off, os->ptLoad->p_align, os->addr);
std::max<uint64_t>(os->ptLoad->p_align, config->maxPageSize);
return alignTo(off, alignment, os->addr);
}
// File offsets are not significant for .bss sections other than the first one // File offsets are not significant for .bss sections other than the first one
// in a PT_LOAD. By convention, we keep section offsets monotonically // in a PT_LOAD. By convention, we keep section offsets monotonically
@ -2291,13 +2347,12 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
for (OutputSection *sec : outputSections) { for (OutputSection *sec : outputSections) {
off = setFileOffset(sec, off); off = setFileOffset(sec, off);
if (script->hasSectionsCommand)
continue;
// If this is a last section of the last executable segment and that // If this is a last section of the last executable segment and that
// segment is the last loadable segment, align the offset of the // segment is the last loadable segment, align the offset of the
// following section to avoid loading non-segments parts of the file. // following section to avoid loading non-segments parts of the file.
if (lastRX && lastRX->lastSec == sec) if (config->zSeparate != SeparateSegmentKind::None && lastRX &&
lastRX->lastSec == sec)
off = alignTo(off, config->commonPageSize); off = alignTo(off, config->commonPageSize);
} }
@ -2348,14 +2403,13 @@ template <class ELFT> void Writer<ELFT>::setPhdrs(Partition &part) {
p->p_paddr = first->getLMA(); p->p_paddr = first->getLMA();
} }
if (p->p_type == PT_LOAD) { if (p->p_type == PT_GNU_RELRO) {
p->p_align = std::max<uint64_t>(p->p_align, config->maxPageSize);
} else if (p->p_type == PT_GNU_RELRO) {
p->p_align = 1; p->p_align = 1;
// The glibc dynamic loader rounds the size down, so we need to round up // musl/glibc ld.so rounds the size down, so we need to round up
// to protect the last page. This is a no-op on FreeBSD which always // to protect the last page. This is a no-op on FreeBSD which always
// rounds up. // rounds up.
p->p_memsz = alignTo(p->p_memsz, config->commonPageSize); p->p_memsz = alignTo(p->p_offset + p->p_memsz, config->commonPageSize) -
p->p_offset;
} }
} }
} }
@ -2570,9 +2624,6 @@ static void fillTrap(uint8_t *i, uint8_t *end) {
// We'll leave other pages in segments as-is because the rest will be // We'll leave other pages in segments as-is because the rest will be
// overwritten by output sections. // overwritten by output sections.
template <class ELFT> void Writer<ELFT>::writeTrapInstr() { template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
if (script->hasSectionsCommand)
return;
for (Partition &part : partitions) { for (Partition &part : partitions) {
// Fill the last page. // Fill the last page.
for (PhdrEntry *p : part.phdrs) for (PhdrEntry *p : part.phdrs)
@ -2685,7 +2736,15 @@ template <class ELFT> void Writer<ELFT>::writeBuildId() {
part.buildId->writeBuildId(buildId); part.buildId->writeBuildId(buildId);
} }
template void elf::writeResult<ELF32LE>(); template void createSyntheticSections<ELF32LE>();
template void elf::writeResult<ELF32BE>(); template void createSyntheticSections<ELF32BE>();
template void elf::writeResult<ELF64LE>(); template void createSyntheticSections<ELF64LE>();
template void elf::writeResult<ELF64BE>(); template void createSyntheticSections<ELF64BE>();
template void writeResult<ELF32LE>();
template void writeResult<ELF32BE>();
template void writeResult<ELF64LE>();
template void writeResult<ELF64BE>();
} // namespace elf
} // namespace lld

View File

@ -9,6 +9,7 @@
#ifndef LLD_ELF_WRITER_H #ifndef LLD_ELF_WRITER_H
#define LLD_ELF_WRITER_H #define LLD_ELF_WRITER_H
#include "Config.h"
#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include <cstdint> #include <cstdint>
@ -19,13 +20,18 @@ namespace elf {
class InputFile; class InputFile;
class OutputSection; class OutputSection;
class InputSectionBase; class InputSectionBase;
void copySectionsIntoPartitions();
template <class ELFT> void createSyntheticSections();
void combineEhSections();
template <class ELFT> void writeResult(); template <class ELFT> void writeResult();
// This describes a program header entry. // This describes a program header entry.
// Each contains type, access flags and range of output sections that will be // Each contains type, access flags and range of output sections that will be
// placed in it. // placed in it.
struct PhdrEntry { struct PhdrEntry {
PhdrEntry(unsigned type, unsigned flags) : p_type(type), p_flags(flags) {} PhdrEntry(unsigned type, unsigned flags)
: p_align(type == llvm::ELF::PT_LOAD ? config->maxPageSize : 0),
p_type(type), p_flags(flags) {}
void add(OutputSection *sec); void add(OutputSection *sec);
uint64_t p_paddr = 0; uint64_t p_paddr = 0;

View File

@ -1,19 +1,22 @@
======================= ========================
lld 9.0.0 Release Notes lld 10.0.0 Release Notes
======================= ========================
.. contents:: .. contents::
:local: :local:
.. warning::
These are in-progress notes for the upcoming LLVM 10.0.0 release.
Release notes for previous releases can be found on
`the Download Page <https://releases.llvm.org/download.html>`_.
Introduction Introduction
============ ============
lld is a high-performance linker that supports ELF (Unix), COFF This document contains the release notes for the lld linker, release 10.0.0.
(Windows), Mach-O (macOS), MinGW and WebAssembly. lld is Here we describe the status of lld, including major improvements
command-line-compatible with GNU linkers and Microsoft link.exe and is from the previous release. All lld releases may be downloaded
significantly faster than the system default linkers. from the `LLVM releases web site <https://llvm.org/releases/>`_.
lld 9 has lots of feature improvements and bug fixes.
Non-comprehensive list of changes in this release Non-comprehensive list of changes in this release
================================================= =================================================
@ -21,211 +24,31 @@ Non-comprehensive list of changes in this release
ELF Improvements ELF Improvements
---------------- ----------------
* ld.lld now has typo suggestions for flags: * Glob pattern, which you can use in linker scripts or version scripts,
``$ ld.lld --call-shared`` now prints now supports `\` and `[!...]`. Except character classes
``unknown argument '--call-shared', did you mean '--call_shared'``. (e.g. `[[:digit:]]`), lld's glob pattern should be fully compatible
(`r361518 <https://reviews.llvm.org/rL361518>`_) with GNU now. (`r375051
<https://github.com/llvm/llvm-project/commit/48993d5ab9413f0e5b94dfa292a233ce55b09e3e>`_)
* ``--allow-shlib-undefined`` and ``--no-allow-shlib-undefined``
options are added. ``--no-allow-shlib-undefined`` is the default for
executables.
(`r352826 <https://reviews.llvm.org/rL352826>`_)
* ``-nmagic`` and ``-omagic`` options are fully supported.
(`r360593 <https://reviews.llvm.org/rL360593>`_)
* Segment layout has changed. PT_GNU_RELRO, which was previously
placed in the middle of readable/writable PT_LOAD segments, is now
placed at the beginning of them. This change permits lld-produced
ELF files to be read correctly by GNU strip older than 2.31, which
has a bug to discard a PT_GNU_RELRO in the former layout.
* ``-z common-page-size`` is supported.
(`r360593 <https://reviews.llvm.org/rL360593>`_)
* Diagnostics messages have improved. A new flag ``--vs-diagnostics``
alters the format of diagnostic output to enable source hyperlinks
in Microsoft Visual Studio IDE.
* Linker script compatibility with GNU BFD linker has generally improved.
* The clang ``--dependent-library`` form of autolinking is supported.
This feature is added to implement the Windows-style autolinking for
Unix. On Unix, in order to use a library, you usually have to
include a header file provided by the library and then explicitly
link the library with the linker ``-l`` option. On Windows, header
files usually contain pragmas that list needed libraries. Compilers
copy that information to object files, so that linkers can
automatically link needed libraries. ``--dependent-library`` is
added for implementing that Windows semantics on Unix.
(`r360984 <https://reviews.llvm.org/rL360984>`_)
* AArch64 BTI and PAC are supported.
(`r362793 <https://reviews.llvm.org/rL362793>`_)
* lld now supports replacing ``JAL`` with ``JALX`` instructions in case
of MIPS-microMIPS cross-mode jumps.
(`r354311 <https://reviews.llvm.org/rL354311>`_)
* lld now creates LA25 thunks for MIPS R6 code.
(`r354312 <https://reviews.llvm.org/rL354312>`_)
* Put MIPS-specific .reginfo, .MIPS.options, and .MIPS.abiflags sections
into corresponding PT_MIPS_REGINFO, PT_MIPS_OPTIONS, and PT_MIPS_ABIFLAGS
segments.
* The quality of RISC-V and PowerPC ports have greatly improved. Many
applications can now be linked by lld. PowerPC64 is now almost
production ready.
* The Linux kernel for arm32_7, arm64, ppc64le and x86_64 can now be
linked by lld.
* x86-64 TLSDESC is supported.
(`r361911 <https://reviews.llvm.org/rL361911>`_,
`r362078 <https://reviews.llvm.org/rL362078>`_)
* DF_STATIC_TLS flag is set for i386 and x86-64 when needed.
(`r353293 <https://reviews.llvm.org/rL353293>`_,
`r353378 <https://reviews.llvm.org/rL353378>`_)
* The experimental partitioning feature is added to allow a program to
be split into multiple pieces.
The feature allows you to semi-automatically split a single program
into multiple ELF files called "partitions". Since all partitions
share the same memory address space and don't use PLT/GOT, split
programs run as fast as regular programs.
With the mechanism, you can start a program only with a "main"
partition and load remaining partitions on-demand. For example, you
can split a web browser into a main partition and a PDF reader
sub-partition and load the PDF reader partition only when a user
tries to open a PDF file.
See `the documentation <Partitions.html>`_ for more information.
* If "-" is given as an output filename, lld writes the final result
to the standard output. Previously, it created a file "-" in the
current directory.
(`r351852 <https://reviews.llvm.org/rL351852>`_)
* ``-z ifunc-noplt`` option is added to reduce IFunc function call
overhead in a freestanding environment such as the OS kernel.
Functions resolved by the IFunc mechanism are usually dispatched via
PLT and thus slower than regular functions because of the cost of
indirection. With ``-z ifunc-noplt``, you can eliminate it by doing
text relocations at load-time. You need a special loader to utilize
this feature. This feature is added for the FreeBSD kernel but can
be used by any operating systems.
(`r360685 <https://reviews.llvm.org/rL360685>`_)
* ``--undefined-glob`` option is added. The new option is an extension
to ``--undefined`` to take a glob pattern instead of a single symbol
name.
(`r363396 <https://reviews.llvm.org/rL363396>`_)
COFF Improvements COFF Improvements
----------------- -----------------
* Like the ELF driver, lld-link now has typo suggestions for flags. * ...
(`r361518 <https://reviews.llvm.org/rL361518>`_)
* lld-link now correctly reports duplicate symbol errors for object
files that were compiled with ``/Gy``.
(`r352590 <https://reviews.llvm.org/rL352590>`_)
* lld-link now correctly reports duplicate symbol errors when several
resource (.res) input files define resources with the same type,
name and language. This can be demoted to a warning using
``/force:multipleres``.
(`r359829 <https://reviews.llvm.org/rL359829>`_)
* lld-link now rejects more than one resource object input files,
matching link.exe. Previously, lld-link would silently ignore all
but one. If you hit this: Don't pass resource object files to the
linker, instead pass res files to the linker directly. Don't put
resource files in static libraries, pass them on the command line.
(`r359749 <https://reviews.llvm.org/rL359749>`_)
* Having more than two ``/natvis:`` now works correctly; it used to not
work for larger binaries before.
(`r359515 <https://reviews.llvm.org/rL359515>`_)
* Undefined symbols are now printed only in demangled form. Pass
``/demangle:no`` to see raw symbol names instead.
(`r355878 <https://reviews.llvm.org/rL355878>`_)
* Several speed and memory usage improvements.
* lld-link now supports resource object files created by GNU windres and
MS cvtres, not only llvm-cvtres.
* The generated thunks for delayimports now share the majority of code
among thunks, significantly reducing the overhead of using delayimport.
(`r365823 <https://reviews.llvm.org/rL365823>`_)
* ``IMAGE_REL_ARM{,64}_REL32`` relocations are supported.
(`r352325 <https://reviews.llvm.org/rL352325>`_)
* Range extension thunks for AArch64 are now supported, so lld can
create large executables for Windows/ARM64.
(`r352929 <https://reviews.llvm.org/rL352929>`_)
* The following flags have been added:
``/functionpadmin`` (`r354716 <https://reviews.llvm.org/rL354716>`_),
``/swaprun:`` (`r359192 <https://reviews.llvm.org/rL359192>`_),
``/threads:no`` (`r355029 <https://reviews.llvm.org/rL355029>`_),
``/filealign`` (`r361634 <https://reviews.llvm.org/rL361634>`_)
WebAssembly Improvements
------------------------
* Imports from custom module names are supported.
(`r352828 <https://reviews.llvm.org/rL352828>`_)
* Symbols that are in llvm.used are now exported by default.
(`r353364 <https://reviews.llvm.org/rL353364>`_)
* Initial support for PIC and dynamic linking has landed.
(`r357022 <https://reviews.llvm.org/rL357022>`_)
* wasm-ld now add ``__start_``/``__stop_`` symbols for data sections.
(`r361236 <https://reviews.llvm.org/rL361236>`_)
* wasm-ld now doesn't report an error on archives without a symbol index.
(`r364338 <https://reviews.llvm.org/rL364338>`_)
* The following flags have been added:
``--emit-relocs`` (`r361635 <https://reviews.llvm.org/rL361635>`_),
``--wrap`` (`r361639 <https://reviews.llvm.org/rL361639>`_),
``--trace`` and ``--trace-symbol``
(`r353264 <https://reviews.llvm.org/rL353264>`_).
MinGW Improvements MinGW Improvements
------------------ ------------------
* lld now correctly links crtend.o as the last object file, handling * ...
terminators for the sections such as .eh_frame properly, fixing
DWARF exception handling with libgcc and gcc's crtend.o.
* lld now also handles DWARF unwind info generated by GCC, when linking MachO Improvements
with libgcc. ------------------
* PDB output can be requested without manually specifying the PDB file * Item 1.
name, with the new option ``-pdb=`` with an empty value to the option.
(The old existing syntax ``-pdb <filename>`` was more cumbersome to use
with an empty parameter value.)
* ``--no-insert-timestamp`` option is added as an alias to ``/timestamp:0``. WebAssembly Improvements
(`r353145 <https://reviews.llvm.org/rL353145>`_) ------------------------
* Many more GNU ld options are now supported, which e.g. allows the lld * `__data_end` and `__heap_base` are no longer exported by default,
MinGW frontend to be called by GCC. as it's best to keep them internal when possible. They can be
explicitly exported with `--export=__data_end` and
* The following options are added: ``--exclude-all-symbols``, `--export=__heap_base`, respectively.
``--appcontainer``, ``--undefined``

View File

@ -109,7 +109,7 @@ trap at runtime (functions that contain only an ``unreachable`` instruction)
and use these stub functions at the otherwise invalid call sites. and use these stub functions at the otherwise invalid call sites.
The default behaviour is to generate these stub function and to produce The default behaviour is to generate these stub function and to produce
a warning. The ``--falal-warnings`` flag can be used to disable this behaviour a warning. The ``--fatal-warnings`` flag can be used to disable this behaviour
and error out if mismatched are found. and error out if mismatched are found.
Imports and Exports Imports and Exports

View File

@ -48,9 +48,9 @@
# built documents. # built documents.
# #
# The short version. # The short version.
version = '9' version = '10'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '9' release = '10'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -14,7 +14,6 @@
.Nm ld.lld .Nm ld.lld
.Op Ar options .Op Ar options
.Ar objfile ... .Ar objfile ...
.Sh DESCRIPTION .Sh DESCRIPTION
A linker takes one or more object, archive, and library files, and combines A linker takes one or more object, archive, and library files, and combines
them into an output file (an executable, a shared library, or another object them into an output file (an executable, a shared library, or another object
@ -40,7 +39,6 @@ All these targets are always supported however
was built, so you can always use was built, so you can always use
.Nm .Nm
as a native linker as well as a cross linker. as a native linker as well as a cross linker.
.Sh OPTIONS .Sh OPTIONS
Many options have both a single-letter and long form. Many options have both a single-letter and long form.
When using the long form options other than those beginning with the When using the long form options other than those beginning with the
@ -175,8 +173,8 @@ A value of zero indicates that there is no limit.
.It Fl -error-unresolved-symbols .It Fl -error-unresolved-symbols
Report unresolved symbols as errors. Report unresolved symbols as errors.
.It Fl -execute-only .It Fl -execute-only
Mark executable sections unreadable. This option is currently only Mark executable sections unreadable.
supported on AArch64. This option is currently only supported on AArch64.
.It Fl -exclude-libs Ns = Ns Ar value .It Fl -exclude-libs Ns = Ns Ar value
Exclude static libraries from automatic export. Exclude static libraries from automatic export.
.It Fl -export-dynamic , Fl E .It Fl -export-dynamic , Fl E
@ -232,13 +230,16 @@ Enable safe identical code folding.
Disable identical code folding. Disable identical code folding.
.It Fl -ignore-data-address-equality .It Fl -ignore-data-address-equality
Ignore address equality of data. C/C++ requires each data to have a unique Ignore address equality of data. C/C++ requires each data to have a unique
address. This option allows lld to do unsafe optimization that breaks the address.
This option allows lld to do unsafe optimization that breaks the
requirement: create copies of read-only data or merge two or more read-only data requirement: create copies of read-only data or merge two or more read-only data
that happen to have the same value. that happen to have the same value.
.It Fl -ignore-function-address-equality .It Fl -ignore-function-address-equality
Ignore address equality of functions. This option allows non-PIC calls to a Ignore address equality of functions.
function with non-default visibility in a shared object. The function may have This option allows non-PIC calls to a function with non-default visibility in
different addresses within the executable and within the shared object. a shared object.
The function may have different addresses within the executable and within the
shared object.
.It Fl -image-base Ns = Ns Ar value .It Fl -image-base Ns = Ns Ar value
Set the base address to Set the base address to
.Ar value . .Ar value .
@ -344,7 +345,8 @@ is
.Cm binary , .Cm binary ,
which produces output with no ELF header. which produces output with no ELF header.
.It Fl -omagic , Fl N .It Fl -omagic , Fl N
Set the text and data sections to be readable and writable, do not page align sections, link against static libraries. Set the text and data sections to be readable and writable, do not page align
sections, link against static libraries.
.It Fl -opt-remarks-filename Ar file .It Fl -opt-remarks-filename Ar file
Write optimization remarks in YAML format to Write optimization remarks in YAML format to
.Ar file . .Ar file .
@ -354,8 +356,8 @@ Filter optimization remarks by only allowing the passes matching
.It Fl -opt-remarks-with-hotness .It Fl -opt-remarks-with-hotness
Include hotness information in the optimization remarks file. Include hotness information in the optimization remarks file.
.It Fl -orphan-handling Ns = Ns Ar mode .It Fl -orphan-handling Ns = Ns Ar mode
Control how orphan sections are handled. An orphan section is one not Control how orphan sections are handled.
specifically mentioned in a linker script. An orphan section is one not specifically mentioned in a linker script.
.Ar mode .Ar mode
may be: may be:
.Pp .Pp
@ -381,17 +383,21 @@ may be:
.Pp .Pp
.Bl -tag -width 2n -compact .Bl -tag -width 2n -compact
.It Cm none .It Cm none
Don't pack. Dynamic relocations are encoded in SHT_REL(A). Do not pack.
Dynamic relocations are encoded in SHT_REL(A).
.It Cm android .It Cm android
Pack dynamic relocations in SHT_ANDROID_REL(A). Pack dynamic relocations in SHT_ANDROID_REL(A).
.It Cm relr .It Cm relr
Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in SHT_REL(A). Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in
SHT_REL(A).
.It Cm android+relr .It Cm android+relr
Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in SHT_ANDROID_REL(A). Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in
SHT_ANDROID_REL(A).
.El .El
.Pp .Pp
.Cm none .Cm none
is the default. If is the default.
If
.Fl -use-android-relr-tags .Fl -use-android-relr-tags
is specified, use SHT_ANDROID_RELR instead of SHT_RELR. is specified, use SHT_ANDROID_RELR instead of SHT_RELR.
.Pp .Pp
@ -418,8 +424,14 @@ Undo the effect of
.Fl -push-state. .Fl -push-state.
.It Fl -relocatable , Fl r .It Fl -relocatable , Fl r
Create relocatable object file. Create relocatable object file.
.It Fl -reproduce Ns = Ns Ar value .It Fl -reproduce Ns = Ns Ar path
Dump linker invocation and input files for debugging. Write a tar file to
.Ar path,
containing all the input files needed to reproduce the link, a text file called
response.txt containing the command line options and a text file called
version.txt containing the output of ld.lld --version.
The archive when
unpacked can be used to re-run the linker with the same options and input files.
.It Fl -retain-symbols-file Ns = Ns Ar file .It Fl -retain-symbols-file Ns = Ns Ar file
Retain only the symbols listed in the file. Retain only the symbols listed in the file.
.It Fl -rpath Ns = Ns Ar value , Fl R Ar value .It Fl -rpath Ns = Ns Ar value , Fl R Ar value
@ -435,6 +447,8 @@ and
.It Fl -script Ns = Ns Ar file , Fl T Ar file .It Fl -script Ns = Ns Ar file , Fl T Ar file
Read linker script from Read linker script from
.Ar file . .Ar file .
If multiple linker scripts are given, they are processed as if they
were concatenated in the order they appeared on the command line.
.It Fl -section-start Ns = Ns Ar section Ns = Ns Ar address .It Fl -section-start Ns = Ns Ar section Ns = Ns Ar address
Set address of section. Set address of section.
.It Fl -shared , Fl -Bsharable .It Fl -shared , Fl -Bsharable
@ -523,13 +537,15 @@ to include the object file in the output file.
.It Fl -undefined-glob Ns = Ns Ar pattern .It Fl -undefined-glob Ns = Ns Ar pattern
Synonym for Synonym for
.Fl -undefined , .Fl -undefined ,
except that it takes a glob pattern. In a glob pattern, except that it takes a glob pattern.
In a glob pattern,
.Cm * .Cm *
matches zero or more characters, matches zero or more characters,
.Cm ? .Cm ?
matches any single character, and matches any single character, and
.Cm [...] .Cm [...]
matches the characters within brackets. All symbols that match matches the characters within brackets.
All symbols that match
a given pattern are handled as if they were given as arguments of a given pattern are handled as if they were given as arguments of
.Fl -undefined . .Fl -undefined .
.It Fl -unresolved-symbols Ns = Ns Ar value .It Fl -unresolved-symbols Ns = Ns Ar value
@ -556,13 +572,17 @@ Warn about duplicate common symbols.
Warn about using ifunc symbols in conjunction with text relocations. Warn about using ifunc symbols in conjunction with text relocations.
Older versions of glibc library (2.28 and earlier) has a bug that causes Older versions of glibc library (2.28 and earlier) has a bug that causes
the segment that includes ifunc symbols to be marked as not executable when the segment that includes ifunc symbols to be marked as not executable when
they are relocated. As a result, although the program compiles and links they are relocated.
As a result, although the program compiles and links
successfully, it gives segmentation fault when the instruction pointer reaches successfully, it gives segmentation fault when the instruction pointer reaches
an ifunc symbol. Use -warn-ifunc-textrel to let lld give a warning, if the an ifunc symbol.
Use -warn-ifunc-textrel to let lld give a warning, if the
code may include ifunc symbols, may do text relocations and be linked with code may include ifunc symbols, may do text relocations and be linked with
an older glibc version. Otherwise, there is no need to use it, as the default an older glibc version.
value does not give a warning. This flag has been introduced in late 2018, Otherwise, there is no need to use it, as the default value does not give a
has no counter part in ld and gold linkers, and may be removed in the future. warning.
This flag has been introduced in late 2018, has no counter part in ld and gold
linkers, and may be removed in the future.
.It Fl -warn-unresolved-symbols .It Fl -warn-unresolved-symbols
Report unresolved symbols as warnings. Report unresolved symbols as warnings.
.It Fl -whole-archive .It Fl -whole-archive
@ -571,18 +591,21 @@ Force load of all members in a static library.
Use wrapper functions for symbol. Use wrapper functions for symbol.
.It Fl z Ar option .It Fl z Ar option
Linker option extensions. Linker option extensions.
.Bl -tag -width indent .Bl -tag -width indent -compact
.Pp
.It Cm execstack .It Cm execstack
Make the main stack executable. Make the main stack executable.
Stack permissions are recorded in the Stack permissions are recorded in the
.Dv PT_GNU_STACK .Dv PT_GNU_STACK
segment. segment.
.Pp
.It Cm global .It Cm global
Sets the Sets the
.Dv DF_1_GLOBAL flag in the .Dv DF_1_GLOBAL flag in the
.Dv DYNAMIC .Dv DYNAMIC
section. section.
Different loaders can decide how to handle this flag on their own. Different loaders can decide how to handle this flag on their own.
.Pp
.It Cm ifunc-noplt .It Cm ifunc-noplt
Do not emit PLT entries for ifunc symbols. Do not emit PLT entries for ifunc symbols.
Instead, emit text relocations referencing the resolver. Instead, emit text relocations referencing the resolver.
@ -591,64 +614,78 @@ environments where text relocations do not have the usual drawbacks.
This option must be combined with the This option must be combined with the
.Fl z Li notext .Fl z Li notext
option. option.
.Pp
.It Cm initfirst .It Cm initfirst
Sets the Sets the
.Dv DF_1_INITFIRST .Dv DF_1_INITFIRST
flag to indicate the module should be initialized first. flag to indicate the module should be initialized first.
.Pp
.It Cm interpose .It Cm interpose
Set the Set the
.Dv DF_1_INTERPOSE .Dv DF_1_INTERPOSE
flag to indicate to the runtime linker that the object is an interposer. flag to indicate to the runtime linker that the object is an interposer.
During symbol resolution interposers are searched after the application During symbol resolution interposers are searched after the application
but before other dependencies. but before other dependencies.
.Pp
.It Cm muldefs .It Cm muldefs
Do not error if a symbol is defined multiple times. Do not error if a symbol is defined multiple times.
The first definition will be used. The first definition will be used.
This is a synonym for This is a synonym for
.Fl -allow-multiple-definition. .Fl -allow-multiple-definition.
.Pp
.It Cm nocombreloc .It Cm nocombreloc
Disable combining and sorting multiple relocation sections. Disable combining and sorting multiple relocation sections.
.Pp
.It Cm nocopyreloc .It Cm nocopyreloc
Disable the creation of copy relocations. Disable the creation of copy relocations.
.Pp
.It Cm nodefaultlib .It Cm nodefaultlib
Set the Set the
.Dv DF_1_NODEFLIB .Dv DF_1_NODEFLIB
flag to indicate that default library search paths should be ignored. flag to indicate that default library search paths should be ignored.
.Pp
.It Cm nodelete .It Cm nodelete
Set the Set the
.Dv DF_1_NODELETE .Dv DF_1_NODELETE
flag to indicate that the object cannot be unloaded from a process. flag to indicate that the object cannot be unloaded from a process.
.Pp
.It Cm nodlopen .It Cm nodlopen
Set the Set the
.Dv DF_1_NOOPEN .Dv DF_1_NOOPEN
flag to indicate that the object may not be opened by flag to indicate that the object may not be opened by
.Xr dlopen 3 . .Xr dlopen 3 .
.Pp
.It Cm norelro .It Cm norelro
Do not indicate that portions of the object shold be mapped read-only Do not indicate that portions of the object shold be mapped read-only
after initial relocation processing. after initial relocation processing.
The object will omit the The object will omit the
.Dv PT_GNU_RELRO .Dv PT_GNU_RELRO
segment. segment.
.Pp
.It Cm notext .It Cm notext
Allow relocations against read-only segments. Allow relocations against read-only segments.
Sets the Sets the
.Dv DT_TEXTREL flag in the .Dv DT_TEXTREL flag in the
.Dv DYNAMIC .Dv DYNAMIC
section. section.
.Pp
.It Cm now .It Cm now
Set the Set the
.Dv DF_BIND_NOW .Dv DF_BIND_NOW
flag to indicate that the run-time loader should perform all relocation flag to indicate that the run-time loader should perform all relocation
processing as part of object initialization. processing as part of object initialization.
By default relocations may be performed on demand. By default relocations may be performed on demand.
.Pp
.It Cm origin .It Cm origin
Set the Set the
.Dv DF_ORIGIN .Dv DF_ORIGIN
flag to indicate that the object requires flag to indicate that the object requires
$ORIGIN $ORIGIN
processing. processing.
.Pp
.It Cm retpolineplt .It Cm retpolineplt
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715. Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
.Pp
.It Cm rodynamic .It Cm rodynamic
Make the Make the
.Li .dynamic .Li .dynamic
@ -656,6 +693,18 @@ section read-only.
The The
.Dv DT_DEBUG .Dv DT_DEBUG
tag will not be emitted. tag will not be emitted.
.Pp
.It Cm separate-loadable-segments
.It Cm separate-code
.It Cm noseparate-code
Specify whether two adjacent PT_LOAD segments are allowed to overlap in pages.
.Cm noseparate-code
(default) allows overlap.
.Cm separate-code
allows overlap between two executable segments, or two non-executable segments.
.Cm separate-loadable-segments
disallows overlap.
.Pp
.It Cm stack-size Ns = Ns Ar size .It Cm stack-size Ns = Ns Ar size
Set the main thread's stack size to Set the main thread's stack size to
.Ar size . .Ar size .
@ -663,9 +712,11 @@ The stack size is recorded as the size of the
.Ar size . .Ar size .
.Dv PT_GNU_STACK .Dv PT_GNU_STACK
program segment. program segment.
.Pp
.It Cm text .It Cm text
Do not allow relocations against read-only segments. Do not allow relocations against read-only segments.
This is the default. This is the default.
.Pp
.It Cm wxneeded .It Cm wxneeded
Create a Create a
.Dv PT_OPENBSD_WXNEEDED .Dv PT_OPENBSD_WXNEEDED

View File

@ -0,0 +1,47 @@
//===- DWARF.h --------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLD_DWARF_H
#define LLD_DWARF_H
#include "lld/Common/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
#include <memory>
#include <string>
namespace llvm {
struct DILineInfo;
} // namespace llvm
namespace lld {
class DWARFCache {
public:
DWARFCache(std::unique_ptr<llvm::DWARFContext> dwarf);
llvm::Optional<llvm::DILineInfo> getDILineInfo(uint64_t offset,
uint64_t sectionIndex);
llvm::Optional<std::pair<std::string, unsigned>>
getVariableLoc(StringRef name);
private:
std::unique_ptr<llvm::DWARFContext> dwarf;
std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
struct VarLoc {
const llvm::DWARFDebugLine::LineTable *lt;
unsigned file;
unsigned line;
};
llvm::DenseMap<StringRef, VarLoc> variableLoc;
};
} // namespace lld
#endif

View File

@ -87,7 +87,6 @@ class ErrorHandler {
StringRef errorLimitExceededMsg = "too many errors emitted, stopping now"; StringRef errorLimitExceededMsg = "too many errors emitted, stopping now";
StringRef logName = "lld"; StringRef logName = "lld";
llvm::raw_ostream *errorOS = &llvm::errs(); llvm::raw_ostream *errorOS = &llvm::errs();
bool colorDiagnostics = llvm::errs().has_colors();
bool exitEarly = true; bool exitEarly = true;
bool fatalWarnings = false; bool fatalWarnings = false;
bool verbose = false; bool verbose = false;
@ -102,12 +101,16 @@ class ErrorHandler {
std::unique_ptr<llvm::FileOutputBuffer> outputBuffer; std::unique_ptr<llvm::FileOutputBuffer> outputBuffer;
private: private:
void printHeader(StringRef s, raw_ostream::Colors c, const Twine &msg); using Colors = raw_ostream::Colors;
std::string getLocation(const Twine &msg);
}; };
/// Returns the default error handler. /// Returns the default error handler.
ErrorHandler &errorHandler(); ErrorHandler &errorHandler();
void enableColors(bool enable);
inline void error(const Twine &msg) { errorHandler().error(msg); } inline void error(const Twine &msg) { errorHandler().error(msg); }
inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg) { inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg) {
errorHandler().fatal(msg); errorHandler().fatal(msg);

View File

@ -17,6 +17,7 @@
// This should be the only #include, force #includes of all the others on // This should be the only #include, force #includes of all the others on
// clients. // clients.
#include "llvm/ADT/Hashing.h" #include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Casting.h" #include "llvm/Support/Casting.h"
#include <utility> #include <utility>

View File

@ -18,9 +18,8 @@
namespace lld { namespace lld {
// Returns a demangled C++ symbol name. If Name is not a mangled // Returns a demangled C++ symbol name. If Name is not a mangled
// name, it returns Optional::None. // name, it returns name.
llvm::Optional<std::string> demangleItanium(llvm::StringRef name); std::string demangleItanium(llvm::StringRef name);
llvm::Optional<std::string> demangleMSVC(llvm::StringRef s);
std::vector<uint8_t> parseHex(llvm::StringRef s); std::vector<uint8_t> parseHex(llvm::StringRef s);
bool isValidCIdentifier(llvm::StringRef s); bool isValidCIdentifier(llvm::StringRef s);

View File

@ -16,6 +16,7 @@
namespace lld { namespace lld {
llvm::TargetOptions initTargetOptionsFromCodeGenFlags(); llvm::TargetOptions initTargetOptionsFromCodeGenFlags();
llvm::Optional<llvm::Reloc::Model> getRelocModelFromCMModel();
llvm::Optional<llvm::CodeModel::Model> getCodeModelFromCMModel(); llvm::Optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
std::string getCPUStr(); std::string getCPUStr();
std::vector<std::string> getMAttrs(); std::vector<std::string> getMAttrs();

View File

@ -16,6 +16,7 @@
#include "llvm/ADT/Optional.h" #include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Twine.h" #include "llvm/ADT/Twine.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorHandling.h"
#include <functional> #include <functional>
#include <memory> #include <memory>

View File

@ -101,7 +101,7 @@ class MachOLinkingContext : public LinkingContext {
auto file = std::unique_ptr<T>(new T(std::forward<Args>(args)...)); auto file = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
auto *filePtr = file.get(); auto *filePtr = file.get();
auto *ctx = const_cast<MachOLinkingContext *>(this); auto *ctx = const_cast<MachOLinkingContext *>(this);
ctx->getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); ctx->getNodes().push_back(std::make_unique<FileNode>(std::move(file)));
return filePtr; return filePtr;
} }

View File

@ -95,7 +95,7 @@ class DarwinLdOptTable : public llvm::opt::OptTable {
static std::vector<std::unique_ptr<File>> static std::vector<std::unique_ptr<File>>
makeErrorFile(StringRef path, std::error_code ec) { makeErrorFile(StringRef path, std::error_code ec) {
std::vector<std::unique_ptr<File>> result; std::vector<std::unique_ptr<File>> result;
result.push_back(llvm::make_unique<ErrorFile>(path, ec)); result.push_back(std::make_unique<ErrorFile>(path, ec));
return result; return result;
} }
@ -160,7 +160,7 @@ static void addFile(StringRef path, MachOLinkingContext &ctx,
std::vector<std::unique_ptr<File>> files = std::vector<std::unique_ptr<File>> files =
loadFile(ctx, path, loadWholeArchive, upwardDylib); loadFile(ctx, path, loadWholeArchive, upwardDylib);
for (std::unique_ptr<File> &file : files) for (std::unique_ptr<File> &file : files)
ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); ctx.getNodes().push_back(std::make_unique<FileNode>(std::move(file)));
} }
// Export lists are one symbol per line. Blank lines are ignored. // Export lists are one symbol per line. Blank lines are ignored.
@ -1138,7 +1138,7 @@ static void createFiles(MachOLinkingContext &ctx, bool Implicit) {
ctx.createInternalFiles(Files); ctx.createInternalFiles(Files);
for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) { for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) {
auto &members = ctx.getNodes(); auto &members = ctx.getNodes();
members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i))); members.insert(members.begin(), std::make_unique<FileNode>(std::move(*i)));
} }
} }
@ -1151,7 +1151,7 @@ bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
"'-error-limit 0' to see all errors)"; "'-error-limit 0' to see all errors)";
errorHandler().errorOS = &Error; errorHandler().errorOS = &Error;
errorHandler().exitEarly = CanExitEarly; errorHandler().exitEarly = CanExitEarly;
errorHandler().colorDiagnostics = Error.has_colors(); enableColors(Error.has_colors());
MachOLinkingContext ctx; MachOLinkingContext ctx;
if (!parse(args, ctx)) if (!parse(args, ctx))
@ -1185,7 +1185,7 @@ bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
merged = mergedFile.get(); merged = mergedFile.get();
auto &members = ctx.getNodes(); auto &members = ctx.getNodes();
members.insert(members.begin(), members.insert(members.begin(),
llvm::make_unique<FileNode>(std::move(mergedFile))); std::make_unique<FileNode>(std::move(mergedFile)));
} }
resolveTask.end(); resolveTask.end();

View File

@ -210,7 +210,7 @@ class ArchiveReader : public Reader {
const Registry &reg) const override { const Registry &reg) const override {
StringRef path = mb->getBufferIdentifier(); StringRef path = mb->getBufferIdentifier();
std::unique_ptr<File> ret = std::unique_ptr<File> ret =
llvm::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading); std::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading);
return std::move(ret); return std::move(ret);
} }

View File

@ -181,6 +181,20 @@ class ArchHandler_x86_64 : public ArchHandler {
FindAddressForAtom addressForAtom, FindAddressForAtom addressForAtom,
normalized::Relocations &relocs) override; normalized::Relocations &relocs) override;
bool isDataInCodeTransition(Reference::KindValue refKind) override {
return refKind == modeCode || refKind == modeData;
}
Reference::KindValue dataInCodeTransitionStart(
const MachODefinedAtom &atom) override {
return modeData;
}
Reference::KindValue dataInCodeTransitionEnd(
const MachODefinedAtom &atom) override {
return modeCode;
}
private: private:
static const Registry::KindStrings _sKindStrings[]; static const Registry::KindStrings _sKindStrings[];
static const StubInfo _sStubInfo; static const StubInfo _sStubInfo;
@ -188,6 +202,9 @@ class ArchHandler_x86_64 : public ArchHandler {
enum X86_64Kind: Reference::KindValue { enum X86_64Kind: Reference::KindValue {
invalid, /// for error condition invalid, /// for error condition
modeCode, /// Content starting at this offset is code.
modeData, /// Content starting at this offset is data.
// Kinds found in mach-o .o files: // Kinds found in mach-o .o files:
branch32, /// ex: call _foo branch32, /// ex: call _foo
ripRel32, /// ex: movq _foo(%rip), %rax ripRel32, /// ex: movq _foo(%rip), %rax
@ -242,24 +259,34 @@ class ArchHandler_x86_64 : public ArchHandler {
}; };
const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch32), LLD_KIND_STRING_ENTRY(invalid),
LLD_KIND_STRING_ENTRY(ripRel32), LLD_KIND_STRING_ENTRY(ripRel32Minus1), LLD_KIND_STRING_ENTRY(modeCode),
LLD_KIND_STRING_ENTRY(ripRel32Minus2), LLD_KIND_STRING_ENTRY(ripRel32Minus4), LLD_KIND_STRING_ENTRY(modeData),
LLD_KIND_STRING_ENTRY(branch32),
LLD_KIND_STRING_ENTRY(ripRel32),
LLD_KIND_STRING_ENTRY(ripRel32Minus1),
LLD_KIND_STRING_ENTRY(ripRel32Minus2),
LLD_KIND_STRING_ENTRY(ripRel32Minus4),
LLD_KIND_STRING_ENTRY(ripRel32Anon), LLD_KIND_STRING_ENTRY(ripRel32Anon),
LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon),
LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon),
LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon),
LLD_KIND_STRING_ENTRY(ripRel32GotLoad), LLD_KIND_STRING_ENTRY(ripRel32GotLoad),
LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea), LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea),
LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(ripRel32Tlv), LLD_KIND_STRING_ENTRY(ripRel32Got),
LLD_KIND_STRING_ENTRY(ripRel32Tlv),
LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyPointer),
LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon), LLD_KIND_STRING_ENTRY(pointer64),
LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64), LLD_KIND_STRING_ENTRY(pointer64Anon),
LLD_KIND_STRING_ENTRY(delta32Anon), LLD_KIND_STRING_ENTRY(delta64Anon), LLD_KIND_STRING_ENTRY(delta32),
LLD_KIND_STRING_ENTRY(delta64),
LLD_KIND_STRING_ENTRY(delta32Anon),
LLD_KIND_STRING_ENTRY(delta64Anon),
LLD_KIND_STRING_ENTRY(negDelta64), LLD_KIND_STRING_ENTRY(negDelta64),
LLD_KIND_STRING_ENTRY(negDelta32), LLD_KIND_STRING_ENTRY(negDelta32),
LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), LLD_KIND_STRING_ENTRY(imageOffset),
LLD_KIND_STRING_ENTRY(imageOffsetGot),
LLD_KIND_STRING_ENTRY(unwindFDEToFunction), LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
LLD_KIND_STRING_ENTRY(tlvInitSectionOffset), LLD_KIND_STRING_ENTRY(tlvInitSectionOffset),
@ -601,6 +628,8 @@ void ArchHandler_x86_64::applyFixupFinal(
case negDelta32: case negDelta32:
*loc32 = fixupAddress - targetAddress + ref.addend(); *loc32 = fixupAddress - targetAddress + ref.addend();
return; return;
case modeCode:
case modeData:
case lazyPointer: case lazyPointer:
// Do nothing // Do nothing
return; return;
@ -720,6 +749,8 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,
case unwindInfoToEhFrame: case unwindInfoToEhFrame:
llvm_unreachable("fixup implies __unwind_info"); llvm_unreachable("fixup implies __unwind_info");
return; return;
case modeCode:
case modeData:
case unwindFDEToFunction: case unwindFDEToFunction:
// Do nothing for now // Do nothing for now
return; return;
@ -743,6 +774,9 @@ void ArchHandler_x86_64::appendSectionRelocations(
assert(ref.kindArch() == Reference::KindArch::x86_64); assert(ref.kindArch() == Reference::KindArch::x86_64);
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
switch (static_cast<X86_64Kind>(ref.kindValue())) { switch (static_cast<X86_64Kind>(ref.kindValue())) {
case modeCode:
case modeData:
return;
case branch32: case branch32:
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4); X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4);

View File

@ -573,7 +573,7 @@ class CompactUnwindPass : public Pass {
void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) { void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
assert(ctx.needsCompactUnwindPass()); assert(ctx.needsCompactUnwindPass());
pm.add(llvm::make_unique<CompactUnwindPass>(ctx)); pm.add(std::make_unique<CompactUnwindPass>(ctx));
} }
} // end namesapce mach_o } // end namesapce mach_o

View File

@ -12,6 +12,7 @@
#include "lld/Core/Atom.h" #include "lld/Core/Atom.h"
#include <vector> #include <vector>
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Format.h" #include "llvm/Support/Format.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"

View File

@ -176,7 +176,7 @@ class GOTPass : public Pass {
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) { void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
assert(ctx.needsGOTPass()); assert(ctx.needsGOTPass());
pm.add(llvm::make_unique<GOTPass>(ctx)); pm.add(std::make_unique<GOTPass>(ctx));
} }
} // end namesapce mach_o } // end namesapce mach_o

View File

@ -478,7 +478,7 @@ llvm::Error LayoutPass::perform(SimpleFile &mergedFile) {
} }
void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) { void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) {
pm.add(llvm::make_unique<LayoutPass>( pm.add(std::make_unique<LayoutPass>(
ctx.registry(), [&](const DefinedAtom * left, const DefinedAtom * right, ctx.registry(), [&](const DefinedAtom * left, const DefinedAtom * right,
bool & leftBeforeRight) ->bool { bool & leftBeforeRight) ->bool {
return ctx.customAtomOrderer(left, right, leftBeforeRight); return ctx.customAtomOrderer(left, right, leftBeforeRight);

View File

@ -802,9 +802,9 @@ void MachOLinkingContext::addSectCreateSection(
std::unique_ptr<MemoryBuffer> content) { std::unique_ptr<MemoryBuffer> content) {
if (!_sectCreateFile) { if (!_sectCreateFile) {
auto sectCreateFile = llvm::make_unique<mach_o::SectCreateFile>(); auto sectCreateFile = std::make_unique<mach_o::SectCreateFile>();
_sectCreateFile = sectCreateFile.get(); _sectCreateFile = sectCreateFile.get();
getNodes().push_back(llvm::make_unique<FileNode>(std::move(sectCreateFile))); getNodes().push_back(std::make_unique<FileNode>(std::move(sectCreateFile)));
} }
assert(_sectCreateFile && "sectcreate file does not exist."); assert(_sectCreateFile && "sectcreate file does not exist.");
@ -897,8 +897,8 @@ static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
std::error_code MachOLinkingContext::createDependencyFile(StringRef path) { std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
std::error_code ec; std::error_code ec;
_dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(new _dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(
llvm::raw_fd_ostream(path, ec, llvm::sys::fs::F_None)); new llvm::raw_fd_ostream(path, ec, llvm::sys::fs::OF_None));
if (ec) { if (ec) {
_dependencyInfo.reset(); _dependencyInfo.reset();
return ec; return ec;
@ -1019,7 +1019,7 @@ void MachOLinkingContext::finalizeInputFiles() {
return !isLibrary(a) && isLibrary(b); return !isLibrary(a) && isLibrary(b);
}); });
size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary); size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary);
elements.push_back(llvm::make_unique<GroupEnd>(numLibs)); elements.push_back(std::make_unique<GroupEnd>(numLibs));
} }
llvm::Error MachOLinkingContext::handleLoadedFile(File &file) { llvm::Error MachOLinkingContext::handleLoadedFile(File &file) {

View File

@ -542,7 +542,7 @@ class MachOObjectReader : public Reader {
loadFile(std::unique_ptr<MemoryBuffer> mb, loadFile(std::unique_ptr<MemoryBuffer> mb,
const Registry &registry) const override { const Registry &registry) const override {
std::unique_ptr<File> ret = std::unique_ptr<File> ret =
llvm::make_unique<MachOFile>(std::move(mb), &_ctx); std::make_unique<MachOFile>(std::move(mb), &_ctx);
return std::move(ret); return std::move(ret);
} }
@ -568,7 +568,7 @@ class MachODylibReader : public Reader {
loadFile(std::unique_ptr<MemoryBuffer> mb, loadFile(std::unique_ptr<MemoryBuffer> mb,
const Registry &registry) const override { const Registry &registry) const override {
std::unique_ptr<File> ret = std::unique_ptr<File> ret =
llvm::make_unique<MachODylibFile>(std::move(mb), &_ctx); std::make_unique<MachODylibFile>(std::move(mb), &_ctx);
return std::move(ret); return std::move(ret);
} }

View File

@ -626,17 +626,19 @@ llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) {
+ _file.sections.size() * sizeof(typename T::section); + _file.sections.size() * sizeof(typename T::section);
uint8_t *next = lc + seg->cmdsize; uint8_t *next = lc + seg->cmdsize;
memset(seg->segname, 0, 16); memset(seg->segname, 0, 16);
seg->flags = 0;
seg->vmaddr = 0; seg->vmaddr = 0;
seg->vmsize = _file.sections.back().address
+ _file.sections.back().content.size();
seg->fileoff = _endOfLoadCommands; seg->fileoff = _endOfLoadCommands;
seg->filesize = _sectInfo[&_file.sections.back()].fileOffset +
_file.sections.back().content.size() -
_sectInfo[&_file.sections.front()].fileOffset;
seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE; seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
seg->nsects = _file.sections.size(); seg->nsects = _file.sections.size();
seg->flags = 0; if (seg->nsects) {
seg->vmsize = _file.sections.back().address
+ _file.sections.back().content.size();
seg->filesize = _sectInfo[&_file.sections.back()].fileOffset +
_file.sections.back().content.size() -
_sectInfo[&_file.sections.front()].fileOffset;
}
if (_swap) if (_swap)
swapStruct(*seg); swapStruct(*seg);
typename T::section *sout = reinterpret_cast<typename T::section*> typename T::section *sout = reinterpret_cast<typename T::section*>

View File

@ -717,7 +717,7 @@ llvm::Error parseStabs(MachOFile &file,
// FIXME: Kill this off when we can move to sane yaml parsing. // FIXME: Kill this off when we can move to sane yaml parsing.
std::unique_ptr<BumpPtrAllocator> allocator; std::unique_ptr<BumpPtrAllocator> allocator;
if (copyRefs) if (copyRefs)
allocator = llvm::make_unique<BumpPtrAllocator>(); allocator = std::make_unique<BumpPtrAllocator>();
enum { start, inBeginEnd } state = start; enum { start, inBeginEnd } state = start;
@ -812,7 +812,7 @@ llvm::Error parseStabs(MachOFile &file,
stabsList.push_back(stab); stabsList.push_back(stab);
} }
file.setDebugInfo(llvm::make_unique<StabsDebugInfo>(std::move(stabsList))); file.setDebugInfo(std::make_unique<StabsDebugInfo>(std::move(stabsList)));
// FIXME: Kill this off when we fix YAML memory ownership. // FIXME: Kill this off when we fix YAML memory ownership.
file.debugInfo()->setAllocator(std::move(allocator)); file.debugInfo()->setAllocator(std::move(allocator));
@ -832,10 +832,10 @@ dataExtractorFromSection(const NormalizedFile &normalizedFile,
// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE // FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
// inspection" code if possible. // inspection" code if possible.
static uint32_t getCUAbbrevOffset(llvm::DataExtractor abbrevData, static uint64_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
uint64_t abbrCode) { uint64_t abbrCode) {
uint64_t curCode; uint64_t curCode;
uint32_t offset = 0; uint64_t offset = 0;
while ((curCode = abbrevData.getULEB128(&offset)) != abbrCode) { while ((curCode = abbrevData.getULEB128(&offset)) != abbrCode) {
// Tag // Tag
abbrevData.getULEB128(&offset); abbrevData.getULEB128(&offset);
@ -853,13 +853,13 @@ static uint32_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
static Expected<const char *> static Expected<const char *>
getIndexedString(const NormalizedFile &normalizedFile, getIndexedString(const NormalizedFile &normalizedFile,
llvm::dwarf::Form form, llvm::DataExtractor infoData, llvm::dwarf::Form form, llvm::DataExtractor infoData,
uint32_t &infoOffset, const Section &stringsSection) { uint64_t &infoOffset, const Section &stringsSection) {
if (form == llvm::dwarf::DW_FORM_string) if (form == llvm::dwarf::DW_FORM_string)
return infoData.getCStr(&infoOffset); return infoData.getCStr(&infoOffset);
if (form != llvm::dwarf::DW_FORM_strp) if (form != llvm::dwarf::DW_FORM_strp)
return llvm::make_error<GenericError>( return llvm::make_error<GenericError>(
"string field encoded without DW_FORM_strp"); "string field encoded without DW_FORM_strp");
uint32_t stringOffset = infoData.getU32(&infoOffset); uint64_t stringOffset = infoData.getU32(&infoOffset);
llvm::DataExtractor stringsData = llvm::DataExtractor stringsData =
dataExtractorFromSection(normalizedFile, stringsSection); dataExtractorFromSection(normalizedFile, stringsSection);
return stringsData.getCStr(&stringOffset); return stringsData.getCStr(&stringOffset);
@ -875,7 +875,7 @@ readCompUnit(const NormalizedFile &normalizedFile,
StringRef path) { StringRef path) {
// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE // FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
// inspection" code if possible. // inspection" code if possible.
uint32_t offset = 0; uint64_t offset = 0;
llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32; llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32;
auto infoData = dataExtractorFromSection(normalizedFile, info); auto infoData = dataExtractorFromSection(normalizedFile, info);
uint32_t length = infoData.getU32(&offset); uint32_t length = infoData.getU32(&offset);
@ -897,7 +897,7 @@ readCompUnit(const NormalizedFile &normalizedFile,
uint32_t abbrCode = infoData.getULEB128(&offset); uint32_t abbrCode = infoData.getULEB128(&offset);
auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev); auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev);
uint32_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode); uint64_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode);
uint64_t tag = abbrevData.getULEB128(&abbrevOffset); uint64_t tag = abbrevData.getULEB128(&abbrevOffset);
if (tag != llvm::dwarf::DW_TAG_compile_unit) if (tag != llvm::dwarf::DW_TAG_compile_unit)
return llvm::make_error<GenericError>("top level DIE is not a compile unit"); return llvm::make_error<GenericError>("top level DIE is not a compile unit");
@ -974,11 +974,11 @@ llvm::Error parseDebugInfo(MachOFile &file,
// memory ownership. // memory ownership.
std::unique_ptr<BumpPtrAllocator> allocator; std::unique_ptr<BumpPtrAllocator> allocator;
if (copyRefs) { if (copyRefs) {
allocator = llvm::make_unique<BumpPtrAllocator>(); allocator = std::make_unique<BumpPtrAllocator>();
tuOrErr->name = copyDebugString(tuOrErr->name, *allocator); tuOrErr->name = copyDebugString(tuOrErr->name, *allocator);
tuOrErr->path = copyDebugString(tuOrErr->path, *allocator); tuOrErr->path = copyDebugString(tuOrErr->path, *allocator);
} }
file.setDebugInfo(llvm::make_unique<DwarfDebugInfo>(std::move(*tuOrErr))); file.setDebugInfo(std::make_unique<DwarfDebugInfo>(std::move(*tuOrErr)));
if (copyRefs) if (copyRefs)
file.debugInfo()->setAllocator(std::move(allocator)); file.debugInfo()->setAllocator(std::move(allocator));
} else } else

View File

@ -124,7 +124,7 @@ class ObjCPass : public Pass {
void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx) { void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx) {
pm.add(llvm::make_unique<ObjCPass>(ctx)); pm.add(std::make_unique<ObjCPass>(ctx));
} }
} // end namespace mach_o } // end namespace mach_o

Some files were not shown because too many files have changed in this diff Show More