Merge ^/vendor/lld/dist up to its last change, and resolve conflicts.
This commit is contained in:
commit
85868e8a1d
@ -28,8 +28,10 @@ add_lld_library(lldCOFF
|
||||
BinaryFormat
|
||||
Core
|
||||
DebugInfoCodeView
|
||||
DebugInfoDWARF
|
||||
DebugInfoMSF
|
||||
DebugInfoPDB
|
||||
Demangle
|
||||
LibDriver
|
||||
LTO
|
||||
MC
|
||||
|
@ -122,6 +122,7 @@ struct Configuration {
|
||||
bool dll = false;
|
||||
StringRef implib;
|
||||
std::vector<Export> exports;
|
||||
bool hadExplicitExports;
|
||||
std::set<std::string> delayLoads;
|
||||
std::map<std::string, int> dllOrder;
|
||||
Symbol *delayLoadHelper = nullptr;
|
||||
@ -189,6 +190,9 @@ struct Configuration {
|
||||
// Used for /thinlto-object-suffix-replace:
|
||||
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
|
||||
|
||||
// Used for /lto-obj-path:
|
||||
llvm::StringRef ltoObjPath;
|
||||
|
||||
uint64_t align = 4096;
|
||||
uint64_t imageBase = -1;
|
||||
uint64_t fileAlign = 512;
|
||||
|
@ -135,7 +135,7 @@ class NullChunk : public NonSectionChunk {
|
||||
static std::vector<std::vector<DefinedImportData *>>
|
||||
binImports(const std::vector<DefinedImportData *> &imports) {
|
||||
// Group DLL-imported symbols by DLL name because that's how
|
||||
// symbols are layed out in the import descriptor table.
|
||||
// symbols are laid out in the import descriptor table.
|
||||
auto less = [](const std::string &a, const std::string &b) {
|
||||
return config->dllOrder[a] < config->dllOrder[b];
|
||||
};
|
||||
@ -188,7 +188,7 @@ class DelayDirectoryChunk : public NonSectionChunk {
|
||||
|
||||
// Initial contents for delay-loaded functions.
|
||||
// This code calls __delayLoadHelper2 function to resolve a symbol
|
||||
// and then overwrites its jump table slot with the result
|
||||
// which then overwrites its jump table slot with the result
|
||||
// for subsequent function calls.
|
||||
static const uint8_t thunkX64[] = {
|
||||
0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]
|
||||
|
@ -17,11 +17,12 @@
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
namespace {
|
||||
// The TypeServerSource class represents a PDB type server, a file referenced by
|
||||
// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
|
||||
@ -96,27 +97,25 @@ TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {
|
||||
GC.push_back(std::unique_ptr<TpiSource>(this));
|
||||
}
|
||||
|
||||
TpiSource *lld::coff::makeTpiSource(const ObjFile *f) {
|
||||
TpiSource *makeTpiSource(const ObjFile *f) {
|
||||
return new TpiSource(TpiSource::Regular, f);
|
||||
}
|
||||
|
||||
TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f,
|
||||
TpiSource *makeUseTypeServerSource(const ObjFile *f,
|
||||
const TypeServer2Record *ts) {
|
||||
TypeServerSource::enqueue(f, *ts);
|
||||
return new UseTypeServerSource(f, ts);
|
||||
}
|
||||
|
||||
TpiSource *lld::coff::makePrecompSource(const ObjFile *f) {
|
||||
TpiSource *makePrecompSource(const ObjFile *f) {
|
||||
return new PrecompSource(f);
|
||||
}
|
||||
|
||||
TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f,
|
||||
TpiSource *makeUsePrecompSource(const ObjFile *f,
|
||||
const PrecompRecord *precomp) {
|
||||
return new UsePrecompSource(f, precomp);
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
template <>
|
||||
const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
|
||||
assert(source->kind == TpiSource::UsingPCH);
|
||||
@ -128,8 +127,6 @@ const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
|
||||
assert(source->kind == TpiSource::UsingPDB);
|
||||
return ((const UseTypeServerSource *)source)->typeServerDependency;
|
||||
}
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
std::map<std::string, std::pair<std::string, TypeServerSource *>>
|
||||
TypeServerSource::instances;
|
||||
@ -210,8 +207,7 @@ TypeServerSource::findFromFile(const ObjFile *dependentFile) {
|
||||
|
||||
// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
|
||||
// moved here.
|
||||
Expected<llvm::pdb::NativeSession *>
|
||||
lld::coff::findTypeServerSource(const ObjFile *f) {
|
||||
Expected<llvm::pdb::NativeSession *> findTypeServerSource(const ObjFile *f) {
|
||||
Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
|
||||
if (!ts)
|
||||
return ts.takeError();
|
||||
@ -231,7 +227,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
|
||||
if (!it.second)
|
||||
return; // another OBJ already scheduled this PDB for load
|
||||
|
||||
driver->enqueuePath(*p, false);
|
||||
driver->enqueuePath(*p, false, false);
|
||||
}
|
||||
|
||||
// Create an instance of TypeServerSource or an error string if the PDB couldn't
|
||||
@ -239,7 +235,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
|
||||
// will be merged in. NOTE - a PDB load failure is not a link error: some
|
||||
// debug info will simply be missing from the final PDB - that is the default
|
||||
// accepted behavior.
|
||||
void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) {
|
||||
void loadTypeServerSource(llvm::MemoryBufferRef m) {
|
||||
std::string path = normalizePdbPath(m.getBufferIdentifier());
|
||||
|
||||
Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
|
||||
@ -266,3 +262,6 @@ Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
|
||||
return info.takeError();
|
||||
return new TypeServerSource(m, session.release());
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/BinaryFormat/Magic.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/ArchiveWriter.h"
|
||||
#include "llvm/Object/COFFImportFile.h"
|
||||
#include "llvm/Object/COFFModuleDefinition.h"
|
||||
@ -63,16 +64,16 @@ LinkerDriver *driver;
|
||||
bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) {
|
||||
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
|
||||
errorHandler().errorOS = &diag;
|
||||
errorHandler().colorDiagnostics = diag.has_colors();
|
||||
errorHandler().errorLimitExceededMsg =
|
||||
"too many errors emitted, stopping now"
|
||||
" (use /errorlimit:0 to see all errors)";
|
||||
errorHandler().exitEarly = canExitEarly;
|
||||
enableColors(diag.has_colors());
|
||||
|
||||
config = make<Configuration>();
|
||||
|
||||
symtab = make<SymbolTable>();
|
||||
|
||||
driver = make<LinkerDriver>();
|
||||
|
||||
driver->link(args);
|
||||
|
||||
// Call exit() if we can to avoid calling destructors.
|
||||
@ -170,7 +171,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
|
||||
}
|
||||
|
||||
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||
bool wholeArchive) {
|
||||
bool wholeArchive, bool lazy) {
|
||||
StringRef filename = mb->getBufferIdentifier();
|
||||
|
||||
MemoryBufferRef mbref = takeBuffer(std::move(mb));
|
||||
@ -188,18 +189,25 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||
Archive *archive = file.get();
|
||||
make<std::unique_ptr<Archive>>(std::move(file)); // take ownership
|
||||
|
||||
int memberIndex = 0;
|
||||
for (MemoryBufferRef m : getArchiveMembers(archive))
|
||||
addArchiveBuffer(m, "<whole-archive>", filename, 0);
|
||||
addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++);
|
||||
return;
|
||||
}
|
||||
symtab->addFile(make<ArchiveFile>(mbref));
|
||||
break;
|
||||
case file_magic::bitcode:
|
||||
symtab->addFile(make<BitcodeFile>(mbref, "", 0));
|
||||
if (lazy)
|
||||
symtab->addFile(make<LazyObjFile>(mbref));
|
||||
else
|
||||
symtab->addFile(make<BitcodeFile>(mbref, "", 0));
|
||||
break;
|
||||
case file_magic::coff_object:
|
||||
case file_magic::coff_import_library:
|
||||
symtab->addFile(make<ObjFile>(mbref));
|
||||
if (lazy)
|
||||
symtab->addFile(make<LazyObjFile>(mbref));
|
||||
else
|
||||
symtab->addFile(make<ObjFile>(mbref));
|
||||
break;
|
||||
case file_magic::pdb:
|
||||
loadTypeServerSource(mbref);
|
||||
@ -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 =
|
||||
std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
|
||||
std::string pathStr = path;
|
||||
@ -240,7 +248,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
|
||||
else
|
||||
error(msg + "; did you mean '" + nearest + "'");
|
||||
} else
|
||||
driver->addBuffer(std::move(mbOrErr.first), wholeArchive);
|
||||
driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
|
||||
});
|
||||
}
|
||||
|
||||
@ -303,9 +311,10 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
|
||||
auto mbOrErr = future->get();
|
||||
if (mbOrErr.second)
|
||||
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)),
|
||||
toCOFFString(sym), parentName,
|
||||
/*OffsetInArchive=*/0);
|
||||
toCOFFString(sym), "", /*OffsetInArchive=*/0);
|
||||
});
|
||||
}
|
||||
|
||||
@ -359,7 +368,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
|
||||
break;
|
||||
case OPT_defaultlib:
|
||||
if (Optional<StringRef> path = findLib(arg->getValue()))
|
||||
enqueuePath(*path, false);
|
||||
enqueuePath(*path, false, false);
|
||||
break;
|
||||
case OPT_entry:
|
||||
config->entry = addUndefined(mangle(arg->getValue()));
|
||||
@ -594,6 +603,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
|
||||
for (auto *arg : args) {
|
||||
switch (arg->getOption().getID()) {
|
||||
case OPT_linkrepro:
|
||||
case OPT_reproduce:
|
||||
case OPT_INPUT:
|
||||
case OPT_defaultlib:
|
||||
case OPT_libpath:
|
||||
@ -708,8 +718,7 @@ static std::string getImplibPath() {
|
||||
return out.str();
|
||||
}
|
||||
|
||||
//
|
||||
// The import name is caculated as the following:
|
||||
// The import name is calculated as follows:
|
||||
//
|
||||
// | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY
|
||||
// -----+----------------+---------------------+------------------
|
||||
@ -991,30 +1000,37 @@ static void parsePDBAltPath(StringRef altPath) {
|
||||
config->pdbAltPath = buf;
|
||||
}
|
||||
|
||||
/// Check that at most one resource obj file was used.
|
||||
/// Convert resource files and potentially merge input resource object
|
||||
/// trees into one resource tree.
|
||||
/// Call after ObjFile::Instances is complete.
|
||||
static void diagnoseMultipleResourceObjFiles() {
|
||||
// The .rsrc$01 section in a resource obj file contains a tree description
|
||||
// of resources. Merging multiple resource obj files would require merging
|
||||
// the trees instead of using usual linker section merging semantics.
|
||||
// Since link.exe disallows linking more than one resource obj file with
|
||||
// LNK4078, mirror that. The normal use of resource files is to give the
|
||||
// linker many .res files, which are then converted to a single resource obj
|
||||
// file internally, so this is not a big restriction in practice.
|
||||
ObjFile *resourceObjFile = nullptr;
|
||||
void LinkerDriver::convertResources() {
|
||||
std::vector<ObjFile *> resourceObjFiles;
|
||||
|
||||
for (ObjFile *f : ObjFile::instances) {
|
||||
if (!f->isResourceObjFile)
|
||||
continue;
|
||||
|
||||
if (!resourceObjFile) {
|
||||
resourceObjFile = f;
|
||||
continue;
|
||||
}
|
||||
|
||||
error(toString(f) +
|
||||
": more than one resource obj file not allowed, already got " +
|
||||
toString(resourceObjFile));
|
||||
if (f->isResourceObjFile())
|
||||
resourceObjFiles.push_back(f);
|
||||
}
|
||||
|
||||
if (!config->mingw &&
|
||||
(resourceObjFiles.size() > 1 ||
|
||||
(resourceObjFiles.size() == 1 && !resources.empty()))) {
|
||||
error((!resources.empty() ? "internal .obj file created from .res files"
|
||||
: toString(resourceObjFiles[1])) +
|
||||
": more than one resource obj file not allowed, already got " +
|
||||
toString(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
|
||||
@ -1055,11 +1071,25 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
|
||||
});
|
||||
}
|
||||
|
||||
static const char *libcallRoutineNames[] = {
|
||||
#define HANDLE_LIBCALL(code, name) name,
|
||||
#include "llvm/IR/RuntimeLibcalls.def"
|
||||
#undef HANDLE_LIBCALL
|
||||
};
|
||||
// lld has a feature to create a tar file containing all input files as well as
|
||||
// all command line options, so that other people can run lld again with exactly
|
||||
// the same inputs. This feature is accessible via /linkrepro and /reproduce.
|
||||
//
|
||||
// /linkrepro and /reproduce are very similar, but /linkrepro takes a directory
|
||||
// name while /reproduce takes a full path. We have /linkrepro for compatibility
|
||||
// with Microsoft link.exe.
|
||||
Optional<std::string> getReproduceFile(const opt::InputArgList &args) {
|
||||
if (auto *arg = args.getLastArg(OPT_reproduce))
|
||||
return std::string(arg->getValue());
|
||||
|
||||
if (auto *arg = args.getLastArg(OPT_linkrepro)) {
|
||||
SmallString<64> path = StringRef(arg->getValue());
|
||||
sys::path::append(path, "repro.tar");
|
||||
return path.str().str();
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
// Needed for LTO.
|
||||
@ -1079,7 +1109,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
|
||||
// Parse command line options.
|
||||
ArgParser parser;
|
||||
opt::InputArgList args = parser.parseLINK(argsArr);
|
||||
opt::InputArgList args = parser.parse(argsArr);
|
||||
|
||||
// Parse and evaluate -mllvm options.
|
||||
std::vector<const char *> v;
|
||||
@ -1123,22 +1153,20 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
// options are handled.
|
||||
config->mingw = args.hasArg(OPT_lldmingw);
|
||||
|
||||
if (auto *arg = args.getLastArg(OPT_linkrepro)) {
|
||||
SmallString<64> path = StringRef(arg->getValue());
|
||||
sys::path::append(path, "repro.tar");
|
||||
|
||||
// Handle /linkrepro and /reproduce.
|
||||
if (Optional<std::string> path = getReproduceFile(args)) {
|
||||
Expected<std::unique_ptr<TarWriter>> errOrWriter =
|
||||
TarWriter::create(path, "repro");
|
||||
TarWriter::create(*path, sys::path::stem(*path));
|
||||
|
||||
if (errOrWriter) {
|
||||
tar = std::move(*errOrWriter);
|
||||
} else {
|
||||
error("/linkrepro: failed to open " + path + ": " +
|
||||
error("/linkrepro: failed to open " + *path + ": " +
|
||||
toString(errOrWriter.takeError()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) {
|
||||
if (!args.hasArg(OPT_INPUT)) {
|
||||
if (args.hasArg(OPT_deffile))
|
||||
config->noEntry = true;
|
||||
else
|
||||
@ -1149,7 +1177,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
searchPaths.push_back("");
|
||||
for (auto *arg : args.filtered(OPT_libpath))
|
||||
searchPaths.push_back(arg->getValue());
|
||||
addLibSearchPaths();
|
||||
if (!args.hasArg(OPT_lldignoreenv))
|
||||
addLibSearchPaths();
|
||||
|
||||
// Handle /ignore
|
||||
for (auto *arg : args.filtered(OPT_ignore)) {
|
||||
@ -1481,6 +1510,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
getOldNewOptions(args, OPT_thinlto_prefix_replace);
|
||||
config->thinLTOObjectSuffixReplace =
|
||||
getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
|
||||
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path);
|
||||
// Handle miscellaneous boolean flags.
|
||||
config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
|
||||
config->allowIsolation =
|
||||
@ -1545,19 +1575,45 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Create a list of input files. Files can be given as arguments
|
||||
// for /defaultlib option.
|
||||
for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file))
|
||||
if (Optional<StringRef> path = findFile(arg->getValue()))
|
||||
enqueuePath(*path, isWholeArchive(*path));
|
||||
// Create a list of input files. These can be given as OPT_INPUT options
|
||||
// and OPT_wholearchive_file options, and we also need to track OPT_start_lib
|
||||
// and OPT_end_lib.
|
||||
bool inLib = false;
|
||||
for (auto *arg : args) {
|
||||
switch (arg->getOption().getID()) {
|
||||
case OPT_end_lib:
|
||||
if (!inLib)
|
||||
error("stray " + arg->getSpelling());
|
||||
inLib = false;
|
||||
break;
|
||||
case OPT_start_lib:
|
||||
if (inLib)
|
||||
error("nested " + arg->getSpelling());
|
||||
inLib = true;
|
||||
break;
|
||||
case OPT_wholearchive_file:
|
||||
if (Optional<StringRef> path = findFile(arg->getValue()))
|
||||
enqueuePath(*path, true, inLib);
|
||||
break;
|
||||
case OPT_INPUT:
|
||||
if (Optional<StringRef> path = findFile(arg->getValue()))
|
||||
enqueuePath(*path, isWholeArchive(*path), inLib);
|
||||
break;
|
||||
default:
|
||||
// Ignore other options.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Process files specified as /defaultlib. These should be enequeued after
|
||||
// other files, which is why they are in a separate loop.
|
||||
for (auto *arg : args.filtered(OPT_defaultlib))
|
||||
if (Optional<StringRef> path = findLib(arg->getValue()))
|
||||
enqueuePath(*path, false);
|
||||
enqueuePath(*path, false, false);
|
||||
|
||||
// Windows specific -- Create a resource file containing a manifest file.
|
||||
if (config->manifest == Configuration::Embed)
|
||||
addBuffer(createManifestRes(), false);
|
||||
addBuffer(createManifestRes(), false, false);
|
||||
|
||||
// Read all input files given via the command line.
|
||||
run();
|
||||
@ -1582,12 +1638,6 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
|
||||
parseFunctionPadMin(arg, config->machine);
|
||||
|
||||
// Input files can be Windows resource files (.res files). We use
|
||||
// WindowsResource to convert resource files to a regular COFF file,
|
||||
// then link the resulting file normally.
|
||||
if (!resources.empty())
|
||||
symtab->addFile(make<ObjFile>(convertResToCOFF(resources)));
|
||||
|
||||
if (tar)
|
||||
tar->append("response.txt",
|
||||
createResponseFile(args, filePaths,
|
||||
@ -1626,7 +1676,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
}
|
||||
|
||||
// Handle generation of import library from a def file.
|
||||
if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) {
|
||||
if (!args.hasArg(OPT_INPUT)) {
|
||||
fixupExports();
|
||||
createImportLibrary(/*asLib=*/true);
|
||||
return;
|
||||
@ -1672,8 +1722,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
|
||||
// Set default image name if neither /out or /def set it.
|
||||
if (config->outputFile.empty()) {
|
||||
config->outputFile = getOutputPath(
|
||||
(*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue());
|
||||
config->outputFile =
|
||||
getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue());
|
||||
}
|
||||
|
||||
// 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
|
||||
// compile those archive members by adding them to the link beforehand.
|
||||
if (!BitcodeFile::instances.empty())
|
||||
for (const char *s : libcallRoutineNames)
|
||||
for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
|
||||
symtab->addLibcall(s);
|
||||
|
||||
// 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"));
|
||||
} while (run());
|
||||
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
||||
// link those files (unless -thinlto-index-only was given, in which case we
|
||||
// resolve symbols and write indices, but don't generate native code or link).
|
||||
symtab->addCombinedLTOObjects();
|
||||
|
||||
// If -thinlto-index-only is given, we should create only "index
|
||||
// files" and not object files. Index file creation is already done
|
||||
// in addCombinedLTOObject, so we are done if that's the case.
|
||||
if (config->thinLTOIndexOnly)
|
||||
return;
|
||||
|
||||
// If we generated native object files from bitcode files, this resolves
|
||||
// references to the symbols we use from them.
|
||||
run();
|
||||
|
||||
if (args.hasArg(OPT_include_optional)) {
|
||||
// Handle /includeoptional
|
||||
for (auto *arg : args.filtered(OPT_include_optional))
|
||||
if (dyn_cast_or_null<Lazy>(symtab->find(arg->getValue())))
|
||||
if (dyn_cast_or_null<LazyArchive>(symtab->find(arg->getValue())))
|
||||
addUndefined(arg->getValue());
|
||||
while (run());
|
||||
}
|
||||
@ -1821,11 +1853,36 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
run();
|
||||
}
|
||||
|
||||
// Make sure we have resolved all symbols.
|
||||
symtab->reportRemainingUndefines();
|
||||
// At this point, we should not have any symbols that cannot be resolved.
|
||||
// If we are going to do codegen for link-time optimization, check for
|
||||
// unresolvable symbols first, so we don't spend time generating code that
|
||||
// will fail to link anyway.
|
||||
if (!BitcodeFile::instances.empty() && !config->forceUnresolved)
|
||||
symtab->reportUnresolvable();
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
||||
// link those files (unless -thinlto-index-only was given, in which case we
|
||||
// resolve symbols and write indices, but don't generate native code or link).
|
||||
symtab->addCombinedLTOObjects();
|
||||
|
||||
// If -thinlto-index-only is given, we should create only "index
|
||||
// files" and not object files. Index file creation is already done
|
||||
// in addCombinedLTOObject, so we are done if that's the case.
|
||||
if (config->thinLTOIndexOnly)
|
||||
return;
|
||||
|
||||
// If we generated native object files from bitcode files, this resolves
|
||||
// references to the symbols we use from them.
|
||||
run();
|
||||
|
||||
// Resolve remaining undefined symbols and warn about imported locals.
|
||||
symtab->resolveRemainingUndefines();
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
config->hadExplicitExports = !config->exports.empty();
|
||||
if (config->mingw) {
|
||||
// In MinGW, all symbols are automatically exported if no symbols
|
||||
// are chosen to be exported.
|
||||
@ -1849,10 +1906,12 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
}
|
||||
|
||||
// Windows specific -- when we are creating a .dll file, we also
|
||||
// need to create a .lib file.
|
||||
// need to create a .lib file. In MinGW mode, we only do that when the
|
||||
// -implib option is given explicitly, for compatibility with GNU ld.
|
||||
if (!config->exports.empty() || config->dll) {
|
||||
fixupExports();
|
||||
createImportLibrary(/*asLib=*/false);
|
||||
if (!config->mingw || !config->implib.empty())
|
||||
createImportLibrary(/*asLib=*/false);
|
||||
assignExportOrdinals();
|
||||
}
|
||||
|
||||
@ -1896,7 +1955,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||
markLive(symtab->getChunks());
|
||||
|
||||
// Needs to happen after the last call to addFile().
|
||||
diagnoseMultipleResourceObjFiles();
|
||||
convertResources();
|
||||
|
||||
// Identify identical COMDAT sections to merge them.
|
||||
if (config->doICF) {
|
||||
|
@ -43,8 +43,8 @@ class COFFOptTable : public llvm::opt::OptTable {
|
||||
|
||||
class ArgParser {
|
||||
public:
|
||||
// Concatenate LINK environment variable and given arguments and parse them.
|
||||
llvm::opt::InputArgList parseLINK(std::vector<const char *> args);
|
||||
// Parses command line options.
|
||||
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
|
||||
|
||||
// Tokenizes a given string and then parses as command line options.
|
||||
llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); }
|
||||
@ -56,8 +56,8 @@ class ArgParser {
|
||||
parseDirectives(StringRef s);
|
||||
|
||||
private:
|
||||
// Parses command line options.
|
||||
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
|
||||
// Concatenate LINK environment variable.
|
||||
void addLINK(SmallVector<const char *, 256> &argv);
|
||||
|
||||
std::vector<const char *> tokenize(StringRef s);
|
||||
|
||||
@ -77,7 +77,7 @@ class LinkerDriver {
|
||||
|
||||
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
|
||||
|
||||
void enqueuePath(StringRef path, bool wholeArchive);
|
||||
void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
|
||||
@ -98,6 +98,10 @@ class LinkerDriver {
|
||||
// Library search path. The first element is always "" (current directory).
|
||||
std::vector<StringRef> searchPaths;
|
||||
|
||||
// Convert resource files and potentially merge input resource object
|
||||
// trees into one resource tree.
|
||||
void convertResources();
|
||||
|
||||
void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args);
|
||||
|
||||
// We don't want to add the same file more than once.
|
||||
@ -120,7 +124,8 @@ class LinkerDriver {
|
||||
StringRef findDefaultEntry();
|
||||
WindowsSubsystem inferSubsystem();
|
||||
|
||||
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive);
|
||||
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
|
||||
bool lazy);
|
||||
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
|
||||
StringRef parentName, uint64_t offsetInArchive);
|
||||
|
||||
@ -184,7 +189,8 @@ void assignExportOrdinals();
|
||||
void checkFailIfMismatch(StringRef arg, InputFile *source);
|
||||
|
||||
// Convert Windows resource files (.res files) to a .obj file.
|
||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs);
|
||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
|
||||
ArrayRef<ObjFile *> objs);
|
||||
|
||||
void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);
|
||||
|
||||
|
@ -322,7 +322,7 @@ class TemporaryFile {
|
||||
|
||||
if (!contents.empty()) {
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(path, ec, sys::fs::F_None);
|
||||
raw_fd_ostream os(path, ec, sys::fs::OF_None);
|
||||
if (ec)
|
||||
fatal("failed to open " + path + ": " + ec.message());
|
||||
os << contents;
|
||||
@ -410,7 +410,7 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
|
||||
// Create the default manifest file as a temporary file.
|
||||
TemporaryFile Default("defaultxml", "manifest");
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(Default.path, ec, sys::fs::F_Text);
|
||||
raw_fd_ostream os(Default.path, ec, sys::fs::OF_Text);
|
||||
if (ec)
|
||||
fatal("failed to open " + Default.path + ": " + ec.message());
|
||||
os << defaultXml;
|
||||
@ -511,7 +511,7 @@ void createSideBySideManifest() {
|
||||
if (path == "")
|
||||
path = config->outputFile + ".manifest";
|
||||
std::error_code ec;
|
||||
raw_fd_ostream out(path, ec, sys::fs::F_Text);
|
||||
raw_fd_ostream out(path, ec, sys::fs::OF_Text);
|
||||
if (ec)
|
||||
fatal("failed to create manifest: " + ec.message());
|
||||
out << createManifestXml();
|
||||
@ -700,26 +700,42 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) {
|
||||
|
||||
// Convert Windows resource files (.res files) to a .obj file.
|
||||
// Does what cvtres.exe does, but in-process and cross-platform.
|
||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs) {
|
||||
object::WindowsResourceParser parser;
|
||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
|
||||
ArrayRef<ObjFile *> objs) {
|
||||
object::WindowsResourceParser parser(/* MinGW */ config->mingw);
|
||||
|
||||
std::vector<std::string> duplicates;
|
||||
for (MemoryBufferRef mb : mbs) {
|
||||
std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
|
||||
object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
|
||||
if (!rf)
|
||||
fatal("cannot compile non-resource file as resource");
|
||||
|
||||
std::vector<std::string> duplicates;
|
||||
if (auto ec = parser.parse(rf, duplicates))
|
||||
fatal(toString(std::move(ec)));
|
||||
|
||||
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 =
|
||||
llvm::object::writeWindowsResourceCOFF(config->machine, parser,
|
||||
config->timestamp);
|
||||
@ -757,15 +773,15 @@ static void handleColorDiagnostics(opt::InputArgList &args) {
|
||||
if (!arg)
|
||||
return;
|
||||
if (arg->getOption().getID() == OPT_color_diagnostics) {
|
||||
errorHandler().colorDiagnostics = true;
|
||||
enableColors(true);
|
||||
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
|
||||
errorHandler().colorDiagnostics = false;
|
||||
enableColors(false);
|
||||
} else {
|
||||
StringRef s = arg->getValue();
|
||||
if (s == "always")
|
||||
errorHandler().colorDiagnostics = true;
|
||||
enableColors(true);
|
||||
else if (s == "never")
|
||||
errorHandler().colorDiagnostics = false;
|
||||
enableColors(false);
|
||||
else if (s != "auto")
|
||||
error("unknown option: --color-diagnostics=" + s);
|
||||
}
|
||||
@ -792,13 +808,17 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
|
||||
|
||||
// We need to get the quoting style for response files before parsing all
|
||||
// options so we parse here before and ignore all the options but
|
||||
// --rsp-quoting.
|
||||
// --rsp-quoting and /lldignoreenv.
|
||||
// (This means --rsp-quoting can't be added through %LINK%.)
|
||||
opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
|
||||
|
||||
// Expand response files (arguments in the form of @<filename>)
|
||||
// and then parse the argument again.
|
||||
|
||||
// Expand response files (arguments in the form of @<filename>) and insert
|
||||
// flags from %LINK% and %_LINK_%, and then parse the argument again.
|
||||
SmallVector<const char *, 256> expandedArgv(argv.data(),
|
||||
argv.data() + argv.size());
|
||||
if (!args.hasArg(OPT_lldignoreenv))
|
||||
addLINK(expandedArgv);
|
||||
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
|
||||
args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
|
||||
missingCount);
|
||||
@ -868,7 +888,7 @@ ArgParser::parseDirectives(StringRef s) {
|
||||
// link.exe has an interesting feature. If LINK or _LINK_ environment
|
||||
// variables exist, their contents are handled as command line strings.
|
||||
// So you can pass extra arguments using them.
|
||||
opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
|
||||
void ArgParser::addLINK(SmallVector<const char *, 256> &argv) {
|
||||
// Concatenate LINK env and command line arguments, and then parse them.
|
||||
if (Optional<std::string> s = Process::GetEnv("LINK")) {
|
||||
std::vector<const char *> v = tokenize(*s);
|
||||
@ -878,7 +898,6 @@ opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
|
||||
std::vector<const char *> v = tokenize(*s);
|
||||
argv.insert(std::next(argv.begin()), v.begin(), v.end());
|
||||
}
|
||||
return parse(argv);
|
||||
}
|
||||
|
||||
std::vector<const char *> ArgParser::tokenize(StringRef s) {
|
||||
|
@ -13,7 +13,7 @@
|
||||
//
|
||||
// On Windows, ICF is enabled by default.
|
||||
//
|
||||
// See ELF/ICF.cpp for the details about the algortihm.
|
||||
// See ELF/ICF.cpp for the details about the algorithm.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@ -77,7 +77,7 @@ class ICF {
|
||||
// section is insignificant to the user program and the behaviour matches that
|
||||
// of the Visual C++ linker.
|
||||
bool ICF::isEligible(SectionChunk *c) {
|
||||
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
|
||||
// Non-comdat chunks, dead chunks, and writable chunks are not eligible.
|
||||
bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
|
||||
if (!c->isCOMDAT() || !c->live || writable)
|
||||
return false;
|
||||
@ -274,7 +274,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
|
||||
for (Symbol *b : sc->symbols())
|
||||
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
|
||||
hash += sym->getChunk()->eqClass[cnt % 2];
|
||||
// Set MSB to 1 to avoid collisions with non-hash classs.
|
||||
// Set MSB to 1 to avoid collisions with non-hash classes.
|
||||
sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
|
||||
});
|
||||
}
|
||||
@ -297,7 +297,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
|
||||
|
||||
log("ICF needed " + Twine(cnt) + " iterations");
|
||||
|
||||
// Merge sections in the same classs.
|
||||
// Merge sections in the same classes.
|
||||
forEachClass([&](size_t begin, size_t end) {
|
||||
if (end - begin == 1)
|
||||
return;
|
||||
|
@ -47,6 +47,24 @@ using llvm::Triple;
|
||||
using llvm::support::ulittle32_t;
|
||||
|
||||
namespace lld {
|
||||
|
||||
// Returns the last element of a path, which is supposed to be a filename.
|
||||
static StringRef getBasename(StringRef path) {
|
||||
return sys::path::filename(path, sys::path::Style::windows);
|
||||
}
|
||||
|
||||
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
|
||||
std::string toString(const coff::InputFile *file) {
|
||||
if (!file)
|
||||
return "<internal>";
|
||||
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
|
||||
return file->getName();
|
||||
|
||||
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
|
||||
")")
|
||||
.str();
|
||||
}
|
||||
|
||||
namespace coff {
|
||||
|
||||
std::vector<ObjFile *> ObjFile::instances;
|
||||
@ -73,6 +91,10 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
|
||||
}
|
||||
}
|
||||
|
||||
static bool ignoredSymbolName(StringRef name) {
|
||||
return name == "@feat.00" || name == "@comp.id";
|
||||
}
|
||||
|
||||
ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
|
||||
|
||||
void ArchiveFile::parse() {
|
||||
@ -81,7 +103,7 @@ void ArchiveFile::parse() {
|
||||
|
||||
// Read the symbol table to construct Lazy objects.
|
||||
for (const Archive::Symbol &sym : file->symbols())
|
||||
symtab->addLazy(this, sym);
|
||||
symtab->addLazyArchive(this, sym);
|
||||
}
|
||||
|
||||
// Returns a buffer pointing to a member file containing a given symbol.
|
||||
@ -116,6 +138,49 @@ std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
|
||||
return v;
|
||||
}
|
||||
|
||||
void LazyObjFile::fetch() {
|
||||
if (mb.getBuffer().empty())
|
||||
return;
|
||||
|
||||
InputFile *file;
|
||||
if (isBitcode(mb))
|
||||
file = make<BitcodeFile>(mb, "", 0, std::move(symbols));
|
||||
else
|
||||
file = make<ObjFile>(mb, std::move(symbols));
|
||||
mb = {};
|
||||
symtab->addFile(file);
|
||||
}
|
||||
|
||||
void LazyObjFile::parse() {
|
||||
if (isBitcode(this->mb)) {
|
||||
// Bitcode file.
|
||||
std::unique_ptr<lto::InputFile> obj =
|
||||
CHECK(lto::InputFile::create(this->mb), this);
|
||||
for (const lto::InputFile::Symbol &sym : obj->symbols()) {
|
||||
if (!sym.isUndefined())
|
||||
symtab->addLazyObject(this, sym.getName());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Native object file.
|
||||
std::unique_ptr<Binary> coffObjPtr = CHECK(createBinary(mb), this);
|
||||
COFFObjectFile *coffObj = cast<COFFObjectFile>(coffObjPtr.get());
|
||||
uint32_t numSymbols = coffObj->getNumberOfSymbols();
|
||||
for (uint32_t i = 0; i < numSymbols; ++i) {
|
||||
COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
|
||||
if (coffSym.isUndefined() || !coffSym.isExternal() ||
|
||||
coffSym.isWeakExternal())
|
||||
continue;
|
||||
StringRef name;
|
||||
coffObj->getSymbolName(coffSym, name);
|
||||
if (coffSym.isAbsolute() && ignoredSymbolName(name))
|
||||
continue;
|
||||
symtab->addLazyObject(this, name);
|
||||
i += coffSym.getNumberOfAuxSymbols();
|
||||
}
|
||||
}
|
||||
|
||||
void ObjFile::parse() {
|
||||
// Parse a memory buffer as a COFF file.
|
||||
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
|
||||
@ -206,10 +271,6 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
|
||||
if (def)
|
||||
c->checksum = def->CheckSum;
|
||||
|
||||
// link.exe uses the presence of .rsrc$01 for LNK4078, so match that.
|
||||
if (name == ".rsrc$01")
|
||||
isResourceObjFile = true;
|
||||
|
||||
// CodeView sections are stored to a different vector because they are not
|
||||
// linked in the regular manner.
|
||||
if (c->isCodeView())
|
||||
@ -226,12 +287,18 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
|
||||
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
|
||||
// for string literals) are subject to string tail merging.
|
||||
MergeChunk::addSection(c);
|
||||
else if (name == ".rsrc" || name.startswith(".rsrc$"))
|
||||
resourceChunks.push_back(c);
|
||||
else
|
||||
chunks.push_back(c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void ObjFile::includeResourceChunks() {
|
||||
chunks.insert(chunks.end(), resourceChunks.begin(), resourceChunks.end());
|
||||
}
|
||||
|
||||
void ObjFile::readAssociativeDefinition(
|
||||
COFFSymbolRef sym, const coff_aux_section_definition *def) {
|
||||
readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
|
||||
@ -315,7 +382,8 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
|
||||
StringRef name;
|
||||
coffObj->getSymbolName(sym, name);
|
||||
if (sc)
|
||||
return symtab->addRegular(this, name, sym.getGeneric(), sc);
|
||||
return symtab->addRegular(this, name, sym.getGeneric(), sc,
|
||||
sym.getValue());
|
||||
// For MinGW symbols named .weak.* that point to a discarded section,
|
||||
// don't create an Undefined symbol. If nothing ever refers to the symbol,
|
||||
// everything should be fine. If something actually refers to the symbol
|
||||
@ -469,7 +537,7 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
|
||||
// if the two comdat sections have e.g. different alignment.
|
||||
// Match that.
|
||||
if (leaderChunk->getContents() != newChunk.getContents())
|
||||
symtab->reportDuplicate(leader, this);
|
||||
symtab->reportDuplicate(leader, this, &newChunk, sym.getValue());
|
||||
break;
|
||||
}
|
||||
|
||||
@ -524,13 +592,11 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||
if (sym.isAbsolute()) {
|
||||
StringRef name = getName();
|
||||
|
||||
// Skip special symbols.
|
||||
if (name == "@comp.id")
|
||||
return nullptr;
|
||||
if (name == "@feat.00") {
|
||||
if (name == "@feat.00")
|
||||
feat00Flags = sym.getValue();
|
||||
// Skip special symbols.
|
||||
if (ignoredSymbolName(name))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (sym.isExternal())
|
||||
return symtab->addAbsolute(name, sym);
|
||||
@ -552,7 +618,7 @@ Optional<Symbol *> ObjFile::createDefined(
|
||||
// Comdat handling.
|
||||
// A comdat symbol consists of two symbol table entries.
|
||||
// The first symbol entry has the name of the section (e.g. .text), fixed
|
||||
// values for the other fields, and one auxilliary record.
|
||||
// values for the other fields, and one auxiliary record.
|
||||
// The second symbol entry has the name of the comdat symbol, called the
|
||||
// "comdat leader".
|
||||
// When this function is called for the first symbol entry of a comdat,
|
||||
@ -622,7 +688,7 @@ ArrayRef<uint8_t> ObjFile::getDebugSection(StringRef secName) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// OBJ files systematically store critical informations in a .debug$S stream,
|
||||
// OBJ files systematically store critical information in a .debug$S stream,
|
||||
// even if the TU was compiled with no debug info. At least two records are
|
||||
// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the
|
||||
// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is
|
||||
@ -723,6 +789,37 @@ void ObjFile::initializeDependencies() {
|
||||
debugTypesObj = makeTpiSource(this);
|
||||
}
|
||||
|
||||
// Used only for DWARF debug info, which is not common (except in MinGW
|
||||
// environments). This returns an optional pair of file name and line
|
||||
// number for where the variable was defined.
|
||||
Optional<std::pair<StringRef, uint32_t>>
|
||||
ObjFile::getVariableLocation(StringRef var) {
|
||||
if (!dwarf) {
|
||||
dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
|
||||
if (!dwarf)
|
||||
return None;
|
||||
}
|
||||
if (config->machine == I386)
|
||||
var.consume_front("_");
|
||||
Optional<std::pair<std::string, unsigned>> ret = dwarf->getVariableLoc(var);
|
||||
if (!ret)
|
||||
return None;
|
||||
return std::make_pair(saver.save(ret->first), ret->second);
|
||||
}
|
||||
|
||||
// Used only for DWARF debug info, which is not common (except in MinGW
|
||||
// environments).
|
||||
Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
|
||||
uint32_t sectionIndex) {
|
||||
if (!dwarf) {
|
||||
dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
|
||||
if (!dwarf)
|
||||
return None;
|
||||
}
|
||||
|
||||
return dwarf->getDILineInfo(offset, sectionIndex);
|
||||
}
|
||||
|
||||
StringRef ltrim1(StringRef s, const char *chars) {
|
||||
if (!s.empty() && strchr(chars, s[0]))
|
||||
return s.substr(1);
|
||||
@ -780,8 +877,9 @@ void ImportFile::parse() {
|
||||
}
|
||||
|
||||
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||
uint64_t offsetInArchive)
|
||||
: InputFile(BitcodeKind, mb) {
|
||||
uint64_t offsetInArchive,
|
||||
std::vector<Symbol *> &&symbols)
|
||||
: InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
|
||||
std::string path = mb.getBufferIdentifier().str();
|
||||
if (config->thinLTOIndexOnly)
|
||||
path = replaceThinLTOSuffix(mb.getBufferIdentifier());
|
||||
@ -860,22 +958,6 @@ std::string replaceThinLTOSuffix(StringRef path) {
|
||||
return (path + repl).str();
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
// Returns the last element of a path, which is supposed to be a filename.
|
||||
static StringRef getBasename(StringRef path) {
|
||||
return sys::path::filename(path, sys::path::Style::windows);
|
||||
}
|
||||
|
||||
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
|
||||
std::string lld::toString(const coff::InputFile *file) {
|
||||
if (!file)
|
||||
return "<internal>";
|
||||
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
|
||||
return file->getName();
|
||||
|
||||
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
|
||||
")")
|
||||
.str();
|
||||
}
|
||||
|
@ -10,10 +10,12 @@
|
||||
#define LLD_COFF_INPUT_FILES_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "lld/Common/DWARF.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/BinaryFormat/Magic.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
@ -24,6 +26,7 @@
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
struct DILineInfo;
|
||||
namespace pdb {
|
||||
class DbiModuleDescriptorBuilder;
|
||||
}
|
||||
@ -47,7 +50,6 @@ class Defined;
|
||||
class DefinedImportData;
|
||||
class DefinedImportThunk;
|
||||
class DefinedRegular;
|
||||
class Lazy;
|
||||
class SectionChunk;
|
||||
class Symbol;
|
||||
class Undefined;
|
||||
@ -56,7 +58,13 @@ class TpiSource;
|
||||
// The root class of input files.
|
||||
class InputFile {
|
||||
public:
|
||||
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind };
|
||||
enum Kind {
|
||||
ArchiveKind,
|
||||
ObjectKind,
|
||||
LazyObjectKind,
|
||||
ImportKind,
|
||||
BitcodeKind
|
||||
};
|
||||
Kind kind() const { return fileKind; }
|
||||
virtual ~InputFile() {}
|
||||
|
||||
@ -103,10 +111,28 @@ class ArchiveFile : public InputFile {
|
||||
llvm::DenseSet<uint64_t> seen;
|
||||
};
|
||||
|
||||
// .obj or .o file between -start-lib and -end-lib.
|
||||
class LazyObjFile : public InputFile {
|
||||
public:
|
||||
explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {}
|
||||
static bool classof(const InputFile *f) {
|
||||
return f->kind() == LazyObjectKind;
|
||||
}
|
||||
// Makes this object file part of the link.
|
||||
void fetch();
|
||||
// Adds the symbols in this file to the symbol table as LazyObject symbols.
|
||||
void parse() override;
|
||||
|
||||
private:
|
||||
std::vector<Symbol *> symbols;
|
||||
};
|
||||
|
||||
// .obj or .o file. This may be a member of an archive file.
|
||||
class ObjFile : public InputFile {
|
||||
public:
|
||||
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
|
||||
explicit ObjFile(MemoryBufferRef m, std::vector<Symbol *> &&symbols)
|
||||
: InputFile(ObjectKind, m), symbols(std::move(symbols)) {}
|
||||
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
|
||||
void parse() override;
|
||||
MachineTypes getMachineType() override;
|
||||
@ -135,6 +161,10 @@ class ObjFile : public InputFile {
|
||||
return symbols.size() - 1;
|
||||
}
|
||||
|
||||
void includeResourceChunks();
|
||||
|
||||
bool isResourceObjFile() const { return !resourceChunks.empty(); }
|
||||
|
||||
static std::vector<ObjFile *> instances;
|
||||
|
||||
// Flags in the absolute @feat.00 symbol if it is present. These usually
|
||||
@ -162,9 +192,6 @@ class ObjFile : public InputFile {
|
||||
// precompiled object. Any difference indicates out-of-date objects.
|
||||
llvm::Optional<uint32_t> pchSignature;
|
||||
|
||||
// Whether this is an object file created from .res files.
|
||||
bool isResourceObjFile = false;
|
||||
|
||||
// Whether this file was compiled with /hotpatch.
|
||||
bool hotPatchable = false;
|
||||
|
||||
@ -177,6 +204,12 @@ class ObjFile : public InputFile {
|
||||
// The .debug$T stream if there's one.
|
||||
llvm::Optional<llvm::codeview::CVTypeArray> debugTypes;
|
||||
|
||||
llvm::Optional<std::pair<StringRef, uint32_t>>
|
||||
getVariableLocation(StringRef var);
|
||||
|
||||
llvm::Optional<llvm::DILineInfo> getDILineInfo(uint32_t offset,
|
||||
uint32_t sectionIndex);
|
||||
|
||||
private:
|
||||
const coff_section* getSection(uint32_t i);
|
||||
const coff_section *getSection(COFFSymbolRef sym) {
|
||||
@ -234,6 +267,8 @@ class ObjFile : public InputFile {
|
||||
// chunks and non-section chunks for common symbols.
|
||||
std::vector<Chunk *> chunks;
|
||||
|
||||
std::vector<SectionChunk *> resourceChunks;
|
||||
|
||||
// CodeView debug info sections.
|
||||
std::vector<SectionChunk *> debugChunks;
|
||||
|
||||
@ -258,6 +293,8 @@ class ObjFile : public InputFile {
|
||||
// index. Nonexistent indices (which are occupied by auxiliary
|
||||
// symbols in the real symbol table) are filled with null pointers.
|
||||
std::vector<Symbol *> symbols;
|
||||
|
||||
DWARFCache *dwarf = nullptr;
|
||||
};
|
||||
|
||||
// This type represents import library members that contain DLL names
|
||||
@ -299,7 +336,11 @@ class ImportFile : public InputFile {
|
||||
class BitcodeFile : public InputFile {
|
||||
public:
|
||||
BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||
uint64_t offsetInArchive);
|
||||
uint64_t offsetInArchive)
|
||||
: BitcodeFile(mb, archiveName, offsetInArchive, {}) {}
|
||||
explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName,
|
||||
uint64_t offsetInArchive,
|
||||
std::vector<Symbol *> &&symbols);
|
||||
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
||||
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
||||
MachineTypes getMachineType() override;
|
||||
@ -312,6 +353,10 @@ class BitcodeFile : public InputFile {
|
||||
std::vector<Symbol *> symbols;
|
||||
};
|
||||
|
||||
inline bool isBitcode(MemoryBufferRef mb) {
|
||||
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
|
||||
}
|
||||
|
||||
std::string replaceThinLTOSuffix(StringRef path);
|
||||
} // namespace coff
|
||||
|
||||
|
@ -39,14 +39,14 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
// Creates an empty file to and returns a raw_fd_ostream to write to it.
|
||||
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
|
||||
std::error_code ec;
|
||||
auto ret =
|
||||
llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
|
||||
std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
|
||||
if (ec) {
|
||||
error("cannot open " + file + ": " + ec.message());
|
||||
return nullptr;
|
||||
@ -105,7 +105,7 @@ BitcodeCompiler::BitcodeCompiler() {
|
||||
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
|
||||
}
|
||||
|
||||
ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
|
||||
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
|
||||
config->ltoPartitions);
|
||||
}
|
||||
|
||||
@ -160,8 +160,8 @@ std::vector<StringRef> BitcodeCompiler::compile() {
|
||||
|
||||
checkError(ltoObj->run(
|
||||
[&](size_t task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(buf[task]));
|
||||
return std::make_unique<lto::NativeObjectStream>(
|
||||
std::make_unique<raw_svector_ostream>(buf[task]));
|
||||
},
|
||||
cache));
|
||||
|
||||
@ -177,6 +177,8 @@ std::vector<StringRef> BitcodeCompiler::compile() {
|
||||
// files. After that, we exit from linker and ThinLTO backend runs in a
|
||||
// distributed environment.
|
||||
if (config->thinLTOIndexOnly) {
|
||||
if (!config->ltoObjPath.empty())
|
||||
saveBuffer(buf[0], config->ltoObjPath);
|
||||
if (indexFile)
|
||||
indexFile->close();
|
||||
return {};
|
||||
@ -204,3 +206,6 @@ std::vector<StringRef> BitcodeCompiler::compile() {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
@ -29,14 +29,14 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
using SymbolMapTy =
|
||||
DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
|
||||
|
||||
static const std::string indent8 = " "; // 8 spaces
|
||||
static const std::string indent16 = " "; // 16 spaces
|
||||
static constexpr char indent8[] = " "; // 8 spaces
|
||||
static constexpr char indent16[] = " "; // 16 spaces
|
||||
|
||||
// Print out the first three columns of a line.
|
||||
static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
|
||||
@ -87,12 +87,12 @@ getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
|
||||
void writeMapFile(ArrayRef<OutputSection *> outputSections) {
|
||||
if (config->mapFile.empty())
|
||||
return;
|
||||
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
|
||||
raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
|
||||
if (ec)
|
||||
fatal("cannot open " + config->mapFile + ": " + ec.message());
|
||||
|
||||
@ -122,3 +122,6 @@ void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
@ -13,11 +13,12 @@
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
using namespace llvm::COFF;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
AutoExporter::AutoExporter() {
|
||||
excludeLibs = {
|
||||
"libgcc",
|
||||
@ -55,7 +56,7 @@ AutoExporter::AutoExporter() {
|
||||
// C++ symbols
|
||||
"__rtti_",
|
||||
"__builtin_",
|
||||
// Artifical symbols such as .refptr
|
||||
// Artificial symbols such as .refptr
|
||||
".",
|
||||
};
|
||||
|
||||
@ -146,9 +147,9 @@ bool AutoExporter::shouldExport(Defined *sym) const {
|
||||
return !excludeObjects.count(fileName);
|
||||
}
|
||||
|
||||
void coff::writeDefFile(StringRef name) {
|
||||
void writeDefFile(StringRef name) {
|
||||
std::error_code ec;
|
||||
raw_fd_ostream os(name, ec, sys::fs::F_None);
|
||||
raw_fd_ostream os(name, ec, sys::fs::OF_None);
|
||||
if (ec)
|
||||
fatal("cannot open " + name + ": " + ec.message());
|
||||
|
||||
@ -164,3 +165,6 @@ void coff::writeDefFile(StringRef name) {
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
@ -21,9 +21,9 @@ def aligncomm : P<"aligncomm", "Set common symbol alignment">;
|
||||
def alternatename : P<"alternatename", "Define weak alias">;
|
||||
def base : P<"base", "Base address of the program">;
|
||||
def color_diagnostics: Flag<["--"], "color-diagnostics">,
|
||||
HelpText<"Use colors in diagnostics">;
|
||||
HelpText<"Use colors in diagnostics">;
|
||||
def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
|
||||
HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">;
|
||||
HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">;
|
||||
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
|
||||
def delayload : P<"delayload", "Delay loaded DLL name">;
|
||||
def entry : P<"entry", "Name of entry point symbol">;
|
||||
@ -34,7 +34,8 @@ def export : P<"export", "Export a function">;
|
||||
def failifmismatch : P<"failifmismatch", "">;
|
||||
def filealign : P<"filealign", "Section alignment in the output file">;
|
||||
def functionpadmin : F<"functionpadmin">;
|
||||
def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">;
|
||||
def functionpadmin_opt : P<"functionpadmin",
|
||||
"Prepares an image for hotpatching">;
|
||||
def guard : P<"guard", "Control flow guard">;
|
||||
def heap : P<"heap", "Size of the heap">;
|
||||
def ignore : P<"ignore", "Specify warning codes to ignore">;
|
||||
@ -42,9 +43,14 @@ def implib : P<"implib", "Import library name">;
|
||||
def lib : F<"lib">,
|
||||
HelpText<"Act like lib.exe; must be first argument if present">;
|
||||
def libpath : P<"libpath", "Additional library search path">;
|
||||
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
|
||||
def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">;
|
||||
def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">;
|
||||
def linkrepro : P<"linkrepro",
|
||||
"Dump linker invocation and input files for debugging">;
|
||||
def lldignoreenv : F<"lldignoreenv">,
|
||||
HelpText<"Ignore environment variables like %LIB%">;
|
||||
def lldltocache : P<"lldltocache",
|
||||
"Path to ThinLTO cached object file directory">;
|
||||
def lldltocachepolicy : P<"lldltocachepolicy",
|
||||
"Pruning policy for the ThinLTO cache">;
|
||||
def lldsavetemps : F<"lldsavetemps">,
|
||||
HelpText<"Save temporary files instead of deleting them">;
|
||||
def machine : P<"machine", "Specify target platform">;
|
||||
@ -56,7 +62,7 @@ def order : P<"order", "Put functions in order">;
|
||||
def out : P<"out", "Path to file to write output">;
|
||||
def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
|
||||
def no_color_diagnostics: F<"no-color-diagnostics">,
|
||||
HelpText<"Do not use colors in diagnostics">;
|
||||
HelpText<"Do not use colors in diagnostics">;
|
||||
def pdb : P<"pdb", "PDB file path">;
|
||||
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
|
||||
def section : P<"section", "Specify section attributes">;
|
||||
@ -65,7 +71,8 @@ def stub : P<"stub", "Specify DOS stub file">;
|
||||
def subsystem : P<"subsystem", "Specify subsystem">;
|
||||
def timestamp : P<"timestamp", "Specify the PE header timestamp">;
|
||||
def version : P<"version", "Specify a version number in the PE header">;
|
||||
def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
|
||||
def wholearchive_file : P<"wholearchive",
|
||||
"Include all object files from this library">;
|
||||
|
||||
def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
|
||||
Alias<nodefaultlib>;
|
||||
@ -105,6 +112,8 @@ def noentry : F<"noentry">,
|
||||
def profile : F<"profile">;
|
||||
def repro : F<"Brepro">,
|
||||
HelpText<"Use a hash of the executable as the PE header timestamp">;
|
||||
def reproduce : P<"reproduce",
|
||||
"Dump linker invocation and input files for debugging">;
|
||||
def swaprun : P<"swaprun",
|
||||
"Comma-separated list of 'cd' or 'net'">;
|
||||
def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
|
||||
@ -112,10 +121,11 @@ def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
|
||||
def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
|
||||
HelpText<"Make loader run output binary from swap instead of from network">;
|
||||
def verbose : F<"verbose">;
|
||||
def wholearchive_flag : F<"wholearchive">;
|
||||
def wholearchive_flag : F<"wholearchive">,
|
||||
HelpText<"Include all object files from all libraries">;
|
||||
|
||||
def force : F<"force">,
|
||||
HelpText<"Allow undefined and multiply defined symbols when creating executables">;
|
||||
HelpText<"Allow undefined and multiply defined symbols">;
|
||||
def force_unresolved : F<"force:unresolved">,
|
||||
HelpText<"Allow undefined symbols when creating executables">;
|
||||
def force_multiple : F<"force:multiple">,
|
||||
@ -162,6 +172,8 @@ def help : F<"help">;
|
||||
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
|
||||
|
||||
// LLD extensions
|
||||
def end_lib : F<"end-lib">,
|
||||
HelpText<"Ends group of objects treated as if they were in a library">;
|
||||
def exclude_all_symbols : F<"exclude-all-symbols">;
|
||||
def export_all_symbols : F<"export-all-symbols">;
|
||||
defm demangle : B<"demangle",
|
||||
@ -173,9 +185,11 @@ def kill_at : F<"kill-at">;
|
||||
def lldmingw : F<"lldmingw">;
|
||||
def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
|
||||
def pdb_source_path : P<"pdbsourcepath",
|
||||
"Base path used to make relative source file path absolute in PDB">;
|
||||
"Base path used to make relative source file path absolute in PDB">;
|
||||
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
|
||||
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
|
||||
def start_lib : F<"start-lib">,
|
||||
HelpText<"Starts group of objects treated as if they were in a library">;
|
||||
def thinlto_emit_imports_files :
|
||||
F<"thinlto-emit-imports-files">,
|
||||
HelpText<"Emit .imports files with -thinlto-index-only">;
|
||||
@ -191,6 +205,9 @@ def thinlto_object_suffix_replace : P<
|
||||
def thinlto_prefix_replace: P<
|
||||
"thinlto-prefix-replace",
|
||||
"'old;new' replace old prefix with new prefix in ThinLTO outputs">;
|
||||
def lto_obj_path : P<
|
||||
"lto-obj-path",
|
||||
"output native object for merged LTO unit to this path">;
|
||||
def dash_dash_version : Flag<["--"], "version">,
|
||||
HelpText<"Print version information">;
|
||||
defm threads: B<"threads",
|
||||
|
@ -51,21 +51,22 @@
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Object/CVDebugRecord.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/CRC.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/JamCRC.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
using namespace llvm;
|
||||
using namespace llvm::codeview;
|
||||
|
||||
using llvm::object::coff_section;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
static ExitOnError exitOnErr;
|
||||
|
||||
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
|
||||
@ -513,16 +514,15 @@ static bool equals_path(StringRef path1, StringRef path2) {
|
||||
return path1.equals(path2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Find by name an OBJ provided on the command line
|
||||
static ObjFile *findObjByName(StringRef fileNameOnly) {
|
||||
SmallString<128> currentPath;
|
||||
|
||||
static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly,
|
||||
uint32_t precompSignature) {
|
||||
for (ObjFile *f : ObjFile::instances) {
|
||||
StringRef currentFileName = sys::path::filename(f->getName());
|
||||
|
||||
// Compare based solely on the file name (link.exe behavior)
|
||||
if (equals_path(currentFileName, fileNameOnly))
|
||||
if (f->pchSignature.hasValue() &&
|
||||
f->pchSignature.getValue() == precompSignature &&
|
||||
equals_path(fileNameOnly, currentFileName))
|
||||
return f;
|
||||
}
|
||||
return nullptr;
|
||||
@ -559,22 +559,15 @@ Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) {
|
||||
|
||||
// link.exe requires that a precompiled headers object must always be provided
|
||||
// on the command-line, even if that's not necessary.
|
||||
auto precompFile = findObjByName(precompFileName);
|
||||
auto precompFile =
|
||||
findObjWithPrecompSignature(precompFileName, precomp.Signature);
|
||||
if (!precompFile)
|
||||
return createFileError(
|
||||
precompFileName.str(),
|
||||
make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref));
|
||||
precomp.getPrecompFilePath().str(),
|
||||
make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
|
||||
|
||||
addObjFile(precompFile, &indexMap);
|
||||
|
||||
if (!precompFile->pchSignature)
|
||||
fatal(precompFile->getName() + " is not a precompiled headers object");
|
||||
|
||||
if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0))
|
||||
return createFileError(
|
||||
precomp.getPrecompFilePath().str(),
|
||||
make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
|
||||
|
||||
return indexMap;
|
||||
}
|
||||
|
||||
@ -965,9 +958,7 @@ static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
|
||||
sc.Imod = secChunk->file->moduleDBI->getModuleIndex();
|
||||
ArrayRef<uint8_t> contents = secChunk->getContents();
|
||||
JamCRC crc(0);
|
||||
ArrayRef<char> charContents = makeArrayRef(
|
||||
reinterpret_cast<const char *>(contents.data()), contents.size());
|
||||
crc.update(charContents);
|
||||
crc.update(contents);
|
||||
sc.DataCrc = crc.getCRC();
|
||||
} else {
|
||||
sc.Characteristics = os ? os->header.Characteristics : 0;
|
||||
@ -1150,7 +1141,7 @@ void DebugSHandler::finish() {
|
||||
// string table. Generally the string table subsection appears after the
|
||||
// checksum table, so we have to do this after looping over all the
|
||||
// subsections.
|
||||
auto newChecksums = make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
|
||||
auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
|
||||
for (FileChecksumEntry &fc : checksums) {
|
||||
SmallString<128> filename =
|
||||
exitOnErr(cVStrTab.getString(fc.FileNameOffset));
|
||||
@ -1599,7 +1590,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
|
||||
}
|
||||
|
||||
// Creates a PDB file.
|
||||
void coff::createPDB(SymbolTable *symtab,
|
||||
void createPDB(SymbolTable *symtab,
|
||||
ArrayRef<OutputSection *> outputSections,
|
||||
ArrayRef<uint8_t> sectionTable,
|
||||
llvm::codeview::DebugInfo *buildId) {
|
||||
@ -1693,6 +1684,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
|
||||
}
|
||||
|
||||
void PDBLinker::commit(codeview::GUID *guid) {
|
||||
ExitOnError exitOnErr((config->pdbPath + ": ").str());
|
||||
// Write to a file.
|
||||
exitOnErr(builder.commit(config->pdbPath, guid));
|
||||
}
|
||||
@ -1797,10 +1789,10 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
|
||||
}
|
||||
|
||||
// Use CodeView line tables to resolve a file and line number for the given
|
||||
// offset into the given chunk and return them, or {"", 0} if a line table was
|
||||
// offset into the given chunk and return them, or None if a line table was
|
||||
// not found.
|
||||
std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
|
||||
uint32_t addr) {
|
||||
Optional<std::pair<StringRef, uint32_t>>
|
||||
getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
|
||||
ExitOnError exitOnErr;
|
||||
|
||||
DebugStringTableSubsectionRef cVStrTab;
|
||||
@ -1809,7 +1801,7 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
|
||||
uint32_t offsetInLinetable;
|
||||
|
||||
if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
|
||||
return {"", 0};
|
||||
return None;
|
||||
|
||||
Optional<uint32_t> nameIndex;
|
||||
Optional<uint32_t> lineNumber;
|
||||
@ -1823,14 +1815,17 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
|
||||
}
|
||||
StringRef filename =
|
||||
exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
|
||||
return {filename, *lineNumber};
|
||||
return std::make_pair(filename, *lineNumber);
|
||||
}
|
||||
nameIndex = entry.NameIndex;
|
||||
lineNumber = li.getStartLine();
|
||||
}
|
||||
}
|
||||
if (!nameIndex)
|
||||
return {"", 0};
|
||||
return None;
|
||||
StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
|
||||
return {filename, *lineNumber};
|
||||
return std::make_pair(filename, *lineNumber);
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define LLD_COFF_PDB_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
namespace llvm {
|
||||
@ -29,9 +30,9 @@ void createPDB(SymbolTable *symtab,
|
||||
llvm::ArrayRef<uint8_t> sectionTable,
|
||||
llvm::codeview::DebugInfo *buildId);
|
||||
|
||||
std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c,
|
||||
uint32_t addr);
|
||||
}
|
||||
}
|
||||
llvm::Optional<std::pair<llvm::StringRef, uint32_t>>
|
||||
getFileLineCodeView(const SectionChunk *c, uint32_t addr);
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Timer.h"
|
||||
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/Object/WindowsMachineFlag.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
@ -61,6 +62,24 @@ static void errorOrWarn(const Twine &s) {
|
||||
error(s);
|
||||
}
|
||||
|
||||
// Causes the file associated with a lazy symbol to be linked in.
|
||||
static void forceLazy(Symbol *s) {
|
||||
s->pendingArchiveLoad = true;
|
||||
switch (s->kind()) {
|
||||
case Symbol::Kind::LazyArchiveKind: {
|
||||
auto *l = cast<LazyArchive>(s);
|
||||
l->file->addMember(l->sym);
|
||||
break;
|
||||
}
|
||||
case Symbol::Kind::LazyObjectKind:
|
||||
cast<LazyObject>(s)->file->fetch();
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable(
|
||||
"symbol passed to forceLazy is not a LazyArchive or LazyObject");
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
|
||||
// This is generally the global variable or function whose definition contains
|
||||
// Addr.
|
||||
@ -69,7 +88,8 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
|
||||
|
||||
for (Symbol *s : sc->file->getSymbols()) {
|
||||
auto *d = dyn_cast_or_null<DefinedRegular>(s);
|
||||
if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr ||
|
||||
if (!d || !d->data || d->file != sc->file || d->getChunk() != sc ||
|
||||
d->getValue() > addr ||
|
||||
(candidate && d->getValue() < candidate->getValue()))
|
||||
continue;
|
||||
|
||||
@ -79,6 +99,38 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
static std::vector<std::string> getSymbolLocations(BitcodeFile *file) {
|
||||
std::string res("\n>>> referenced by ");
|
||||
StringRef source = file->obj->getSourceFileName();
|
||||
if (!source.empty())
|
||||
res += source.str() + "\n>>> ";
|
||||
res += toString(file);
|
||||
return {res};
|
||||
}
|
||||
|
||||
static Optional<std::pair<StringRef, uint32_t>>
|
||||
getFileLineDwarf(const SectionChunk *c, uint32_t addr) {
|
||||
Optional<DILineInfo> optionalLineInfo =
|
||||
c->file->getDILineInfo(addr, c->getSectionNumber() - 1);
|
||||
if (!optionalLineInfo)
|
||||
return None;
|
||||
const DILineInfo &lineInfo = *optionalLineInfo;
|
||||
if (lineInfo.FileName == DILineInfo::BadString)
|
||||
return None;
|
||||
return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line);
|
||||
}
|
||||
|
||||
static Optional<std::pair<StringRef, uint32_t>>
|
||||
getFileLine(const SectionChunk *c, uint32_t addr) {
|
||||
// MinGW can optionally use codeview, even if the default is dwarf.
|
||||
Optional<std::pair<StringRef, uint32_t>> fileLine =
|
||||
getFileLineCodeView(c, addr);
|
||||
// If codeview didn't yield any result, check dwarf in MinGW mode.
|
||||
if (!fileLine && config->mingw)
|
||||
fileLine = getFileLineDwarf(c, addr);
|
||||
return fileLine;
|
||||
}
|
||||
|
||||
// Given a file and the index of a symbol in that file, returns a description
|
||||
// of all references to that symbol from that file. If no debug information is
|
||||
// available, returns just the name of the file, else one string per actual
|
||||
@ -97,11 +149,13 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
|
||||
for (const coff_relocation &r : sc->getRelocs()) {
|
||||
if (r.SymbolTableIndex != symIndex)
|
||||
continue;
|
||||
std::pair<StringRef, uint32_t> fileLine =
|
||||
Optional<std::pair<StringRef, uint32_t>> fileLine =
|
||||
getFileLine(sc, r.VirtualAddress);
|
||||
Symbol *sym = getSymbol(sc, r.VirtualAddress);
|
||||
if (!fileLine.first.empty() || sym)
|
||||
locations.push_back({sym, fileLine});
|
||||
if (fileLine)
|
||||
locations.push_back({sym, *fileLine});
|
||||
else if (sym)
|
||||
locations.push_back({sym, {"", 0}});
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,13 +177,23 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
|
||||
return symbolLocations;
|
||||
}
|
||||
|
||||
std::vector<std::string> getSymbolLocations(InputFile *file,
|
||||
uint32_t symIndex) {
|
||||
if (auto *o = dyn_cast<ObjFile>(file))
|
||||
return getSymbolLocations(o, symIndex);
|
||||
if (auto *b = dyn_cast<BitcodeFile>(file))
|
||||
return getSymbolLocations(b);
|
||||
llvm_unreachable("unsupported file type passed to getSymbolLocations");
|
||||
return {};
|
||||
}
|
||||
|
||||
// For an undefined symbol, stores all files referencing it and the index of
|
||||
// the undefined symbol in each file.
|
||||
struct UndefinedDiag {
|
||||
Symbol *sym;
|
||||
struct File {
|
||||
ObjFile *oFile;
|
||||
uint64_t symIndex;
|
||||
InputFile *file;
|
||||
uint32_t symIndex;
|
||||
};
|
||||
std::vector<File> files;
|
||||
};
|
||||
@ -143,7 +207,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
|
||||
size_t i = 0, numRefs = 0;
|
||||
for (const UndefinedDiag::File &ref : undefDiag.files) {
|
||||
std::vector<std::string> symbolLocations =
|
||||
getSymbolLocations(ref.oFile, ref.symIndex);
|
||||
getSymbolLocations(ref.file, ref.symIndex);
|
||||
numRefs += symbolLocations.size();
|
||||
for (const std::string &s : symbolLocations) {
|
||||
if (i >= maxUndefReferences)
|
||||
@ -165,28 +229,33 @@ void SymbolTable::loadMinGWAutomaticImports() {
|
||||
continue;
|
||||
if (!sym->isUsedInRegularObj)
|
||||
continue;
|
||||
if (undef->getWeakAlias())
|
||||
continue;
|
||||
|
||||
StringRef name = undef->getName();
|
||||
|
||||
if (name.startswith("__imp_"))
|
||||
continue;
|
||||
// If we have an undefined symbol, but we have a Lazy representing a
|
||||
// symbol we could load from file, make sure to load that.
|
||||
Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str()));
|
||||
if (!l || l->pendingArchiveLoad)
|
||||
// If we have an undefined symbol, but we have a lazy symbol we could
|
||||
// load, load it.
|
||||
Symbol *l = find(("__imp_" + name).str());
|
||||
if (!l || l->pendingArchiveLoad || !l->isLazy())
|
||||
continue;
|
||||
|
||||
log("Loading lazy " + l->getName() + " from " + l->file->getName() +
|
||||
log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() +
|
||||
" for automatic import");
|
||||
l->pendingArchiveLoad = true;
|
||||
l->file->addMember(l->sym);
|
||||
forceLazy(l);
|
||||
}
|
||||
}
|
||||
|
||||
bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
||||
Defined *SymbolTable::impSymbol(StringRef name) {
|
||||
if (name.startswith("__imp_"))
|
||||
return false;
|
||||
Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
|
||||
return nullptr;
|
||||
return dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
|
||||
}
|
||||
|
||||
bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
||||
Defined *imp = impSymbol(name);
|
||||
if (!imp)
|
||||
return false;
|
||||
|
||||
@ -232,7 +301,97 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SymbolTable::reportRemainingUndefines() {
|
||||
/// Helper function for reportUnresolvable and resolveRemainingUndefines.
|
||||
/// This function emits an "undefined symbol" diagnostic for each symbol in
|
||||
/// undefs. If localImports is not nullptr, it also emits a "locally
|
||||
/// defined symbol imported" diagnostic for symbols in localImports.
|
||||
/// objFiles and bitcodeFiles (if not nullptr) are used to report where
|
||||
/// undefined symbols are referenced.
|
||||
static void
|
||||
reportProblemSymbols(const SmallPtrSetImpl<Symbol *> &undefs,
|
||||
const DenseMap<Symbol *, Symbol *> *localImports,
|
||||
const std::vector<ObjFile *> objFiles,
|
||||
const std::vector<BitcodeFile *> *bitcodeFiles) {
|
||||
|
||||
// Return early if there is nothing to report (which should be
|
||||
// the common case).
|
||||
if (undefs.empty() && (!localImports || localImports->empty()))
|
||||
return;
|
||||
|
||||
for (Symbol *b : config->gcroot) {
|
||||
if (undefs.count(b))
|
||||
errorOrWarn("<root>: undefined symbol: " + toString(*b));
|
||||
if (localImports)
|
||||
if (Symbol *imp = localImports->lookup(b))
|
||||
warn("<root>: locally defined symbol imported: " + toString(*imp) +
|
||||
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
||||
}
|
||||
|
||||
std::vector<UndefinedDiag> undefDiags;
|
||||
DenseMap<Symbol *, int> firstDiag;
|
||||
|
||||
auto processFile = [&](InputFile *file, ArrayRef<Symbol *> symbols) {
|
||||
uint32_t symIndex = (uint32_t)-1;
|
||||
for (Symbol *sym : symbols) {
|
||||
++symIndex;
|
||||
if (!sym)
|
||||
continue;
|
||||
if (undefs.count(sym)) {
|
||||
auto it = firstDiag.find(sym);
|
||||
if (it == firstDiag.end()) {
|
||||
firstDiag[sym] = undefDiags.size();
|
||||
undefDiags.push_back({sym, {{file, symIndex}}});
|
||||
} else {
|
||||
undefDiags[it->second].files.push_back({file, symIndex});
|
||||
}
|
||||
}
|
||||
if (localImports)
|
||||
if (Symbol *imp = localImports->lookup(sym))
|
||||
warn(toString(file) +
|
||||
": locally defined symbol imported: " + toString(*imp) +
|
||||
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
||||
}
|
||||
};
|
||||
|
||||
for (ObjFile *file : objFiles)
|
||||
processFile(file, file->getSymbols());
|
||||
|
||||
if (bitcodeFiles)
|
||||
for (BitcodeFile *file : *bitcodeFiles)
|
||||
processFile(file, file->getSymbols());
|
||||
|
||||
for (const UndefinedDiag &undefDiag : undefDiags)
|
||||
reportUndefinedSymbol(undefDiag);
|
||||
}
|
||||
|
||||
void SymbolTable::reportUnresolvable() {
|
||||
SmallPtrSet<Symbol *, 8> undefs;
|
||||
for (auto &i : symMap) {
|
||||
Symbol *sym = i.second;
|
||||
auto *undef = dyn_cast<Undefined>(sym);
|
||||
if (!undef)
|
||||
continue;
|
||||
if (undef->getWeakAlias())
|
||||
continue;
|
||||
StringRef name = undef->getName();
|
||||
if (name.startswith("__imp_")) {
|
||||
Symbol *imp = find(name.substr(strlen("__imp_")));
|
||||
if (imp && isa<Defined>(imp))
|
||||
continue;
|
||||
}
|
||||
if (name.contains("_PchSym_"))
|
||||
continue;
|
||||
if (config->mingw && impSymbol(name))
|
||||
continue;
|
||||
undefs.insert(sym);
|
||||
}
|
||||
|
||||
reportProblemSymbols(undefs,
|
||||
/* localImports */ nullptr, ObjFile::instances,
|
||||
&BitcodeFile::instances);
|
||||
}
|
||||
|
||||
void SymbolTable::resolveRemainingUndefines() {
|
||||
SmallPtrSet<Symbol *, 8> undefs;
|
||||
DenseMap<Symbol *, Symbol *> localImports;
|
||||
|
||||
@ -290,46 +449,9 @@ void SymbolTable::reportRemainingUndefines() {
|
||||
undefs.insert(sym);
|
||||
}
|
||||
|
||||
if (undefs.empty() && localImports.empty())
|
||||
return;
|
||||
|
||||
for (Symbol *b : config->gcroot) {
|
||||
if (undefs.count(b))
|
||||
errorOrWarn("<root>: undefined symbol: " + toString(*b));
|
||||
if (config->warnLocallyDefinedImported)
|
||||
if (Symbol *imp = localImports.lookup(b))
|
||||
warn("<root>: locally defined symbol imported: " + toString(*imp) +
|
||||
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
||||
}
|
||||
|
||||
std::vector<UndefinedDiag> undefDiags;
|
||||
DenseMap<Symbol *, int> firstDiag;
|
||||
|
||||
for (ObjFile *file : ObjFile::instances) {
|
||||
size_t symIndex = (size_t)-1;
|
||||
for (Symbol *sym : file->getSymbols()) {
|
||||
++symIndex;
|
||||
if (!sym)
|
||||
continue;
|
||||
if (undefs.count(sym)) {
|
||||
auto it = firstDiag.find(sym);
|
||||
if (it == firstDiag.end()) {
|
||||
firstDiag[sym] = undefDiags.size();
|
||||
undefDiags.push_back({sym, {{file, symIndex}}});
|
||||
} else {
|
||||
undefDiags[it->second].files.push_back({file, symIndex});
|
||||
}
|
||||
}
|
||||
if (config->warnLocallyDefinedImported)
|
||||
if (Symbol *imp = localImports.lookup(sym))
|
||||
warn(toString(file) +
|
||||
": locally defined symbol imported: " + toString(*imp) +
|
||||
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
||||
}
|
||||
}
|
||||
|
||||
for (const UndefinedDiag& undefDiag : undefDiags)
|
||||
reportUndefinedSymbol(undefDiag);
|
||||
reportProblemSymbols(
|
||||
undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
|
||||
ObjFile::instances, /* bitcode files no longer needed */ nullptr);
|
||||
}
|
||||
|
||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
|
||||
@ -356,26 +478,22 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(name, f);
|
||||
if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) {
|
||||
if (wasInserted || (s->isLazy() && isWeakAlias)) {
|
||||
replaceSymbol<Undefined>(s, name);
|
||||
return s;
|
||||
}
|
||||
if (auto *l = dyn_cast<Lazy>(s)) {
|
||||
if (!s->pendingArchiveLoad) {
|
||||
s->pendingArchiveLoad = true;
|
||||
l->file->addMember(l->sym);
|
||||
}
|
||||
}
|
||||
if (s->isLazy())
|
||||
forceLazy(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) {
|
||||
void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
|
||||
StringRef name = sym.getName();
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(name);
|
||||
if (wasInserted) {
|
||||
replaceSymbol<Lazy>(s, f, sym);
|
||||
replaceSymbol<LazyArchive>(s, f, sym);
|
||||
return;
|
||||
}
|
||||
auto *u = dyn_cast<Undefined>(s);
|
||||
@ -385,15 +503,84 @@ void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) {
|
||||
f->addMember(sym);
|
||||
}
|
||||
|
||||
void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
|
||||
std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
|
||||
toString(existing->getFile()) + " and in " +
|
||||
toString(newFile);
|
||||
void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, f);
|
||||
if (wasInserted) {
|
||||
replaceSymbol<LazyObject>(s, f, n);
|
||||
return;
|
||||
}
|
||||
auto *u = dyn_cast<Undefined>(s);
|
||||
if (!u || u->weakAlias || s->pendingArchiveLoad)
|
||||
return;
|
||||
s->pendingArchiveLoad = true;
|
||||
f->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)
|
||||
warn(msg);
|
||||
warn(os.str());
|
||||
else
|
||||
error(msg);
|
||||
error(os.str());
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
|
||||
@ -401,7 +588,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
||||
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||
replaceSymbol<DefinedAbsolute>(s, n, sym);
|
||||
else if (!isa<DefinedCOFF>(s))
|
||||
reportDuplicate(s, nullptr);
|
||||
@ -413,7 +600,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
||||
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||
replaceSymbol<DefinedAbsolute>(s, n, va);
|
||||
else if (!isa<DefinedCOFF>(s))
|
||||
reportDuplicate(s, nullptr);
|
||||
@ -425,7 +612,7 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
||||
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||
replaceSymbol<DefinedSynthetic>(s, n, c);
|
||||
else if (!isa<DefinedCOFF>(s))
|
||||
reportDuplicate(s, nullptr);
|
||||
@ -433,8 +620,8 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
|
||||
const coff_symbol_generic *sym,
|
||||
SectionChunk *c) {
|
||||
const coff_symbol_generic *sym, SectionChunk *c,
|
||||
uint32_t sectionOffset) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, f);
|
||||
@ -442,7 +629,7 @@ Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
|
||||
replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
|
||||
/*IsExternal*/ true, sym, c);
|
||||
else
|
||||
reportDuplicate(s, f);
|
||||
reportDuplicate(s, f, c, sectionOffset);
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -481,7 +668,7 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
|
||||
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
|
||||
replaceSymbol<DefinedImportData>(s, n, f);
|
||||
return s;
|
||||
}
|
||||
@ -496,7 +683,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(name, nullptr);
|
||||
s->isUsedInRegularObj = true;
|
||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
|
||||
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
|
||||
replaceSymbol<DefinedImportThunk>(s, name, id, machine);
|
||||
return s;
|
||||
}
|
||||
@ -510,9 +697,12 @@ void SymbolTable::addLibcall(StringRef name) {
|
||||
if (!sym)
|
||||
return;
|
||||
|
||||
if (Lazy *l = dyn_cast<Lazy>(sym)) {
|
||||
if (auto *l = dyn_cast<LazyArchive>(sym)) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class Defined;
|
||||
class DefinedAbsolute;
|
||||
class DefinedRegular;
|
||||
class DefinedRelative;
|
||||
class Lazy;
|
||||
class LazyArchive;
|
||||
class SectionChunk;
|
||||
class Symbol;
|
||||
|
||||
@ -49,10 +49,13 @@ class SymbolTable {
|
||||
public:
|
||||
void addFile(InputFile *file);
|
||||
|
||||
// Emit errors for symbols that cannot be resolved.
|
||||
void reportUnresolvable();
|
||||
|
||||
// Try to resolve any undefined symbols and update the symbol table
|
||||
// accordingly, then print an error message for any remaining undefined
|
||||
// symbols.
|
||||
void reportRemainingUndefines();
|
||||
// symbols and warn about imported local symbols.
|
||||
void resolveRemainingUndefines();
|
||||
|
||||
void loadMinGWAutomaticImports();
|
||||
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
|
||||
@ -83,11 +86,12 @@ class SymbolTable {
|
||||
Symbol *addAbsolute(StringRef n, uint64_t va);
|
||||
|
||||
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
|
||||
void addLazy(ArchiveFile *f, const Archive::Symbol &sym);
|
||||
void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym);
|
||||
void addLazyObject(LazyObjFile *f, StringRef n);
|
||||
Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
|
||||
Symbol *addRegular(InputFile *f, StringRef n,
|
||||
const llvm::object::coff_symbol_generic *s = nullptr,
|
||||
SectionChunk *c = nullptr);
|
||||
SectionChunk *c = nullptr, uint32_t sectionOffset = 0);
|
||||
std::pair<DefinedRegular *, bool>
|
||||
addComdat(InputFile *f, StringRef n,
|
||||
const llvm::object::coff_symbol_generic *s = nullptr);
|
||||
@ -99,7 +103,9 @@ class SymbolTable {
|
||||
uint16_t machine);
|
||||
void addLibcall(StringRef name);
|
||||
|
||||
void reportDuplicate(Symbol *existing, InputFile *newFile);
|
||||
void reportDuplicate(Symbol *existing, InputFile *newFile,
|
||||
SectionChunk *newSc = nullptr,
|
||||
uint32_t newSectionOffset = 0);
|
||||
|
||||
// A list of chunks which to be added to .rdata.
|
||||
std::vector<Chunk *> localImportChunks;
|
||||
@ -111,6 +117,9 @@ class SymbolTable {
|
||||
}
|
||||
|
||||
private:
|
||||
/// Given a name without "__imp_" prefix, returns a defined symbol
|
||||
/// with the "__imp_" prefix, if it exists.
|
||||
Defined *impSymbol(StringRef name);
|
||||
/// Inserts symbol if not already present.
|
||||
std::pair<Symbol *, bool> insert(StringRef name);
|
||||
/// Same as insert(Name), but also sets isUsedInRegularObj.
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "lld/Common/Strings.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Demangle/Demangle.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
@ -26,15 +27,27 @@ static_assert(sizeof(SymbolUnion) <= 48,
|
||||
"symbols should be optimized for memory usage");
|
||||
|
||||
// Returns a symbol name for an error message.
|
||||
static std::string demangle(StringRef symName) {
|
||||
if (config->demangle)
|
||||
if (Optional<std::string> s = demangleMSVC(symName))
|
||||
return *s;
|
||||
static std::string maybeDemangleSymbol(StringRef symName) {
|
||||
if (config->demangle) {
|
||||
std::string prefix;
|
||||
StringRef prefixless = symName;
|
||||
if (prefixless.consume_front("__imp_"))
|
||||
prefix = "__declspec(dllimport) ";
|
||||
StringRef demangleInput = prefixless;
|
||||
if (config->machine == I386)
|
||||
demangleInput.consume_front("_");
|
||||
std::string demangled = demangle(demangleInput);
|
||||
if (demangled != demangleInput)
|
||||
return prefix + demangle(demangleInput);
|
||||
return (prefix + prefixless).str();
|
||||
}
|
||||
return symName;
|
||||
}
|
||||
std::string toString(coff::Symbol &b) { return demangle(b.getName()); }
|
||||
std::string toString(coff::Symbol &b) {
|
||||
return maybeDemangleSymbol(b.getName());
|
||||
}
|
||||
std::string toCOFFString(const Archive::Symbol &b) {
|
||||
return demangle(b.getName());
|
||||
return maybeDemangleSymbol(b.getName());
|
||||
}
|
||||
|
||||
namespace coff {
|
||||
@ -61,7 +74,9 @@ StringRef Symbol::getName() {
|
||||
InputFile *Symbol::getFile() {
|
||||
if (auto *sym = dyn_cast<DefinedCOFF>(this))
|
||||
return sym->file;
|
||||
if (auto *sym = dyn_cast<Lazy>(this))
|
||||
if (auto *sym = dyn_cast<LazyArchive>(this))
|
||||
return sym->file;
|
||||
if (auto *sym = dyn_cast<LazyObject>(this))
|
||||
return sym->file;
|
||||
return nullptr;
|
||||
}
|
||||
@ -119,7 +134,7 @@ Defined *Undefined::getWeakAlias() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MemoryBufferRef Lazy::getMemberBuffer() {
|
||||
MemoryBufferRef LazyArchive::getMemberBuffer() {
|
||||
Archive::Child c =
|
||||
CHECK(sym.getMember(),
|
||||
"could not get the member for symbol " + toCOFFString(sym));
|
||||
|
@ -59,7 +59,8 @@ class Symbol {
|
||||
DefinedSyntheticKind,
|
||||
|
||||
UndefinedKind,
|
||||
LazyKind,
|
||||
LazyArchiveKind,
|
||||
LazyObjectKind,
|
||||
|
||||
LastDefinedCOFFKind = DefinedCommonKind,
|
||||
LastDefinedKind = DefinedSyntheticKind,
|
||||
@ -79,6 +80,10 @@ class Symbol {
|
||||
// after calling markLive.
|
||||
bool isLive() const;
|
||||
|
||||
bool isLazy() const {
|
||||
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
|
||||
}
|
||||
|
||||
protected:
|
||||
friend SymbolTable;
|
||||
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
|
||||
// created from an archive file header, and it knows how to load an
|
||||
// object file from an archive to replace itself with a defined
|
||||
// symbol. If the resolver finds both Undefined and Lazy for
|
||||
// the same name, it will ask the Lazy to load a file.
|
||||
class Lazy : public Symbol {
|
||||
// symbol. If the resolver finds both Undefined and LazyArchive for
|
||||
// the same name, it will ask the LazyArchive to load a file.
|
||||
class LazyArchive : public Symbol {
|
||||
public:
|
||||
Lazy(ArchiveFile *f, const Archive::Symbol s)
|
||||
: Symbol(LazyKind, s.getName()), file(f), sym(s) {}
|
||||
LazyArchive(ArchiveFile *f, const Archive::Symbol s)
|
||||
: Symbol(LazyArchiveKind, s.getName()), file(f), sym(s) {}
|
||||
|
||||
static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
|
||||
static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
|
||||
|
||||
MemoryBufferRef getMemberBuffer();
|
||||
|
||||
ArchiveFile *file;
|
||||
|
||||
private:
|
||||
friend SymbolTable;
|
||||
|
||||
private:
|
||||
const Archive::Symbol sym;
|
||||
};
|
||||
|
||||
class LazyObject : public Symbol {
|
||||
public:
|
||||
LazyObject(LazyObjFile *f, StringRef n)
|
||||
: Symbol(LazyObjectKind, n), file(f) {}
|
||||
static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
|
||||
LazyObjFile *file;
|
||||
};
|
||||
|
||||
// Undefined symbols.
|
||||
class Undefined : public Symbol {
|
||||
public:
|
||||
@ -381,7 +389,8 @@ inline uint64_t Defined::getRVA() {
|
||||
return cast<DefinedCommon>(this)->getRVA();
|
||||
case DefinedRegularKind:
|
||||
return cast<DefinedRegular>(this)->getRVA();
|
||||
case LazyKind:
|
||||
case LazyArchiveKind:
|
||||
case LazyObjectKind:
|
||||
case UndefinedKind:
|
||||
llvm_unreachable("Cannot get the address for an undefined symbol.");
|
||||
}
|
||||
@ -404,7 +413,8 @@ inline Chunk *Defined::getChunk() {
|
||||
return cast<DefinedLocalImport>(this)->getChunk();
|
||||
case DefinedCommonKind:
|
||||
return cast<DefinedCommon>(this)->getChunk();
|
||||
case LazyKind:
|
||||
case LazyArchiveKind:
|
||||
case LazyObjectKind:
|
||||
case UndefinedKind:
|
||||
llvm_unreachable("Cannot get the chunk of an undefined symbol.");
|
||||
}
|
||||
@ -419,11 +429,12 @@ union SymbolUnion {
|
||||
alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
|
||||
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
|
||||
alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)];
|
||||
alignas(Lazy) char e[sizeof(Lazy)];
|
||||
alignas(LazyArchive) char e[sizeof(LazyArchive)];
|
||||
alignas(Undefined) char f[sizeof(Undefined)];
|
||||
alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
|
||||
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
|
||||
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
|
||||
alignas(LazyObject) char j[sizeof(LazyObject)];
|
||||
};
|
||||
|
||||
template <typename T, typename... ArgT>
|
||||
|
@ -40,8 +40,9 @@ using namespace llvm::COFF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
/* To re-generate DOSProgram:
|
||||
$ cat > /tmp/DOSProgram.asm
|
||||
@ -240,6 +241,8 @@ class Writer {
|
||||
IdataContents idata;
|
||||
Chunk *importTableStart = nullptr;
|
||||
uint64_t importTableSize = 0;
|
||||
Chunk *edataStart = nullptr;
|
||||
Chunk *edataEnd = nullptr;
|
||||
Chunk *iatStart = nullptr;
|
||||
uint64_t iatSize = 0;
|
||||
DelayLoadContents delayIdata;
|
||||
@ -283,9 +286,6 @@ class Writer {
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
static Timer codeLayoutTimer("Code Layout", Timer::root());
|
||||
static Timer diskCommitTimer("Commit Output File", Timer::root());
|
||||
|
||||
@ -331,9 +331,6 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
|
||||
contribSections.push_back(sec);
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
||||
// Check whether the target address S is in range from a relocation
|
||||
// of type relType at address P.
|
||||
static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
|
||||
@ -741,7 +738,8 @@ void Writer::addSyntheticIdata() {
|
||||
add(".idata$2", idata.dirs);
|
||||
add(".idata$4", idata.lookups);
|
||||
add(".idata$5", idata.addresses);
|
||||
add(".idata$6", idata.hints);
|
||||
if (!idata.hints.empty())
|
||||
add(".idata$6", idata.hints);
|
||||
add(".idata$7", idata.dllNames);
|
||||
}
|
||||
|
||||
@ -840,6 +838,7 @@ void Writer::createSections() {
|
||||
}
|
||||
|
||||
fixPartialSectionChars(".rsrc", data | r);
|
||||
fixPartialSectionChars(".edata", data | r);
|
||||
// Even in non MinGW cases, we might need to link against GNU import
|
||||
// libraries.
|
||||
bool hasIdata = fixGnuImportChunks();
|
||||
@ -1014,10 +1013,19 @@ void Writer::appendImportThunks() {
|
||||
}
|
||||
|
||||
void Writer::createExportTable() {
|
||||
if (config->exports.empty())
|
||||
return;
|
||||
for (Chunk *c : edata.chunks)
|
||||
edataSec->addChunk(c);
|
||||
if (!edataSec->chunks.empty()) {
|
||||
// Allow using a custom built export table from input object files, instead
|
||||
// of having the linker synthesize the tables.
|
||||
if (config->hadExplicitExports)
|
||||
warn("literal .edata sections override exports");
|
||||
} else if (!config->exports.empty()) {
|
||||
for (Chunk *c : edata.chunks)
|
||||
edataSec->addChunk(c);
|
||||
}
|
||||
if (!edataSec->chunks.empty()) {
|
||||
edataStart = edataSec->chunks.front();
|
||||
edataEnd = edataSec->chunks.back();
|
||||
}
|
||||
}
|
||||
|
||||
void Writer::removeUnusedSections() {
|
||||
@ -1366,9 +1374,10 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
||||
// Write data directory
|
||||
auto *dir = reinterpret_cast<data_directory *>(buf);
|
||||
buf += sizeof(*dir) * numberOfDataDirectory;
|
||||
if (!config->exports.empty()) {
|
||||
dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA();
|
||||
dir[EXPORT_TABLE].Size = edata.getSize();
|
||||
if (edataStart) {
|
||||
dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA();
|
||||
dir[EXPORT_TABLE].Size =
|
||||
edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA();
|
||||
}
|
||||
if (importTableStart) {
|
||||
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
|
||||
@ -1506,7 +1515,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
|
||||
// Absolute is never code, synthetic generally isn't and usually isn't
|
||||
// determinable.
|
||||
break;
|
||||
case Symbol::LazyKind:
|
||||
case Symbol::LazyArchiveKind:
|
||||
case Symbol::LazyObjectKind:
|
||||
case Symbol::UndefinedKind:
|
||||
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy
|
||||
// symbols shouldn't have relocations.
|
||||
@ -1930,3 +1940,6 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
@ -29,6 +29,7 @@ set_property(SOURCE Version.cpp APPEND PROPERTY
|
||||
|
||||
add_lld_library(lldCommon
|
||||
Args.cpp
|
||||
DWARF.cpp
|
||||
ErrorHandler.cpp
|
||||
Filesystem.cpp
|
||||
Memory.cpp
|
||||
@ -46,6 +47,7 @@ add_lld_library(lldCommon
|
||||
LINK_COMPONENTS
|
||||
Codegen
|
||||
Core
|
||||
DebugInfoDWARF
|
||||
Demangle
|
||||
MC
|
||||
Option
|
||||
|
103
contrib/llvm-project/lld/Common/DWARF.cpp
Normal file
103
contrib/llvm-project/lld/Common/DWARF.cpp
Normal 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
|
@ -29,16 +29,14 @@ using namespace lld;
|
||||
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
||||
static std::mutex mu;
|
||||
|
||||
// Prints "\n" or does nothing, depending on Msg contents of
|
||||
// the previous call of this function.
|
||||
static void newline(raw_ostream *errorOS, const Twine &msg) {
|
||||
// True if the previous error message contained "\n".
|
||||
// We want to separate multi-line error messages with a newline.
|
||||
static bool flag;
|
||||
// We want to separate multi-line messages with a newline. `sep` is "\n"
|
||||
// if the last messages was multi-line. Otherwise "".
|
||||
static StringRef sep;
|
||||
|
||||
if (flag)
|
||||
*errorOS << "\n";
|
||||
flag = StringRef(msg.str()).contains('\n');
|
||||
static StringRef getSeparator(const Twine &msg) {
|
||||
if (StringRef(msg.str()).contains('\n'))
|
||||
return "\n";
|
||||
return "";
|
||||
}
|
||||
|
||||
ErrorHandler &lld::errorHandler() {
|
||||
@ -46,6 +44,10 @@ ErrorHandler &lld::errorHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
void lld::enableColors(bool enable) {
|
||||
errorHandler().errorOS->enable_colors(enable);
|
||||
}
|
||||
|
||||
void lld::exitLld(int val) {
|
||||
// Delete any temporary file, while keeping the memory mapping open.
|
||||
if (errorHandler().outputBuffer)
|
||||
@ -85,56 +87,69 @@ void lld::checkError(Error e) {
|
||||
[&](ErrorInfoBase &eib) { error(eib.message()); });
|
||||
}
|
||||
|
||||
static std::string getLocation(std::string msg, std::string defaultMsg) {
|
||||
static std::vector<std::regex> Regexes{
|
||||
std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"),
|
||||
// This is for --vs-diagnostics.
|
||||
//
|
||||
// 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"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"),
|
||||
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(
|
||||
R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),
|
||||
std::regex(
|
||||
R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"),
|
||||
R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))"),
|
||||
std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),
|
||||
std::regex(R"((\S+):(\d+): unclosed quote)"),
|
||||
};
|
||||
|
||||
std::smatch Match;
|
||||
for (std::regex &Re : Regexes) {
|
||||
if (std::regex_search(msg, Match, Re)) {
|
||||
return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")"
|
||||
: Match.str(1);
|
||||
}
|
||||
}
|
||||
return defaultMsg;
|
||||
}
|
||||
std::string str = msg.str();
|
||||
for (std::regex &re : regexes) {
|
||||
std::smatch m;
|
||||
if (!std::regex_search(str, m, re))
|
||||
continue;
|
||||
|
||||
void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c,
|
||||
const Twine &msg) {
|
||||
|
||||
if (vsDiagnostics) {
|
||||
// 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 << ": ";
|
||||
assert(m.size() == 2 || m.size() == 3);
|
||||
if (m.size() == 2)
|
||||
return m.str(1);
|
||||
return m.str(1) + "(" + m.str(2) + ")";
|
||||
}
|
||||
|
||||
if (colorDiagnostics) {
|
||||
errorOS->changeColor(c, true);
|
||||
*errorOS << s;
|
||||
errorOS->resetColor();
|
||||
} else {
|
||||
*errorOS << s;
|
||||
}
|
||||
return logName;
|
||||
}
|
||||
|
||||
void ErrorHandler::log(const Twine &msg) {
|
||||
if (verbose) {
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
*errorOS << logName << ": " << msg << "\n";
|
||||
}
|
||||
if (!verbose)
|
||||
return;
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
*errorOS << logName << ": " << msg << "\n";
|
||||
}
|
||||
|
||||
void ErrorHandler::message(const Twine &msg) {
|
||||
@ -150,25 +165,41 @@ void ErrorHandler::warn(const Twine &msg) {
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(mu);
|
||||
newline(errorOS, msg);
|
||||
printHeader("warning: ", raw_ostream::MAGENTA, msg);
|
||||
*errorOS << msg << "\n";
|
||||
*errorOS << sep << getLocation(msg) << ": " << Colors::MAGENTA
|
||||
<< "warning: " << Colors::RESET << msg << "\n";
|
||||
sep = getSeparator(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);
|
||||
newline(errorOS, msg);
|
||||
|
||||
if (errorLimit == 0 || errorCount < errorLimit) {
|
||||
printHeader("error: ", raw_ostream::RED, msg);
|
||||
*errorOS << msg << "\n";
|
||||
*errorOS << sep << getLocation(msg) << ": " << Colors::RED
|
||||
<< "error: " << Colors::RESET << msg << "\n";
|
||||
} else if (errorCount == errorLimit) {
|
||||
printHeader("error: ", raw_ostream::RED, msg);
|
||||
*errorOS << errorLimitExceededMsg << "\n";
|
||||
*errorOS << sep << getLocation(msg) << ": " << Colors::RED
|
||||
<< "error: " << Colors::RESET << errorLimitExceededMsg << "\n";
|
||||
if (exitEarly)
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
sep = getSeparator(msg);
|
||||
++errorCount;
|
||||
}
|
||||
|
||||
|
@ -18,39 +18,17 @@
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
|
||||
// Returns the demangled C++ symbol name for Name.
|
||||
Optional<std::string> lld::demangleItanium(StringRef name) {
|
||||
// Returns the demangled C++ symbol name for name.
|
||||
std::string lld::demangleItanium(StringRef name) {
|
||||
// itaniumDemangle can be used to demangle strings other than symbol
|
||||
// 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
|
||||
// result for a C symbol that happens to match a mangled type name.
|
||||
if (!name.startswith("_Z"))
|
||||
return None;
|
||||
return name;
|
||||
|
||||
char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr);
|
||||
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;
|
||||
return demangle(name);
|
||||
}
|
||||
|
||||
StringMatcher::StringMatcher(ArrayRef<StringRef> pat) {
|
||||
@ -96,7 +74,7 @@ bool lld::isValidCIdentifier(StringRef s) {
|
||||
// Write the contents of the a buffer to a file
|
||||
void lld::saveBuffer(StringRef buffer, const Twine &path) {
|
||||
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)
|
||||
error("cannot create " + path + ": " + ec.message());
|
||||
os << buffer;
|
||||
|
@ -26,6 +26,10 @@ llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
|
||||
return ::InitTargetOptionsFromCodeGenFlags();
|
||||
}
|
||||
|
||||
llvm::Optional<llvm::Reloc::Model> lld::getRelocModelFromCMModel() {
|
||||
return getRelocModel();
|
||||
}
|
||||
|
||||
llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
|
||||
return getCodeModel();
|
||||
}
|
||||
|
@ -6,7 +6,10 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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
|
||||
// instructions in the sequence is replaced with a branch to a patch sequence
|
||||
// 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
|
||||
// the replacement sequence.
|
||||
// - 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"
|
||||
@ -48,8 +45,8 @@ using namespace llvm::object;
|
||||
using namespace llvm::support;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
// Helper functions to identify instructions and conditions needed to trigger
|
||||
// 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
|
||||
// InputSection IS. We update Off in this function rather than in the caller as
|
||||
// we can skip ahead much further into the section when we know how many
|
||||
// InputSection isec. We update Off in this function rather than in the caller
|
||||
// as we can skip ahead much further into the section when we know how many
|
||||
// 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.
|
||||
static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
|
||||
uint64_t limit) {
|
||||
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;
|
||||
if (initialPageOff < 0xff8)
|
||||
off += 0xff8 - initialPageOff;
|
||||
@ -374,7 +371,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
|
||||
return patchOff;
|
||||
}
|
||||
|
||||
class lld::elf::Patch843419Section : public SyntheticSection {
|
||||
class Patch843419Section : public SyntheticSection {
|
||||
public:
|
||||
Patch843419Section(InputSection *p, uint64_t off);
|
||||
|
||||
@ -386,13 +383,13 @@ class lld::elf::Patch843419Section : public SyntheticSection {
|
||||
|
||||
// The Section we are patching.
|
||||
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;
|
||||
// A label for the start of the Patch that we can use as a relocation target.
|
||||
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,
|
||||
".text.patch"),
|
||||
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);
|
||||
}
|
||||
|
||||
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
|
||||
uint64_t Patch843419Section::getLDSTAddr() const {
|
||||
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
|
||||
// Patchee Section.
|
||||
// patchee Section.
|
||||
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
|
||||
// also adds outSecOff so we need to subtract to avoid double counting.
|
||||
this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
|
||||
@ -461,18 +458,18 @@ void AArch64Err843419Patcher::init() {
|
||||
// $d.0 $d.1 $x.1.
|
||||
for (auto &kv : sectionMap) {
|
||||
std::vector<const Defined *> &mapSyms = kv.second;
|
||||
if (mapSyms.size() <= 1)
|
||||
continue;
|
||||
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 (isCodeMapSymbol(a) && isCodeMapSymbol(b)) ||
|
||||
(isDataMapSymbol(a) && isDataMapSymbol(b));
|
||||
return isCodeMapSymbol(a) == isCodeMapSymbol(b);
|
||||
}),
|
||||
mapSyms.end());
|
||||
// Always start with a Code Mapping Symbol.
|
||||
if (!mapSyms.empty() && !isCodeMapSymbol(mapSyms.front()))
|
||||
mapSyms.erase(mapSyms.begin());
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
@ -511,19 +508,16 @@ void AArch64Err843419Patcher::insertPatches(
|
||||
(*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
|
||||
// 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 true;
|
||||
if (a->outSecOff == b->outSecOff && isa<Patch843419Section>(a) &&
|
||||
!isa<Patch843419Section>(b))
|
||||
return true;
|
||||
return false;
|
||||
if (a->outSecOff != b->outSecOff)
|
||||
return a->outSecOff < b->outSecOff;
|
||||
return isa<Patch843419Section>(a) && !isa<Patch843419Section>(b);
|
||||
};
|
||||
std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
|
||||
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
|
||||
// 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.
|
||||
static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
|
||||
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
|
||||
// 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 *>
|
||||
AArch64Err843419Patcher::patchInputSectionDescription(
|
||||
InputSectionDescription &isd) {
|
||||
@ -594,10 +588,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
|
||||
// section size).
|
||||
std::vector<const Defined *> &mapSyms = sectionMap[isec];
|
||||
|
||||
auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) {
|
||||
return ms->getName().startswith("$x");
|
||||
});
|
||||
|
||||
auto codeSym = mapSyms.begin();
|
||||
while (codeSym != mapSyms.end()) {
|
||||
auto dataSym = std::next(codeSym);
|
||||
uint64_t off = (*codeSym)->value;
|
||||
@ -606,7 +597,8 @@ AArch64Err843419Patcher::patchInputSectionDescription(
|
||||
|
||||
while (off < limit) {
|
||||
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);
|
||||
}
|
||||
if (dataSym == mapSyms.end())
|
||||
@ -630,7 +622,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
|
||||
// Ouptut and Input Sections may have been changed.
|
||||
// Returns false if no patches were required and no changes were made.
|
||||
bool AArch64Err843419Patcher::createFixes() {
|
||||
if (initialized == false)
|
||||
if (!initialized)
|
||||
init();
|
||||
|
||||
bool addressesChanged = false;
|
||||
@ -649,3 +641,5 @@ bool AArch64Err843419Patcher::createFixes() {
|
||||
}
|
||||
return addressesChanged;
|
||||
}
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
528
contrib/llvm-project/lld/ELF/ARMErrataFix.cpp
Normal file
528
contrib/llvm-project/lld/ELF/ARMErrataFix.cpp
Normal 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
|
51
contrib/llvm-project/lld/ELF/ARMErrataFix.h
Normal file
51
contrib/llvm-project/lld/ELF/ARMErrataFix.h
Normal 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
|
@ -17,13 +17,14 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
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
|
||||
// as (Expr & ~0xFFF). (This applies even if the machine page size
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -76,6 +77,26 @@ AArch64::AArch64() {
|
||||
RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
||||
const uint8_t *loc) const {
|
||||
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:
|
||||
return R_AARCH64_TLSDESC_PAGE;
|
||||
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_LDST64_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;
|
||||
case R_AARCH64_CALL26:
|
||||
case R_AARCH64_CONDBR19:
|
||||
@ -101,6 +127,13 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
||||
case R_AARCH64_PREL64:
|
||||
case R_AARCH64_ADR_PREL_LO21:
|
||||
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;
|
||||
case R_AARCH64_ADR_PREL_PG_HI21:
|
||||
case R_AARCH64_ADR_PREL_PG_HI21_NC:
|
||||
@ -114,7 +147,9 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
||||
case R_AARCH64_NONE:
|
||||
return R_NONE;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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 {
|
||||
switch (type) {
|
||||
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);
|
||||
or32AArch64Imm(loc, getBits(val, 4, 11));
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G0:
|
||||
checkUInt(loc, val, 16, type);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_AARCH64_MOVW_UABS_G0_NC:
|
||||
or32le(loc, (val & 0xFFFF) << 5);
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G1:
|
||||
checkUInt(loc, val, 32, type);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_AARCH64_MOVW_UABS_G1_NC:
|
||||
or32le(loc, (val & 0xFFFF0000) >> 11);
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G2:
|
||||
checkUInt(loc, val, 48, type);
|
||||
LLVM_FALLTHROUGH;
|
||||
case R_AARCH64_MOVW_UABS_G2_NC:
|
||||
or32le(loc, (val & 0xFFFF00000000) >> 27);
|
||||
break;
|
||||
case R_AARCH64_MOVW_UABS_G3:
|
||||
or32le(loc, (val & 0xFFFF000000000000) >> 43);
|
||||
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:
|
||||
checkInt(loc, val, 16, type);
|
||||
or32le(loc, (val & 0xFFFC) << 3);
|
||||
@ -351,7 +444,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
or32AArch64Imm(loc, val);
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
}
|
||||
|
||||
@ -587,4 +680,7 @@ static TargetInfo *getTargetInfo() {
|
||||
return &t;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
|
||||
TargetInfo *getAArch64TargetInfo() { return getTargetInfo(); }
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -17,8 +17,9 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
class AMDGPU final : public TargetInfo {
|
||||
@ -107,7 +108,10 @@ RelType AMDGPU::getDynRel(RelType type) const {
|
||||
return R_AMDGPU_NONE;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getAMDGPUTargetInfo() {
|
||||
TargetInfo *getAMDGPUTargetInfo() {
|
||||
static AMDGPU target;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -18,8 +18,9 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
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;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -36,8 +36,9 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
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;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -19,8 +19,9 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
class Hexagon final : public TargetInfo {
|
||||
@ -29,6 +30,7 @@ class Hexagon final : public TargetInfo {
|
||||
uint32_t calcEFlags() const override;
|
||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||
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 writePltHeader(uint8_t *buf) const override;
|
||||
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,
|
||||
const uint8_t *loc) const {
|
||||
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_X:
|
||||
case R_HEX_B13_PCREL:
|
||||
case R_HEX_B15_PCREL:
|
||||
case R_HEX_B15_PCREL_X:
|
||||
case R_HEX_6_PCREL_X:
|
||||
case R_HEX_32_PCREL:
|
||||
return R_PC;
|
||||
case R_HEX_B9_PCREL_X:
|
||||
case R_HEX_B15_PCREL_X:
|
||||
case R_HEX_B22_PCREL:
|
||||
case R_HEX_PLT_B22_PCREL:
|
||||
case R_HEX_B22_PCREL_X:
|
||||
case R_HEX_B32_PCREL_X:
|
||||
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_16_X:
|
||||
case R_HEX_GOT_32_6_X:
|
||||
return R_HEXAGON_GOT;
|
||||
return R_GOTPLT;
|
||||
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;
|
||||
case R_HEX_11_X:
|
||||
case R_HEX_GOT_11_X:
|
||||
case R_HEX_GOTREL_11_X:
|
||||
or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f));
|
||||
break;
|
||||
case R_HEX_12_X:
|
||||
@ -204,6 +229,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
break;
|
||||
case R_HEX_16_X: // These relocs only have 6 effective bits.
|
||||
case R_HEX_GOT_16_X:
|
||||
case R_HEX_GOTREL_16_X:
|
||||
or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f));
|
||||
break;
|
||||
case R_HEX_32:
|
||||
@ -212,18 +238,22 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
break;
|
||||
case R_HEX_32_6_X:
|
||||
case R_HEX_GOT_32_6_X:
|
||||
case R_HEX_GOTREL_32_6_X:
|
||||
or32le(loc, applyMask(0x0fff3fff, val >> 6));
|
||||
break;
|
||||
case R_HEX_B9_PCREL:
|
||||
checkInt(loc, val, 11, type);
|
||||
or32le(loc, applyMask(0x003000fe, val >> 2));
|
||||
break;
|
||||
case R_HEX_B9_PCREL_X:
|
||||
or32le(loc, applyMask(0x003000fe, val & 0x3f));
|
||||
break;
|
||||
case R_HEX_B13_PCREL:
|
||||
checkInt(loc, val, 15, type);
|
||||
or32le(loc, applyMask(0x00202ffe, val >> 2));
|
||||
break;
|
||||
case R_HEX_B15_PCREL:
|
||||
checkInt(loc, val, 17, type);
|
||||
or32le(loc, applyMask(0x00df20fe, val >> 2));
|
||||
break;
|
||||
case R_HEX_B15_PCREL_X:
|
||||
@ -231,6 +261,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
break;
|
||||
case R_HEX_B22_PCREL:
|
||||
case R_HEX_PLT_B22_PCREL:
|
||||
checkInt(loc, val, 22, type);
|
||||
or32le(loc, applyMask(0x1ff3ffe, val >> 2));
|
||||
break;
|
||||
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:
|
||||
or32le(loc, applyMask(0x0fff3fff, val >> 6));
|
||||
break;
|
||||
case R_HEX_GOTREL_HI16:
|
||||
case R_HEX_HI16:
|
||||
or32le(loc, applyMask(0x00c03fff, val >> 16));
|
||||
break;
|
||||
case R_HEX_GOTREL_LO16:
|
||||
case R_HEX_LO16:
|
||||
or32le(loc, applyMask(0x00c03fff, val));
|
||||
break;
|
||||
default:
|
||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
||||
break;
|
||||
llvm_unreachable("unknown relocation");
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,7 +317,16 @@ void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
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;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -26,8 +26,9 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
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;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -14,15 +14,13 @@
|
||||
#include "Thunks.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
namespace {
|
||||
template <class ELFT> class MIPS final : public TargetInfo {
|
||||
public:
|
||||
@ -85,8 +83,14 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
|
||||
|
||||
switch (type) {
|
||||
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:
|
||||
return R_HINT;
|
||||
return R_NONE;
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_GPREL32:
|
||||
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_DTPREL32:
|
||||
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_LO16:
|
||||
case R_MIPS_TLS_TPREL32:
|
||||
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_LO16:
|
||||
return R_ABS;
|
||||
return R_TLS;
|
||||
case R_MIPS_PC32:
|
||||
case R_MIPS_PC16:
|
||||
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();
|
||||
if (isMicroMips())
|
||||
va |= 1;
|
||||
write32<ELFT::TargetEndianness>(buf, va);
|
||||
write32(buf, va);
|
||||
}
|
||||
|
||||
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
|
||||
// words in a big-endian order. That is why we have to swap these
|
||||
// words to get a correct value.
|
||||
uint32_t v = read32<E>(loc);
|
||||
uint32_t v = read32(loc);
|
||||
if (E == support::little)
|
||||
return (v << 16) | (v >> 16);
|
||||
return v;
|
||||
}
|
||||
|
||||
template <endianness E>
|
||||
static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
||||
uint8_t shift) {
|
||||
uint32_t instr = read32<E>(loc);
|
||||
uint32_t instr = read32(loc);
|
||||
uint32_t mask = 0xffffffff >> (32 - bitsSize);
|
||||
uint32_t data = (instr & ~mask) | ((v >> shift) & mask);
|
||||
write32<E>(loc, data);
|
||||
write32(loc, data);
|
||||
}
|
||||
|
||||
template <endianness E>
|
||||
@ -225,7 +229,7 @@ static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
||||
if (E == support::little)
|
||||
std::swap(words[0], words[1]);
|
||||
|
||||
writeValue<E>(loc, v, bitsSize, shift);
|
||||
writeValue(loc, v, bitsSize, shift);
|
||||
|
||||
if (E == support::little)
|
||||
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>
|
||||
static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
||||
uint8_t shift) {
|
||||
uint16_t instr = read16<E>(loc);
|
||||
uint16_t instr = read16(loc);
|
||||
uint16_t mask = 0xffff >> (16 - bitsSize);
|
||||
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 {
|
||||
const endianness e = ELFT::TargetEndianness;
|
||||
if (isMicroMips()) {
|
||||
uint64_t gotPlt = in.gotPlt->getVA();
|
||||
uint64_t plt = in.plt->getVA();
|
||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||
memset(buf, 0, pltHeaderSize);
|
||||
|
||||
write16<e>(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
|
||||
write16<e>(buf + 4, 0xff23); // lw $25, 0($3)
|
||||
write16<e>(buf + 8, 0x0535); // subu16 $2, $2, $3
|
||||
write16<e>(buf + 10, 0x2525); // srl16 $2, $2, 2
|
||||
write16<e>(buf + 12, 0x3302); // addiu $24, $2, -2
|
||||
write16<e>(buf + 14, 0xfffe);
|
||||
write16<e>(buf + 16, 0x0dff); // move $15, $31
|
||||
write16(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
|
||||
write16(buf + 4, 0xff23); // lw $25, 0($3)
|
||||
write16(buf + 8, 0x0535); // subu16 $2, $2, $3
|
||||
write16(buf + 10, 0x2525); // srl16 $2, $2, 2
|
||||
write16(buf + 12, 0x3302); // addiu $24, $2, -2
|
||||
write16(buf + 14, 0xfffe);
|
||||
write16(buf + 16, 0x0dff); // move $15, $31
|
||||
if (isMipsR6()) {
|
||||
write16<e>(buf + 18, 0x0f83); // move $28, $3
|
||||
write16<e>(buf + 20, 0x472b); // jalrc $25
|
||||
write16<e>(buf + 22, 0x0c00); // nop
|
||||
write16(buf + 18, 0x0f83); // move $28, $3
|
||||
write16(buf + 20, 0x472b); // jalrc $25
|
||||
write16(buf + 22, 0x0c00); // nop
|
||||
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
|
||||
} else {
|
||||
write16<e>(buf + 18, 0x45f9); // jalrc $25
|
||||
write16<e>(buf + 20, 0x0f83); // move $28, $3
|
||||
write16<e>(buf + 22, 0x0c00); // nop
|
||||
write16(buf + 18, 0x45f9); // jalrc $25
|
||||
write16(buf + 20, 0x0f83); // move $28, $3
|
||||
write16(buf + 22, 0x0c00); // nop
|
||||
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (config->mipsN32Abi) {
|
||||
write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32<e>(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
|
||||
write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
|
||||
write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32(buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
write32(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32(buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
} else if (ELFT::Is64Bits) {
|
||||
write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32<e>(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
|
||||
write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<e>(buf + 20, 0x0018c0c2); // srl $24, $24, 3
|
||||
write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
|
||||
write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32(buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
write32(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32(buf + 20, 0x0018c0c2); // srl $24, $24, 3
|
||||
} else {
|
||||
write32<e>(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
|
||||
write32<e>(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
|
||||
write32<e>(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
||||
write32<e>(buf + 12, 0x031cc023); // subu $24, $24, $28
|
||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
write32(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
|
||||
write32(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
|
||||
write32(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
||||
write32(buf + 12, 0x031cc023); // subu $24, $24, $28
|
||||
write32(buf + 16, 0x03e07825); // move $15, $31
|
||||
write32(buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
}
|
||||
|
||||
uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809;
|
||||
write32<e>(buf + 24, jalrInst); // jalr.hb $25 or jalr $25
|
||||
write32<e>(buf + 28, 0x2718fffe); // subu $24, $24, 2
|
||||
write32(buf + 24, jalrInst); // jalr.hb $25 or jalr $25
|
||||
write32(buf + 28, 0x2718fffe); // subu $24, $24, 2
|
||||
|
||||
uint64_t gotPlt = in.gotPlt->getVA();
|
||||
writeValue<e>(buf, gotPlt + 0x8000, 16, 16);
|
||||
writeValue<e>(buf + 4, gotPlt, 16, 0);
|
||||
writeValue<e>(buf + 8, gotPlt, 16, 0);
|
||||
writeValue(buf, gotPlt + 0x8000, 16, 16);
|
||||
writeValue(buf + 4, gotPlt, 16, 0);
|
||||
writeValue(buf + 8, gotPlt, 16, 0);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
uint64_t pltEntryAddr, int32_t index,
|
||||
unsigned relOff) const {
|
||||
const endianness e = ELFT::TargetEndianness;
|
||||
if (isMicroMips()) {
|
||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||
memset(buf, 0, pltEntrySize);
|
||||
|
||||
if (isMipsR6()) {
|
||||
write16<e>(buf, 0x7840); // addiupc $2, (GOTPLT) - .
|
||||
write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
|
||||
write16<e>(buf + 8, 0x0f02); // move $24, $2
|
||||
write16<e>(buf + 10, 0x4723); // jrc $25 / jr16 $25
|
||||
write16(buf, 0x7840); // addiupc $2, (GOTPLT) - .
|
||||
write16(buf + 4, 0xff22); // lw $25, 0($2)
|
||||
write16(buf + 8, 0x0f02); // move $24, $2
|
||||
write16(buf + 10, 0x4723); // jrc $25 / jr16 $25
|
||||
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
|
||||
} else {
|
||||
write16<e>(buf, 0x7900); // addiupc $2, (GOTPLT) - .
|
||||
write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
|
||||
write16<e>(buf + 8, 0x4599); // jrc $25 / jr16 $25
|
||||
write16<e>(buf + 10, 0x0f02); // move $24, $2
|
||||
write16(buf, 0x7900); // addiupc $2, (GOTPLT) - .
|
||||
write16(buf + 4, 0xff22); // lw $25, 0($2)
|
||||
write16(buf + 8, 0x4599); // jrc $25 / jr16 $25
|
||||
write16(buf + 10, 0x0f02); // move $24, $2
|
||||
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
|
||||
}
|
||||
return;
|
||||
@ -332,13 +334,13 @@ void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
: (config->zHazardplt ? 0x03200408 : 0x03200008);
|
||||
uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000;
|
||||
|
||||
write32<e>(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
|
||||
write32<e>(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15)
|
||||
write32<e>(buf + 8, jrInst); // jr $25 / jr.hb $25
|
||||
write32<e>(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry)
|
||||
writeValue<e>(buf, gotPltEntryAddr + 0x8000, 16, 16);
|
||||
writeValue<e>(buf + 4, gotPltEntryAddr, 16, 0);
|
||||
writeValue<e>(buf + 12, gotPltEntryAddr, 16, 0);
|
||||
write32(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
|
||||
write32(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15)
|
||||
write32(buf + 8, jrInst); // jr $25 / jr.hb $25
|
||||
write32(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry)
|
||||
writeValue(buf, gotPltEntryAddr + 0x8000, 16, 16);
|
||||
writeValue(buf + 4, gotPltEntryAddr, 16, 0);
|
||||
writeValue(buf + 12, gotPltEntryAddr, 16, 0);
|
||||
}
|
||||
|
||||
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_TLS_DTPREL32:
|
||||
case R_MIPS_TLS_TPREL32:
|
||||
return SignExtend64<32>(read32<e>(buf));
|
||||
return SignExtend64<32>(read32(buf));
|
||||
case R_MIPS_26:
|
||||
// FIXME (simon): If the relocation target symbol is not a PLT entry
|
||||
// we should use another expression for calculation:
|
||||
// ((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_HI16:
|
||||
case R_MIPS_PCHI16:
|
||||
return SignExtend64<16>(read32<e>(buf)) << 16;
|
||||
return SignExtend64<16>(read32(buf)) << 16;
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_LO16:
|
||||
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_TPREL_HI16:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
return SignExtend64<16>(read32<e>(buf));
|
||||
return SignExtend64<16>(read32(buf));
|
||||
case R_MICROMIPS_GOT16:
|
||||
case R_MICROMIPS_HI16:
|
||||
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:
|
||||
return SignExtend64<9>(readShuffle<e>(buf) << 2);
|
||||
case R_MIPS_PC16:
|
||||
return SignExtend64<18>(read32<e>(buf) << 2);
|
||||
return SignExtend64<18>(read32(buf) << 2);
|
||||
case R_MIPS_PC19_S2:
|
||||
return SignExtend64<21>(read32<e>(buf) << 2);
|
||||
return SignExtend64<21>(read32(buf) << 2);
|
||||
case R_MIPS_PC21_S2:
|
||||
return SignExtend64<23>(read32<e>(buf) << 2);
|
||||
return SignExtend64<23>(read32(buf) << 2);
|
||||
case R_MIPS_PC26_S2:
|
||||
return SignExtend64<28>(read32<e>(buf) << 2);
|
||||
return SignExtend64<28>(read32(buf) << 2);
|
||||
case R_MIPS_PC32:
|
||||
return SignExtend64<32>(read32<e>(buf));
|
||||
return SignExtend64<32>(read32(buf));
|
||||
case R_MICROMIPS_26_S1:
|
||||
return SignExtend64<27>(readShuffle<e>(buf) << 1);
|
||||
case R_MICROMIPS_PC7_S1:
|
||||
return SignExtend64<8>(read16<e>(buf) << 1);
|
||||
return SignExtend64<8>(read16(buf) << 1);
|
||||
case R_MICROMIPS_PC10_S1:
|
||||
return SignExtend64<11>(read16<e>(buf) << 1);
|
||||
return SignExtend64<11>(read16(buf) << 1);
|
||||
case R_MICROMIPS_PC16_S1:
|
||||
return SignExtend64<17>(readShuffle<e>(buf) << 1);
|
||||
case R_MICROMIPS_PC18_S3:
|
||||
@ -487,9 +489,9 @@ static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) {
|
||||
|
||||
switch (type) {
|
||||
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
|
||||
writeValue<e>(loc, 0x1d << 26, 32, 0);
|
||||
writeValue(loc, 0x1d << 26, 32, 0);
|
||||
return val;
|
||||
}
|
||||
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_LO16) {
|
||||
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) {
|
||||
@ -550,25 +547,25 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||
case R_MIPS_GPREL32:
|
||||
case R_MIPS_TLS_DTPREL32:
|
||||
case R_MIPS_TLS_TPREL32:
|
||||
write32<e>(loc, val);
|
||||
write32(loc, val);
|
||||
break;
|
||||
case R_MIPS_64:
|
||||
case R_MIPS_TLS_DTPREL64:
|
||||
case R_MIPS_TLS_TPREL64:
|
||||
write64<e>(loc, val);
|
||||
write64(loc, val);
|
||||
break;
|
||||
case R_MIPS_26:
|
||||
writeValue<e>(loc, val, 26, 2);
|
||||
writeValue(loc, val, 26, 2);
|
||||
break;
|
||||
case R_MIPS_GOT16:
|
||||
// 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
|
||||
// to store a correct addend value.
|
||||
if (config->relocatable) {
|
||||
writeValue<e>(loc, val + 0x8000, 16, 16);
|
||||
writeValue(loc, val + 0x8000, 16, 16);
|
||||
} else {
|
||||
checkInt(loc, val, 16, type);
|
||||
writeValue<e>(loc, val, 16, 0);
|
||||
writeValue(loc, val, 16, 0);
|
||||
}
|
||||
break;
|
||||
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_TLS_DTPREL_LO16:
|
||||
case R_MIPS_TLS_TPREL_LO16:
|
||||
writeValue<e>(loc, val, 16, 0);
|
||||
writeValue(loc, val, 16, 0);
|
||||
break;
|
||||
case R_MICROMIPS_GPREL16:
|
||||
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_TLS_DTPREL_HI16:
|
||||
case R_MIPS_TLS_TPREL_HI16:
|
||||
writeValue<e>(loc, val + 0x8000, 16, 16);
|
||||
writeValue(loc, val + 0x8000, 16, 16);
|
||||
break;
|
||||
case R_MICROMIPS_CALL_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);
|
||||
break;
|
||||
case R_MIPS_HIGHER:
|
||||
writeValue<e>(loc, val + 0x80008000, 16, 32);
|
||||
writeValue(loc, val + 0x80008000, 16, 32);
|
||||
break;
|
||||
case R_MIPS_HIGHEST:
|
||||
writeValue<e>(loc, val + 0x800080008000, 16, 48);
|
||||
writeValue(loc, val + 0x800080008000, 16, 48);
|
||||
break;
|
||||
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:
|
||||
// Ignore this optimization relocation for now
|
||||
break;
|
||||
case R_MIPS_PC16:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 18, type);
|
||||
writeValue<e>(loc, val, 16, 2);
|
||||
writeValue(loc, val, 16, 2);
|
||||
break;
|
||||
case R_MIPS_PC19_S2:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 21, type);
|
||||
writeValue<e>(loc, val, 19, 2);
|
||||
writeValue(loc, val, 19, 2);
|
||||
break;
|
||||
case R_MIPS_PC21_S2:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 23, type);
|
||||
writeValue<e>(loc, val, 21, 2);
|
||||
writeValue(loc, val, 21, 2);
|
||||
break;
|
||||
case R_MIPS_PC26_S2:
|
||||
checkAlignment(loc, val, 4, type);
|
||||
checkInt(loc, val, 28, type);
|
||||
writeValue<e>(loc, val, 26, 2);
|
||||
writeValue(loc, val, 26, 2);
|
||||
break;
|
||||
case R_MIPS_PC32:
|
||||
writeValue<e>(loc, val, 32, 0);
|
||||
writeValue(loc, val, 32, 0);
|
||||
break;
|
||||
case R_MICROMIPS_26_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.
|
||||
template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
|
||||
template <class ELFT> bool isMipsPIC(const Defined *sym) {
|
||||
if (!sym->isFunc())
|
||||
return false;
|
||||
|
||||
@ -725,17 +736,20 @@ template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
|
||||
return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
|
||||
}
|
||||
|
||||
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
|
||||
template <class ELFT> TargetInfo *getMipsTargetInfo() {
|
||||
static MIPS<ELFT> target;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF32BE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF64LE>();
|
||||
template TargetInfo *elf::getMipsTargetInfo<ELF64BE>();
|
||||
template TargetInfo *getMipsTargetInfo<ELF32LE>();
|
||||
template TargetInfo *getMipsTargetInfo<ELF32BE>();
|
||||
template TargetInfo *getMipsTargetInfo<ELF64LE>();
|
||||
template TargetInfo *getMipsTargetInfo<ELF64BE>();
|
||||
|
||||
template bool elf::isMipsPIC<ELF32LE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF32BE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF64LE>(const Defined *);
|
||||
template bool elf::isMipsPIC<ELF64BE>(const Defined *);
|
||||
template bool isMipsPIC<ELF32LE>(const Defined *);
|
||||
template bool isMipsPIC<ELF32BE>(const Defined *);
|
||||
template bool isMipsPIC<ELF64LE>(const Defined *);
|
||||
template bool isMipsPIC<ELF64BE>(const Defined *);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -23,8 +23,8 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
struct ArchTreeEdge {
|
||||
@ -166,17 +166,17 @@ static ArchTreeEdge archTree[] = {
|
||||
{EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
|
||||
};
|
||||
|
||||
static bool isArchMatched(uint32_t New, uint32_t res) {
|
||||
if (New == res)
|
||||
static bool isArchMatched(uint32_t newFlags, uint32_t res) {
|
||||
if (newFlags == res)
|
||||
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;
|
||||
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;
|
||||
for (const auto &edge : archTree) {
|
||||
if (res == edge.child) {
|
||||
res = edge.parent;
|
||||
if (res == New)
|
||||
if (res == newFlags)
|
||||
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);
|
||||
|
||||
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.
|
||||
if (isArchMatched(New, ret))
|
||||
if (isArchMatched(newFlags, ret))
|
||||
continue;
|
||||
if (!isArchMatched(ret, New)) {
|
||||
if (!isArchMatched(ret, newFlags)) {
|
||||
error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
|
||||
getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
|
||||
getFullArchName(New));
|
||||
getFullArchName(newFlags));
|
||||
return 0;
|
||||
}
|
||||
ret = New;
|
||||
ret = newFlags;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// 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.
|
||||
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() {
|
||||
template <class ELFT> uint32_t calcMipsEFlags() {
|
||||
std::vector<FileFlags> v;
|
||||
for (InputFile *f : objectFiles)
|
||||
v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
|
||||
if (v.empty())
|
||||
return getFlagsFromEmulation();
|
||||
if (v.empty()) {
|
||||
// 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);
|
||||
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,
|
||||
StringRef fileName) {
|
||||
uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, StringRef fileName) {
|
||||
if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
|
||||
return newFlag;
|
||||
if (compareMipsFpAbi(oldFlag, newFlag) < 0)
|
||||
@ -379,7 +366,7 @@ template <class ELFT> static bool isN32Abi(const InputFile *f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool elf::isMipsN32Abi(const InputFile *f) {
|
||||
bool isMipsN32Abi(const InputFile *f) {
|
||||
switch (config->ekind) {
|
||||
case ELF32LEKind:
|
||||
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;
|
||||
return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
|
||||
}
|
||||
|
||||
template uint32_t elf::calcMipsEFlags<ELF32LE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF32BE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF64LE>();
|
||||
template uint32_t elf::calcMipsEFlags<ELF64BE>();
|
||||
template uint32_t calcMipsEFlags<ELF32LE>();
|
||||
template uint32_t calcMipsEFlags<ELF32BE>();
|
||||
template uint32_t calcMipsEFlags<ELF64LE>();
|
||||
template uint32_t calcMipsEFlags<ELF64BE>();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -16,8 +16,9 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
// absolute address from a specific .plt slot (usually called .got.plt on
|
||||
// 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;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -16,8 +16,9 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
static uint64_t ppc64TocOffset = 0x8000;
|
||||
static uint64_t dynamicThreadPointerOffset = 0x8000;
|
||||
@ -59,7 +60,7 @@ enum DFormOpcd {
|
||||
ADDI = 14
|
||||
};
|
||||
|
||||
uint64_t elf::getPPC64TocBase() {
|
||||
uint64_t getPPC64TocBase() {
|
||||
// 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
|
||||
// .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;
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
@ -98,7 +99,7 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool elf::isPPC64SmallCodeModelTocReloc(RelType type) {
|
||||
bool isPPC64SmallCodeModelTocReloc(RelType type) {
|
||||
// The only small code model relocations that access the .toc section.
|
||||
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
|
||||
//
|
||||
// Returns true if the relaxation is performed.
|
||||
bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
|
||||
uint8_t *bufLoc) {
|
||||
bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
|
||||
uint8_t *bufLoc) {
|
||||
assert(config->tocOptimize);
|
||||
if (rel.addend < 0)
|
||||
return false;
|
||||
@ -175,6 +176,10 @@ bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
|
||||
if (!d || d->isPreemptible)
|
||||
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.
|
||||
uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase();
|
||||
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) {
|
||||
case LBZX:
|
||||
return LBZ;
|
||||
@ -1089,7 +1094,10 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
|
||||
return true;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getPPC64TargetInfo() {
|
||||
TargetInfo *getPPC64TargetInfo() {
|
||||
static PPC64 target;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -14,8 +14,9 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
|
||||
@ -439,7 +440,10 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
|
||||
}
|
||||
}
|
||||
|
||||
TargetInfo *elf::getRISCVTargetInfo() {
|
||||
TargetInfo *getRISCVTargetInfo() {
|
||||
static RISCV target;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -16,8 +16,9 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
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));
|
||||
}
|
||||
|
||||
TargetInfo *elf::getSPARCV9TargetInfo() {
|
||||
TargetInfo *getSPARCV9TargetInfo() {
|
||||
static SPARCV9 target;
|
||||
return ⌖
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -16,8 +16,9 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
class X86 : public TargetInfo {
|
||||
@ -539,7 +540,7 @@ void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||
write32le(buf + 22, -off - 26);
|
||||
}
|
||||
|
||||
TargetInfo *elf::getX86TargetInfo() {
|
||||
TargetInfo *getX86TargetInfo() {
|
||||
if (config->zRetpolineplt) {
|
||||
if (config->isPic) {
|
||||
static RetpolinePic t;
|
||||
@ -552,3 +553,6 @@ TargetInfo *elf::getX86TargetInfo() {
|
||||
static X86 t;
|
||||
return &t;
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -18,8 +18,9 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
class X86_64 : public TargetInfo {
|
||||
@ -698,4 +699,7 @@ static TargetInfo *getTargetInfo() {
|
||||
return &t;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); }
|
||||
TargetInfo *getX86_64TargetInfo() { return getTargetInfo(); }
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -22,6 +22,7 @@ add_lld_library(lldELF
|
||||
Arch/SPARCV9.cpp
|
||||
Arch/X86.cpp
|
||||
Arch/X86_64.cpp
|
||||
ARMErrataFix.cpp
|
||||
CallGraphSort.cpp
|
||||
DWARF.cpp
|
||||
Driver.cpp
|
||||
|
@ -45,9 +45,12 @@
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
namespace {
|
||||
struct Edge {
|
||||
@ -56,7 +59,7 @@ struct Edge {
|
||||
};
|
||||
|
||||
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 {
|
||||
if (size == 0)
|
||||
@ -64,7 +67,8 @@ struct Cluster {
|
||||
return double(weight) / double(size);
|
||||
}
|
||||
|
||||
std::vector<int> sections;
|
||||
int next;
|
||||
int prev;
|
||||
size_t size = 0;
|
||||
uint64_t weight = 0;
|
||||
uint64_t initialWeight = 0;
|
||||
@ -80,8 +84,6 @@ class CallGraphSort {
|
||||
private:
|
||||
std::vector<Cluster> clusters;
|
||||
std::vector<const InputSectionBase *> sections;
|
||||
|
||||
void groupClusters();
|
||||
};
|
||||
|
||||
// Maximum ammount the combined cluster density can be worse than the original
|
||||
@ -103,7 +105,7 @@ CallGraphSort::CallGraphSort() {
|
||||
DenseMap<const InputSectionBase *, int> secToCluster;
|
||||
|
||||
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) {
|
||||
sections.push_back(isec);
|
||||
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;
|
||||
}
|
||||
|
||||
static void mergeClusters(Cluster &into, Cluster &from) {
|
||||
into.sections.insert(into.sections.end(), from.sections.begin(),
|
||||
from.sections.end());
|
||||
// Find the leader of V's belonged cluster (represented as an equivalence
|
||||
// class). We apply union-find path-halving technique (simple to implement) in
|
||||
// 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.weight += from.weight;
|
||||
from.sections.clear();
|
||||
from.size = 0;
|
||||
from.weight = 0;
|
||||
}
|
||||
|
||||
// Group InputSections into clusters using the Call-Chain Clustering heuristic
|
||||
// then sort the clusters by density.
|
||||
void CallGraphSort::groupClusters() {
|
||||
std::vector<int> sortedSecs(clusters.size());
|
||||
std::vector<Cluster *> secToCluster(clusters.size());
|
||||
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
|
||||
std::vector<int> sorted(clusters.size());
|
||||
std::vector<int> leaders(clusters.size());
|
||||
|
||||
for (size_t i = 0; i < clusters.size(); ++i) {
|
||||
sortedSecs[i] = i;
|
||||
secToCluster[i] = &clusters[i];
|
||||
}
|
||||
|
||||
llvm::stable_sort(sortedSecs, [&](int a, int b) {
|
||||
std::iota(leaders.begin(), leaders.end(), 0);
|
||||
std::iota(sorted.begin(), sorted.end(), 0);
|
||||
llvm::stable_sort(sorted, [&](int a, int b) {
|
||||
return clusters[a].getDensity() > clusters[b].getDensity();
|
||||
});
|
||||
|
||||
for (int si : sortedSecs) {
|
||||
// clusters[si] is the same as secToClusters[si] here because it has not
|
||||
// been merged into another cluster yet.
|
||||
Cluster &c = clusters[si];
|
||||
for (int l : sorted) {
|
||||
// The cluster index is the same as the index of its leader here because
|
||||
// clusters[L] has not been merged into another cluster yet.
|
||||
Cluster &c = clusters[l];
|
||||
|
||||
// Don't consider merging if the edge is unlikely.
|
||||
if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
|
||||
continue;
|
||||
|
||||
Cluster *predC = secToCluster[c.bestPred.from];
|
||||
if (predC == &c)
|
||||
int predL = getLeader(leaders, c.bestPred.from);
|
||||
if (l == predL)
|
||||
continue;
|
||||
|
||||
Cluster *predC = &clusters[predL];
|
||||
if (c.size + predC->size > MAX_CLUSTER_SIZE)
|
||||
continue;
|
||||
|
||||
if (isNewDensityBad(*predC, c))
|
||||
continue;
|
||||
|
||||
// NOTE: Consider using a disjoint-set to track section -> cluster mapping
|
||||
// if this is ever slow.
|
||||
for (int si : c.sections)
|
||||
secToCluster[si] = predC;
|
||||
|
||||
mergeClusters(*predC, c);
|
||||
leaders[l] = predL;
|
||||
mergeClusters(clusters, *predC, predL, c, l);
|
||||
}
|
||||
|
||||
// Remove empty or dead nodes. Invalidates all cluster indices.
|
||||
llvm::erase_if(clusters, [](const Cluster &c) {
|
||||
return c.size == 0 || c.sections.empty();
|
||||
// Sort remaining non-empty clusters by density.
|
||||
sorted.clear();
|
||||
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;
|
||||
ssize_t curOrder = 1;
|
||||
|
||||
for (const Cluster &c : clusters)
|
||||
for (int secIndex : c.sections)
|
||||
orderMap[sections[secIndex]] = curOrder++;
|
||||
int curOrder = 1;
|
||||
for (int leader : sorted)
|
||||
for (int i = leader;;) {
|
||||
orderMap[sections[i]] = curOrder++;
|
||||
i = clusters[i].next;
|
||||
if (i == leader)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!config->printSymbolOrder.empty()) {
|
||||
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) {
|
||||
error("cannot open " + config->printSymbolOrder + ": " + ec.message());
|
||||
return orderMap;
|
||||
@ -235,15 +242,19 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
|
||||
|
||||
// Print the symbols ordered by C3, in the order of increasing curOrder
|
||||
// Instead of sorting all the orderMap, just repeat the loops above.
|
||||
for (const Cluster &c : clusters)
|
||||
for (int secIndex : c.sections)
|
||||
for (int leader : sorted)
|
||||
for (int i = leader;;) {
|
||||
// Search all the symbols in the file of 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 (auto *d = dyn_cast<Defined>(sym))
|
||||
if (sections[secIndex] == d->section)
|
||||
if (sections[i] == d->section)
|
||||
os << sym->getName() << "\n";
|
||||
i = clusters[i].next;
|
||||
if (i == leader)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// according to the C³ huristic. All clusters are then sorted by a density
|
||||
// metric to further improve locality.
|
||||
DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
|
||||
DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder() {
|
||||
return CallGraphSort().run();
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -61,6 +61,9 @@ enum class Target2Policy { Abs, Rel, GotRel };
|
||||
// For tracking ARM Float Argument PCS
|
||||
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 {
|
||||
llvm::StringRef name;
|
||||
bool isExternCpp;
|
||||
@ -71,8 +74,8 @@ struct SymbolVersion {
|
||||
// can be found in version script if it is used for link.
|
||||
struct VersionDefinition {
|
||||
llvm::StringRef name;
|
||||
uint16_t id = 0;
|
||||
std::vector<SymbolVersion> globals;
|
||||
uint16_t id;
|
||||
std::vector<SymbolVersion> patterns;
|
||||
};
|
||||
|
||||
// This struct contains the global configuration for the linker.
|
||||
@ -117,8 +120,6 @@ struct Configuration {
|
||||
std::vector<llvm::StringRef> symbolOrderingFile;
|
||||
std::vector<llvm::StringRef> undefined;
|
||||
std::vector<SymbolVersion> dynamicList;
|
||||
std::vector<SymbolVersion> versionScriptGlobals;
|
||||
std::vector<SymbolVersion> versionScriptLocals;
|
||||
std::vector<uint8_t> buildIdVector;
|
||||
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
|
||||
uint64_t>
|
||||
@ -147,6 +148,7 @@ struct Configuration {
|
||||
bool executeOnly;
|
||||
bool exportDynamic;
|
||||
bool fixCortexA53Errata843419;
|
||||
bool fixCortexA8;
|
||||
bool forceBTI;
|
||||
bool formatBinary = false;
|
||||
bool requireCET;
|
||||
@ -222,8 +224,8 @@ struct Configuration {
|
||||
Target2Policy target2;
|
||||
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
|
||||
BuildIdKind buildId = BuildIdKind::None;
|
||||
SeparateSegmentKind zSeparate;
|
||||
ELFKind ekind = ELFNoneKind;
|
||||
uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
|
||||
uint16_t emachine = llvm::ELF::EM_NONE;
|
||||
llvm::Optional<uint64_t> imageBase;
|
||||
uint64_t commonPageSize;
|
||||
@ -309,6 +311,12 @@ struct Configuration {
|
||||
// The only instance of Configuration struct.
|
||||
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) {
|
||||
if (!config->noinhibitExec)
|
||||
error(msg);
|
||||
|
@ -22,9 +22,9 @@
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
|
||||
for (InputSectionBase *sec : obj->getSections()) {
|
||||
if (!sec)
|
||||
@ -33,11 +33,12 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
|
||||
if (LLDDWARFSection *m =
|
||||
StringSwitch<LLDDWARFSection *>(sec->name)
|
||||
.Case(".debug_addr", &addrSection)
|
||||
.Case(".debug_gnu_pubnames", &gnuPubNamesSection)
|
||||
.Case(".debug_gnu_pubtypes", &gnuPubTypesSection)
|
||||
.Case(".debug_gnu_pubnames", &gnuPubnamesSection)
|
||||
.Case(".debug_gnu_pubtypes", &gnuPubtypesSection)
|
||||
.Case(".debug_info", &infoSection)
|
||||
.Case(".debug_ranges", &rangeSection)
|
||||
.Case(".debug_rnglists", &rngListsSection)
|
||||
.Case(".debug_ranges", &rangesSection)
|
||||
.Case(".debug_rnglists", &rnglistsSection)
|
||||
.Case(".debug_str_offsets", &strOffsetsSection)
|
||||
.Case(".debug_line", &lineSection)
|
||||
.Default(nullptr)) {
|
||||
m->Data = toStringRef(sec->data());
|
||||
@ -50,7 +51,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
|
||||
else if (sec->name == ".debug_str")
|
||||
strSection = toStringRef(sec->data());
|
||||
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>());
|
||||
}
|
||||
|
||||
template class elf::LLDDwarfObj<ELF32LE>;
|
||||
template class elf::LLDDwarfObj<ELF32BE>;
|
||||
template class elf::LLDDwarfObj<ELF64LE>;
|
||||
template class elf::LLDDwarfObj<ELF64BE>;
|
||||
template class LLDDwarfObj<ELF32LE>;
|
||||
template class LLDDwarfObj<ELF32BE>;
|
||||
template class LLDDwarfObj<ELF64LE>;
|
||||
template class LLDDwarfObj<ELF64BE>;
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -32,12 +32,16 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
||||
f(infoSection);
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getRangeSection() const override {
|
||||
return rangeSection;
|
||||
const llvm::DWARFSection &getRangesSection() const override {
|
||||
return rangesSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getRnglistsSection() const override {
|
||||
return rngListsSection;
|
||||
return rnglistsSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getStrOffsetsSection() const override {
|
||||
return strOffsetsSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getLineSection() const override {
|
||||
@ -48,18 +52,18 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
||||
return addrSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getGnuPubNamesSection() const override {
|
||||
return gnuPubNamesSection;
|
||||
const llvm::DWARFSection &getGnuPubnamesSection() const override {
|
||||
return gnuPubnamesSection;
|
||||
}
|
||||
|
||||
const llvm::DWARFSection &getGnuPubTypesSection() const override {
|
||||
return gnuPubTypesSection;
|
||||
const llvm::DWARFSection &getGnuPubtypesSection() const override {
|
||||
return gnuPubtypesSection;
|
||||
}
|
||||
|
||||
StringRef getFileName() const override { return ""; }
|
||||
StringRef getAbbrevSection() const override { return abbrevSection; }
|
||||
StringRef getStringSection() const override { return strSection; }
|
||||
StringRef getLineStringSection() const override { return lineStringSection; }
|
||||
StringRef getStrSection() const override { return strSection; }
|
||||
StringRef getLineStrSection() const override { return lineStrSection; }
|
||||
|
||||
bool isLittleEndian() const override {
|
||||
return ELFT::TargetEndianness == llvm::support::little;
|
||||
@ -74,16 +78,17 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
||||
uint64_t pos,
|
||||
ArrayRef<RelTy> rels) const;
|
||||
|
||||
LLDDWARFSection gnuPubNamesSection;
|
||||
LLDDWARFSection gnuPubTypesSection;
|
||||
LLDDWARFSection gnuPubnamesSection;
|
||||
LLDDWARFSection gnuPubtypesSection;
|
||||
LLDDWARFSection infoSection;
|
||||
LLDDWARFSection rangeSection;
|
||||
LLDDWARFSection rngListsSection;
|
||||
LLDDWARFSection rangesSection;
|
||||
LLDDWARFSection rnglistsSection;
|
||||
LLDDWARFSection strOffsetsSection;
|
||||
LLDDWARFSection lineSection;
|
||||
LLDDWARFSection addrSection;
|
||||
StringRef abbrevSection;
|
||||
StringRef strSection;
|
||||
StringRef lineStringSection;
|
||||
StringRef lineStrSection;
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/Compression.h"
|
||||
#include "llvm/Support/GlobPattern.h"
|
||||
@ -65,24 +66,23 @@ using namespace llvm::object;
|
||||
using namespace llvm::sys;
|
||||
using namespace llvm::support;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
Configuration *elf::config;
|
||||
LinkerDriver *elf::driver;
|
||||
Configuration *config;
|
||||
LinkerDriver *driver;
|
||||
|
||||
static void setConfigs(opt::InputArgList &args);
|
||||
static void readConfigs(opt::InputArgList &args);
|
||||
|
||||
bool elf::link(ArrayRef<const char *> args, bool canExitEarly,
|
||||
raw_ostream &error) {
|
||||
bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &error) {
|
||||
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
|
||||
errorHandler().errorLimitExceededMsg =
|
||||
"too many errors emitted, stopping now (use "
|
||||
"-error-limit=0 to see all errors)";
|
||||
errorHandler().errorOS = &error;
|
||||
errorHandler().exitEarly = canExitEarly;
|
||||
errorHandler().colorDiagnostics = error.has_colors();
|
||||
enableColors(error.has_colors());
|
||||
|
||||
inputSections.clear();
|
||||
outputSections.clear();
|
||||
@ -299,6 +299,9 @@ static void checkOptions() {
|
||||
if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64)
|
||||
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)
|
||||
error("--toc-optimize is only supported on the PowerPC64 target");
|
||||
|
||||
@ -314,6 +317,9 @@ static void checkOptions() {
|
||||
if (!config->relocatable && !config->defineCommon)
|
||||
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)
|
||||
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");
|
||||
if (config->pie)
|
||||
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) {
|
||||
@ -373,17 +381,32 @@ static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2,
|
||||
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) {
|
||||
return s == "combreloc" || s == "copyreloc" || s == "defs" ||
|
||||
s == "execstack" || s == "global" || s == "hazardplt" ||
|
||||
s == "ifunc-noplt" || s == "initfirst" || s == "interpose" ||
|
||||
s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
|
||||
s == "separate-code" || s == "separate-loadable-segments" ||
|
||||
s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
|
||||
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
|
||||
s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" ||
|
||||
s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" ||
|
||||
s == "rodynamic" || s == "text" || s == "wxneeded" ||
|
||||
s.startswith("common-page-size") || s.startswith("max-page-size=") ||
|
||||
s == "nokeep-text-section-prefix" || s == "norelro" ||
|
||||
s == "noseparate-code" || s == "notext" || s == "now" ||
|
||||
s == "origin" || s == "relro" || s == "retpolineplt" ||
|
||||
s == "rodynamic" || s == "text" || s == "undefs" || s == "wxneeded" ||
|
||||
s.startswith("common-page-size=") || s.startswith("max-page-size=") ||
|
||||
s.startswith("stack-size=");
|
||||
}
|
||||
|
||||
@ -513,6 +536,8 @@ static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) {
|
||||
case OPT_z:
|
||||
if (StringRef(arg->getValue()) == "defs")
|
||||
return errorOrWarn;
|
||||
if (StringRef(arg->getValue()) == "undefs")
|
||||
return UnresolvedPolicy::Ignore;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -747,6 +772,12 @@ static bool getCompressDebugSections(opt::InputArgList &args) {
|
||||
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,
|
||||
unsigned id) {
|
||||
auto *arg = args.getLastArg(id);
|
||||
@ -756,7 +787,7 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
|
||||
StringRef s = arg->getValue();
|
||||
std::pair<StringRef, StringRef> ret = s.split(';');
|
||||
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;
|
||||
}
|
||||
|
||||
@ -829,6 +860,7 @@ static void readConfigs(opt::InputArgList &args) {
|
||||
config->filterList = args::getStrings(args, OPT_filter);
|
||||
config->fini = args.getLastArgValue(OPT_fini, "_fini");
|
||||
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->requireCET = args.hasArg(OPT_require_cet);
|
||||
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->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
|
||||
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->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
|
||||
config->mapFile = args.getLastArgValue(OPT_Map);
|
||||
@ -892,17 +924,15 @@ static void readConfigs(opt::InputArgList &args) {
|
||||
config->thinLTOCachePolicy = CHECK(
|
||||
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
|
||||
"--thinlto-cache-policy: invalid cache policy");
|
||||
config->thinLTOEmitImportsFiles =
|
||||
args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files);
|
||||
config->thinLTOIndexOnly = args.hasArg(OPT_plugin_opt_thinlto_index_only) ||
|
||||
args.hasArg(OPT_plugin_opt_thinlto_index_only_eq);
|
||||
config->thinLTOIndexOnlyArg =
|
||||
args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq);
|
||||
config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
|
||||
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
|
||||
args.hasArg(OPT_thinlto_index_only_eq);
|
||||
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
|
||||
config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
|
||||
config->thinLTOObjectSuffixReplace =
|
||||
getOldNewOptions(args, OPT_plugin_opt_thinlto_object_suffix_replace_eq);
|
||||
getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
|
||||
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->undefined = args::getStrings(args, OPT_undefined);
|
||||
config->undefinedVersion =
|
||||
@ -935,6 +965,7 @@ static void readConfigs(opt::InputArgList &args) {
|
||||
config->zRelro = getZFlag(args, "relro", "norelro", true);
|
||||
config->zRetpolineplt = hasZOption(args, "retpolineplt");
|
||||
config->zRodynamic = hasZOption(args, "rodynamic");
|
||||
config->zSeparate = getZSeparate(args);
|
||||
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
|
||||
config->zText = getZFlag(args, "text", "notext", true);
|
||||
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
|
||||
// the file and discard all others.
|
||||
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()))
|
||||
for (StringRef s : args::getLines(*buffer))
|
||||
config->versionScriptGlobals.push_back(
|
||||
{s, /*IsExternCpp*/ false, /*HasWildcard*/ false});
|
||||
config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(
|
||||
{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
|
||||
// symbols private. Note that -export-dynamic takes precedence over them
|
||||
// as it says all symbols should be exported.
|
||||
if (!hasExportDynamic) {
|
||||
if (!config->exportDynamic) {
|
||||
for (auto *arg : args.filtered(OPT_dynamic_list))
|
||||
if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
|
||||
readDynamicList(*buffer);
|
||||
|
||||
for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
|
||||
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
|
||||
@ -1659,12 +1693,6 @@ template <class ELFT> static uint32_t getAndFeatures() {
|
||||
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,
|
||||
// all linker scripts have already been parsed.
|
||||
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
|
||||
// object file to the link.
|
||||
if (!bitcodeFiles.empty())
|
||||
for (const char *s : libcallRoutineNames)
|
||||
for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
|
||||
handleLibcall(s);
|
||||
|
||||
// Return if there were name resolution errors.
|
||||
@ -1880,20 +1908,54 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
|
||||
"feature detected");
|
||||
}
|
||||
|
||||
// This adds a .comment section containing a version string. We have to add it
|
||||
// before mergeSections because the .comment section is a mergeable section.
|
||||
// This adds a .comment section containing a version string.
|
||||
if (!config->relocatable)
|
||||
inputSections.push_back(createCommentSection());
|
||||
|
||||
// Replace common symbols with regular symbols.
|
||||
replaceCommonSymbols();
|
||||
|
||||
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
|
||||
// and identical code folding.
|
||||
// Split SHF_MERGE and .eh_frame sections into pieces in preparation for garbage collection.
|
||||
splitSections<ELFT>();
|
||||
|
||||
// Garbage collection and removal of shared symbols from unused shared objects.
|
||||
markLive<ELFT>();
|
||||
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) {
|
||||
findKeepUniqueSections<ELFT>(args);
|
||||
doIcf<ELFT>();
|
||||
@ -1910,3 +1972,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
|
||||
// Write the result to the file.
|
||||
writeResult<ELFT>();
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -30,8 +30,8 @@ using namespace llvm;
|
||||
using namespace llvm::sys;
|
||||
using namespace llvm::opt;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
// Create OptTable
|
||||
|
||||
@ -59,15 +59,15 @@ static void handleColorDiagnostics(opt::InputArgList &args) {
|
||||
if (!arg)
|
||||
return;
|
||||
if (arg->getOption().getID() == OPT_color_diagnostics) {
|
||||
errorHandler().colorDiagnostics = true;
|
||||
enableColors(true);
|
||||
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
|
||||
errorHandler().colorDiagnostics = false;
|
||||
enableColors(false);
|
||||
} else {
|
||||
StringRef s = arg->getValue();
|
||||
if (s == "always")
|
||||
errorHandler().colorDiagnostics = true;
|
||||
enableColors(true);
|
||||
else if (s == "never")
|
||||
errorHandler().colorDiagnostics = false;
|
||||
enableColors(false);
|
||||
else if (s != "auto")
|
||||
error("unknown option: --color-diagnostics=" + s);
|
||||
}
|
||||
@ -143,7 +143,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) {
|
||||
return args;
|
||||
}
|
||||
|
||||
void elf::printHelp() {
|
||||
void printHelp() {
|
||||
ELFOptTable().PrintHelp(
|
||||
outs(), (config->progName + " [options] file...").str().c_str(), "lld",
|
||||
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
|
||||
// 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;
|
||||
raw_svector_ostream os(data);
|
||||
os << "--chroot .\n";
|
||||
@ -216,7 +216,7 @@ static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
|
||||
return None;
|
||||
}
|
||||
|
||||
Optional<std::string> elf::findFromSearchPaths(StringRef path) {
|
||||
Optional<std::string> findFromSearchPaths(StringRef path) {
|
||||
for (StringRef dir : config->searchPaths)
|
||||
if (Optional<std::string> s = findFile(dir, path))
|
||||
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
|
||||
// search paths.
|
||||
Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
|
||||
Optional<std::string> searchLibraryBaseName(StringRef name) {
|
||||
for (StringRef dir : config->searchPaths) {
|
||||
if (!config->isStatic)
|
||||
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>.
|
||||
Optional<std::string> elf::searchLibrary(StringRef name) {
|
||||
if (name.startswith(":"))
|
||||
return findFromSearchPaths(name.substr(1));
|
||||
return searchLibraryBaseName (name);
|
||||
Optional<std::string> searchLibrary(StringRef name) {
|
||||
if (name.startswith(":"))
|
||||
return findFromSearchPaths(name.substr(1));
|
||||
return searchLibraryBaseName(name);
|
||||
}
|
||||
|
||||
// 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
|
||||
// '-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))
|
||||
return name.str();
|
||||
return findFromSearchPaths(name);
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -30,9 +30,8 @@ using namespace llvm::ELF;
|
||||
using namespace llvm::dwarf;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
namespace {
|
||||
class EhReader {
|
||||
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();
|
||||
}
|
||||
|
||||
@ -149,7 +148,7 @@ void EhReader::skipAugP() {
|
||||
d = d.slice(size);
|
||||
}
|
||||
|
||||
uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
|
||||
uint8_t getFdeEncoding(EhSectionPiece *p) {
|
||||
return EhReader(p->sec, p->data()).getFdeEncoding();
|
||||
}
|
||||
|
||||
@ -195,3 +194,6 @@ uint8_t EhReader::getFdeEncoding() {
|
||||
}
|
||||
return DW_EH_PE_absptr;
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -74,6 +74,8 @@
|
||||
|
||||
#include "ICF.h"
|
||||
#include "Config.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "OutputSections.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
@ -86,12 +88,12 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
namespace {
|
||||
template <class ELFT> class ICF {
|
||||
public:
|
||||
@ -304,10 +306,8 @@ bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
|
||||
return false;
|
||||
|
||||
// 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
|
||||
// script. We probably need to move output section assignment before ICF to
|
||||
// get the correct behaviour here.
|
||||
if (getOutputSectionName(a) != getOutputSectionName(b))
|
||||
assert(a->getParent() && b->getParent());
|
||||
if (a->getParent() != b->getParent())
|
||||
return false;
|
||||
|
||||
if (a->areRelocsRela)
|
||||
@ -446,10 +446,11 @@ static void print(const Twine &s) {
|
||||
// The main function of ICF.
|
||||
template <class ELFT> void ICF<ELFT>::run() {
|
||||
// Collect sections to merge.
|
||||
for (InputSectionBase *sec : inputSections)
|
||||
if (auto *s = dyn_cast<InputSection>(sec))
|
||||
if (isEligible(s))
|
||||
sections.push_back(s);
|
||||
for (InputSectionBase *sec : inputSections) {
|
||||
auto *s = cast<InputSection>(sec);
|
||||
if (isEligible(s))
|
||||
sections.push_back(s);
|
||||
}
|
||||
|
||||
// Initially, we use hash values to partition sections.
|
||||
parallelForEach(sections, [&](InputSection *s) {
|
||||
@ -499,12 +500,24 @@ template <class ELFT> void ICF<ELFT>::run() {
|
||||
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.
|
||||
template <class ELFT> void elf::doIcf() { ICF<ELFT>().run(); }
|
||||
template <class ELFT> void doIcf() { ICF<ELFT>().run(); }
|
||||
|
||||
template void elf::doIcf<ELF32LE>();
|
||||
template void elf::doIcf<ELF32BE>();
|
||||
template void elf::doIcf<ELF64LE>();
|
||||
template void elf::doIcf<ELF64BE>();
|
||||
template void doIcf<ELF32LE>();
|
||||
template void doIcf<ELF32BE>();
|
||||
template void doIcf<ELF64LE>();
|
||||
template void doIcf<ELF64BE>();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "lld/Common/Memory.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/CodeGen/Analysis.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
@ -37,18 +36,31 @@ using namespace llvm::sys;
|
||||
using namespace llvm::sys::fs;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
namespace lld {
|
||||
// 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;
|
||||
uint32_t InputFile::nextGroupId;
|
||||
std::vector<BinaryFile *> elf::binaryFiles;
|
||||
std::vector<BitcodeFile *> elf::bitcodeFiles;
|
||||
std::vector<LazyObjFile *> elf::lazyObjFiles;
|
||||
std::vector<InputFile *> elf::objectFiles;
|
||||
std::vector<SharedFile *> elf::sharedFiles;
|
||||
std::vector<BinaryFile *> binaryFiles;
|
||||
std::vector<BitcodeFile *> bitcodeFiles;
|
||||
std::vector<LazyObjFile *> lazyObjFiles;
|
||||
std::vector<InputFile *> objectFiles;
|
||||
std::vector<SharedFile *> sharedFiles;
|
||||
|
||||
std::unique_ptr<TarWriter> elf::tar;
|
||||
std::unique_ptr<TarWriter> tar;
|
||||
|
||||
static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
|
||||
unsigned char size;
|
||||
@ -88,7 +100,7 @@ InputFile::InputFile(Kind k, MemoryBufferRef m)
|
||||
++nextGroupId;
|
||||
}
|
||||
|
||||
Optional<MemoryBufferRef> elf::readFile(StringRef path) {
|
||||
Optional<MemoryBufferRef> readFile(StringRef path) {
|
||||
// The --chroot option changes our virtual root directory.
|
||||
// This is useful when you are dealing with files created by --reproduce.
|
||||
if (!config->chroot.empty() && path.startswith("/"))
|
||||
@ -127,18 +139,18 @@ static bool isCompatible(InputFile *file) {
|
||||
|
||||
if (!config->emulation.empty()) {
|
||||
error(toString(file) + " is incompatible with " + config->emulation);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -188,7 +200,7 @@ template <class ELFT> static void doParseFile(InputFile *file) {
|
||||
}
|
||||
|
||||
// Add symbols in File to the symbol table.
|
||||
void elf::parseFile(InputFile *file) {
|
||||
void parseFile(InputFile *file) {
|
||||
switch (config->ekind) {
|
||||
case ELF32LEKind:
|
||||
doParseFile<ELF32LE>(file);
|
||||
@ -252,57 +264,8 @@ std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec,
|
||||
}
|
||||
|
||||
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
|
||||
dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
|
||||
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}});
|
||||
}
|
||||
}
|
||||
dwarf = make<DWARFCache>(std::make_unique<DWARFContext>(
|
||||
std::make_unique<LLDDwarfObj<ELFT>>(this)));
|
||||
}
|
||||
|
||||
// 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) {
|
||||
llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
|
||||
|
||||
// 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);
|
||||
return dwarf->getVariableLoc(name);
|
||||
}
|
||||
|
||||
// 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
|
||||
// section. See comments for ObjectInfo class.
|
||||
DILineInfo info;
|
||||
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;
|
||||
return dwarf->getDILineInfo(s->getOffsetInFile() + offset, sectionIndex);
|
||||
}
|
||||
|
||||
ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
|
||||
@ -484,7 +414,8 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
|
||||
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
|
||||
// sometimes makes the linker significantly faster, although the output will
|
||||
// be bigger.
|
||||
@ -516,14 +447,16 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec) {
|
||||
if (entSize == 0)
|
||||
return false;
|
||||
if (sec.sh_size % entSize)
|
||||
fatal(toString(this) +
|
||||
": SHF_MERGE section size must be a multiple of sh_entsize");
|
||||
fatal(toString(this) + ":(" + name + "): SHF_MERGE section size (" +
|
||||
Twine(sec.sh_size) + ") must be a multiple of sh_entsize (" +
|
||||
Twine(entSize) + ")");
|
||||
|
||||
uint64_t flags = sec.sh_flags;
|
||||
if (!(flags & SHF_MERGE))
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
@ -573,7 +506,7 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
|
||||
this->sectionStringTable =
|
||||
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)
|
||||
continue;
|
||||
const Elf_Shdr &sec = objSections[i];
|
||||
@ -652,25 +585,29 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
|
||||
default:
|
||||
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
|
||||
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
|
||||
if (sec.sh_flags & SHF_LINK_ORDER) {
|
||||
InputSectionBase *linkSec = nullptr;
|
||||
if (sec.sh_link < this->sections.size())
|
||||
linkSec = this->sections[sec.sh_link];
|
||||
if (!linkSec)
|
||||
fatal(toString(this) +
|
||||
": invalid sh_link index: " + Twine(sec.sh_link));
|
||||
InputSectionBase *linkSec = nullptr;
|
||||
if (sec.sh_link < this->sections.size())
|
||||
linkSec = this->sections[sec.sh_link];
|
||||
if (!linkSec)
|
||||
fatal(toString(this) + ": invalid sh_link index: " + Twine(sec.sh_link));
|
||||
|
||||
InputSection *isec = cast<InputSection>(this->sections[i]);
|
||||
linkSec->dependentSections.push_back(isec);
|
||||
if (!isa<InputSection>(linkSec))
|
||||
error("a section " + isec->name +
|
||||
" with SHF_LINK_ORDER should not refer a non-regular "
|
||||
"section: " +
|
||||
toString(linkSec));
|
||||
}
|
||||
InputSection *isec = cast<InputSection>(this->sections[i]);
|
||||
linkSec->dependentSections.push_back(isec);
|
||||
if (!isa<InputSection>(linkSec))
|
||||
error("a section " + isec->name +
|
||||
" with SHF_LINK_ORDER should not refer a non-regular section: " +
|
||||
toString(linkSec));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1030,7 +967,7 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &sec) {
|
||||
if (name == ".eh_frame" && !config->relocatable)
|
||||
return make<EhInputSection>(*this, sec, name);
|
||||
|
||||
if (shouldMerge(sec))
|
||||
if (shouldMerge(sec, name))
|
||||
return make<MergeInputSection>(*this, sec, name);
|
||||
return make<InputSection>(*this, sec, name);
|
||||
}
|
||||
@ -1094,6 +1031,7 @@ template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
|
||||
// Handle global undefined symbols.
|
||||
if (eSym.st_shndx == SHN_UNDEF) {
|
||||
this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
|
||||
this->symbols[i]->referenced = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1470,10 +1408,12 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
|
||||
|
||||
int c = objSym.getComdatIndex();
|
||||
if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) {
|
||||
Undefined New(&f, name, binding, visibility, type);
|
||||
Undefined newSym(&f, name, binding, visibility, type);
|
||||
if (canOmitFromDynSym)
|
||||
New.exportDynamic = false;
|
||||
return symtab->addSymbol(New);
|
||||
newSym.exportDynamic = false;
|
||||
Symbol *ret = symtab->addSymbol(newSym);
|
||||
ret->referenced = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (objSym.isCommon())
|
||||
@ -1481,10 +1421,10 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
|
||||
CommonSymbol{&f, name, binding, visibility, STT_OBJECT,
|
||||
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)
|
||||
New.exportDynamic = false;
|
||||
return symtab->addSymbol(New);
|
||||
newSym.exportDynamic = false;
|
||||
return symtab->addSymbol(newSym);
|
||||
}
|
||||
|
||||
template <class ELFT> void BitcodeFile::parse() {
|
||||
@ -1523,8 +1463,8 @@ void BinaryFile::parse() {
|
||||
STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
|
||||
}
|
||||
|
||||
InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName,
|
||||
uint64_t offsetInArchive) {
|
||||
InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName,
|
||||
uint64_t offsetInArchive) {
|
||||
if (isBitcode(mb))
|
||||
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 repl = config->thinLTOObjectSuffixReplace.second;
|
||||
|
||||
@ -1634,12 +1574,15 @@ template void LazyObjFile::parse<ELF32BE>();
|
||||
template void LazyObjFile::parse<ELF64LE>();
|
||||
template void LazyObjFile::parse<ELF64BE>();
|
||||
|
||||
template class elf::ObjFile<ELF32LE>;
|
||||
template class elf::ObjFile<ELF32BE>;
|
||||
template class elf::ObjFile<ELF64LE>;
|
||||
template class elf::ObjFile<ELF64BE>;
|
||||
template class ObjFile<ELF32LE>;
|
||||
template class ObjFile<ELF32BE>;
|
||||
template class ObjFile<ELF64LE>;
|
||||
template class ObjFile<ELF64BE>;
|
||||
|
||||
template void SharedFile::parse<ELF32LE>();
|
||||
template void SharedFile::parse<ELF32BE>();
|
||||
template void SharedFile::parse<ELF64LE>();
|
||||
template void SharedFile::parse<ELF64BE>();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -10,13 +10,13 @@
|
||||
#define LLD_ELF_INPUT_FILES_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "lld/Common/DWARF.h"
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "lld/Common/LLVM.h"
|
||||
#include "lld/Common/Reproduce.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
|
||||
#include "llvm/IR/Comdat.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
@ -26,22 +26,19 @@
|
||||
|
||||
namespace llvm {
|
||||
class TarWriter;
|
||||
struct DILineInfo;
|
||||
namespace lto {
|
||||
class InputFile;
|
||||
}
|
||||
} // namespace llvm
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
class InputSectionBase;
|
||||
}
|
||||
|
||||
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
|
||||
std::string toString(const elf::InputFile *f);
|
||||
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
class InputSectionBase;
|
||||
|
||||
using llvm::object::Archive;
|
||||
|
||||
@ -261,7 +258,7 @@ template <class ELFT> class ObjFile : public ELFFileBase {
|
||||
InputSectionBase *createInputSection(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.
|
||||
// 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
|
||||
// single object file, so we cache debugging information in order to
|
||||
// parse it only once for each object file we link.
|
||||
std::unique_ptr<llvm::DWARFContext> dwarf;
|
||||
std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
|
||||
struct VarLoc {
|
||||
const llvm::DWARFDebugLine::LineTable *lt;
|
||||
unsigned file;
|
||||
unsigned line;
|
||||
};
|
||||
llvm::DenseMap<StringRef, VarLoc> variableLoc;
|
||||
DWARFCache *dwarf;
|
||||
llvm::once_flag initDwarfLine;
|
||||
};
|
||||
|
||||
|
@ -37,16 +37,15 @@ using namespace llvm::support;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::sys;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
std::vector<InputSectionBase *> elf::inputSections;
|
||||
|
||||
namespace lld {
|
||||
// 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();
|
||||
}
|
||||
|
||||
namespace elf {
|
||||
std::vector<InputSectionBase *> inputSections;
|
||||
|
||||
template <class ELFT>
|
||||
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
|
||||
const typename ELFT::Shdr &hdr) {
|
||||
@ -608,26 +607,39 @@ static int64_t getTlsTpOffset(const Symbol &s) {
|
||||
if (&s == ElfSym::tlsModuleBase)
|
||||
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) {
|
||||
// Variant 1.
|
||||
case EM_ARM:
|
||||
case EM_AARCH64:
|
||||
// Variant 1. The thread pointer points to a TCB with a fixed 2-word size,
|
||||
// followed by a variable amount of alignment padding, followed by the TLS
|
||||
// segment.
|
||||
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);
|
||||
return s.getVA(0) + config->wordsize * 2 +
|
||||
((tls->p_vaddr - config->wordsize * 2) & (tls->p_align - 1));
|
||||
case EM_MIPS:
|
||||
case EM_PPC:
|
||||
case EM_PPC64:
|
||||
// The thread pointer points to a fixed offset from the start of the
|
||||
// executable's TLS segment. An offset of 0x7000 allows a signed 16-bit
|
||||
// offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the
|
||||
// program's TLS segment.
|
||||
return s.getVA(0) - 0x7000;
|
||||
// Adjusted Variant 1. TP is placed with a displacement of 0x7000, which is
|
||||
// to allow a signed 16-bit offset to reach 0x1000 of TCB/thread-library
|
||||
// data and 0xf000 of the program's TLS segment.
|
||||
return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)) - 0x7000;
|
||||
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:
|
||||
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_RELAX_TLS_GD_TO_IE:
|
||||
return sym.getGotVA() + a - p;
|
||||
case R_HEXAGON_GOT:
|
||||
return sym.getGotVA() - in.gotPlt->getVA();
|
||||
case R_MIPS_GOTREL:
|
||||
return sym.getVA(a) - in.mipsGot->getGp(file);
|
||||
case R_MIPS_GOT_GP:
|
||||
@ -1071,7 +1081,7 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *buf,
|
||||
end, f->stOther))
|
||||
continue;
|
||||
if (!getFile<ELFT>()->someNoSplitStack)
|
||||
error(lld::toString(this) + ": " + f->getName() +
|
||||
error(toString(this) + ": " + f->getName() +
|
||||
" (with -fsplit-stack) calls " + rel.sym->getName() +
|
||||
" (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<ELF64LE>();
|
||||
template void EhInputSection::split<ELF64BE>();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -54,22 +54,9 @@ class SectionBase {
|
||||
|
||||
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.
|
||||
|
||||
// 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;
|
||||
|
||||
// Set for sections that should not be folded by ICF.
|
||||
@ -108,9 +95,9 @@ class SectionBase {
|
||||
SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
|
||||
uint64_t entsize, uint64_t alignment, uint32_t type,
|
||||
uint32_t info, uint32_t link)
|
||||
: name(name), repl(this), sectionKind(sectionKind), assigned(false),
|
||||
bss(false), keepUnique(false), partition(0), alignment(alignment),
|
||||
flags(flags), entsize(entsize), type(type), link(link), info(info) {}
|
||||
: name(name), repl(this), sectionKind(sectionKind), bss(false),
|
||||
keepUnique(false), partition(0), alignment(alignment), flags(flags),
|
||||
entsize(entsize), type(type), link(link), info(info) {}
|
||||
};
|
||||
|
||||
// This corresponds to a section of an input file.
|
||||
|
@ -42,15 +42,15 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
// Creates an empty file to store a list of object files for final
|
||||
// linking of distributed ThinLTO.
|
||||
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
|
||||
std::error_code ec;
|
||||
auto ret =
|
||||
llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
|
||||
std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
|
||||
if (ec) {
|
||||
error("cannot open " + file + ": " + ec.message());
|
||||
return nullptr;
|
||||
@ -76,7 +76,9 @@ static lto::Config createConfig() {
|
||||
c.Options.FunctionSections = true;
|
||||
c.Options.DataSections = true;
|
||||
|
||||
if (config->relocatable)
|
||||
if (auto relocModel = getRelocModelFromCMModel())
|
||||
c.RelocModel = *relocModel;
|
||||
else if (config->relocatable)
|
||||
c.RelocModel = None;
|
||||
else if (config->isPic)
|
||||
c.RelocModel = Reloc::PIC_;
|
||||
@ -139,7 +141,7 @@ BitcodeCompiler::BitcodeCompiler() {
|
||||
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
|
||||
}
|
||||
|
||||
ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
|
||||
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
|
||||
config->ltoPartitions);
|
||||
|
||||
// Initialize usedStartStop.
|
||||
@ -249,8 +251,8 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
if (!bitcodeFiles.empty())
|
||||
checkError(ltoObj->run(
|
||||
[&](size_t task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(buf[task]));
|
||||
return std::make_unique<lto::NativeObjectStream>(
|
||||
std::make_unique<raw_svector_ostream>(buf[task]));
|
||||
},
|
||||
cache));
|
||||
|
||||
@ -301,3 +303,6 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
||||
ret.push_back(createObjectFile(*file));
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -43,29 +43,27 @@ using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
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) {
|
||||
if (OutputSection *os = inputSec->getOutputSection())
|
||||
return os->addr;
|
||||
error(loc + ": unable to evaluate expression: input section " +
|
||||
inputSec->name + " has no output section assigned");
|
||||
return 0;
|
||||
static uint64_t getOutputSectionVA(SectionBase *sec) {
|
||||
OutputSection *os = sec->getOutputSection();
|
||||
assert(os && "input section has no output section assigned");
|
||||
return os ? os->addr : 0;
|
||||
}
|
||||
|
||||
uint64_t ExprValue::getValue() const {
|
||||
if (sec)
|
||||
return alignTo(sec->getOffset(val) + getOutputSectionVA(sec, loc),
|
||||
return alignTo(sec->getOffset(val) + getOutputSectionVA(sec),
|
||||
alignment);
|
||||
return alignTo(val, alignment);
|
||||
}
|
||||
|
||||
uint64_t ExprValue::getSecAddr() const {
|
||||
if (sec)
|
||||
return sec->getOffset(0) + getOutputSectionVA(sec, loc);
|
||||
return sec->getOffset(0) + getOutputSectionVA(sec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -73,7 +71,7 @@ uint64_t ExprValue::getSectionOffset() const {
|
||||
// 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
|
||||
// cases where the output section is not yet known.
|
||||
if (alignment == 1 && (!sec || !sec->getOutputSection()))
|
||||
if (alignment == 1 && !sec)
|
||||
return val;
|
||||
return getValue() - getSecAddr();
|
||||
}
|
||||
@ -157,8 +155,8 @@ static bool shouldDefineSym(SymbolAssignment *cmd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This function is called from processSectionCommands,
|
||||
// while we are fixing the output section layout.
|
||||
// Called by processSymbolAssignments() to assign definitions to
|
||||
// linker-script-defined symbols.
|
||||
void LinkerScript::addSymbol(SymbolAssignment *cmd) {
|
||||
if (!shouldDefineSym(cmd))
|
||||
return;
|
||||
@ -181,12 +179,12 @@ void LinkerScript::addSymbol(SymbolAssignment *cmd) {
|
||||
// write expressions like this: `alignment = 16; . = ALIGN(., alignment)`.
|
||||
uint64_t symValue = value.sec ? 0 : value.getValue();
|
||||
|
||||
Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, symValue,
|
||||
0, sec);
|
||||
Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE,
|
||||
symValue, 0, sec);
|
||||
|
||||
Symbol *sym = symtab->insert(cmd->name);
|
||||
sym->mergeProperties(New);
|
||||
sym->replace(New);
|
||||
sym->mergeProperties(newSym);
|
||||
sym->replace(newSym);
|
||||
cmd->sym = cast<Defined>(sym);
|
||||
}
|
||||
|
||||
@ -197,19 +195,57 @@ static void declareSymbol(SymbolAssignment *cmd) {
|
||||
return;
|
||||
|
||||
uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT;
|
||||
Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0,
|
||||
nullptr);
|
||||
Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0,
|
||||
nullptr);
|
||||
|
||||
// We can't calculate final value right now.
|
||||
Symbol *sym = symtab->insert(cmd->name);
|
||||
sym->mergeProperties(New);
|
||||
sym->replace(New);
|
||||
sym->mergeProperties(newSym);
|
||||
sym->replace(newSym);
|
||||
|
||||
cmd->sym = cast<Defined>(sym);
|
||||
cmd->provide = false;
|
||||
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 *> §ionCommands) {
|
||||
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
|
||||
// the list of script commands to mix sections inserted into.
|
||||
void LinkerScript::processInsertCommands() {
|
||||
@ -305,46 +341,44 @@ bool LinkerScript::shouldKeep(InputSectionBase *s) {
|
||||
}
|
||||
|
||||
// A helper function for the SORT() command.
|
||||
static std::function<bool(InputSectionBase *, InputSectionBase *)>
|
||||
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,
|
||||
static bool matchConstraints(ArrayRef<InputSectionBase *> sections,
|
||||
ConstraintKind kind) {
|
||||
if (kind == ConstraintKind::NoConstraint)
|
||||
return true;
|
||||
|
||||
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) ||
|
||||
(!isRW && kind == ConstraintKind::ReadOnly);
|
||||
}
|
||||
|
||||
static void sortSections(MutableArrayRef<InputSection *> vec,
|
||||
static void sortSections(MutableArrayRef<InputSectionBase *> vec,
|
||||
SortSectionPolicy k) {
|
||||
if (k != SortSectionPolicy::Default && k != SortSectionPolicy::None)
|
||||
llvm::stable_sort(vec, getComparator(k));
|
||||
auto alignmentComparator = [](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;
|
||||
};
|
||||
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
|
||||
@ -358,7 +392,7 @@ static void sortSections(MutableArrayRef<InputSection *> vec,
|
||||
// --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.
|
||||
// 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) {
|
||||
if (pat.sortOuter == SortSectionPolicy::None)
|
||||
return;
|
||||
@ -371,16 +405,16 @@ static void sortInputSections(MutableArrayRef<InputSection *> vec,
|
||||
}
|
||||
|
||||
// Compute and remember which sections the InputSectionDescription matches.
|
||||
std::vector<InputSection *>
|
||||
std::vector<InputSectionBase *>
|
||||
LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
|
||||
std::vector<InputSection *> ret;
|
||||
std::vector<InputSectionBase *> ret;
|
||||
|
||||
// Collects all sections that satisfy constraints of Cmd.
|
||||
for (const SectionPattern &pat : cmd->sectionPatterns) {
|
||||
size_t sizeBefore = ret.size();
|
||||
|
||||
for (InputSectionBase *sec : inputSections) {
|
||||
if (!sec->isLive() || sec->assigned)
|
||||
if (!sec->isLive() || sec->parent)
|
||||
continue;
|
||||
|
||||
// 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.
|
||||
// We do not ignore SHT_REL[A] linker-synthesized sections here because
|
||||
// want to support scripts that do custom layout for them.
|
||||
if (auto *isec = dyn_cast<InputSection>(sec))
|
||||
if (isec->getRelocatedSection())
|
||||
continue;
|
||||
if (isa<InputSection>(sec) &&
|
||||
cast<InputSection>(sec)->getRelocatedSection())
|
||||
continue;
|
||||
|
||||
std::string filename = getFilename(sec->file);
|
||||
if (!cmd->filePat.match(filename) ||
|
||||
@ -398,88 +432,60 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
|
||||
!pat.sectionPat.match(sec->name))
|
||||
continue;
|
||||
|
||||
// It is safe to assume that Sec is an InputSection
|
||||
// because mergeable or EH input sections have already been
|
||||
// handled and eliminated.
|
||||
ret.push_back(cast<InputSection>(sec));
|
||||
sec->assigned = true;
|
||||
ret.push_back(sec);
|
||||
}
|
||||
|
||||
sortInputSections(MutableArrayRef<InputSection *>(ret).slice(sizeBefore),
|
||||
pat);
|
||||
sortInputSections(
|
||||
MutableArrayRef<InputSectionBase *>(ret).slice(sizeBefore), pat);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LinkerScript::discard(ArrayRef<InputSection *> v) {
|
||||
for (InputSection *s : v) {
|
||||
if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
|
||||
error("discarding " + s->name + " section is not allowed");
|
||||
void LinkerScript::discard(InputSectionBase *s) {
|
||||
if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
|
||||
error("discarding " + s->name + " section is not allowed");
|
||||
|
||||
// You can discard .hash and .gnu.hash sections by linker scripts. Since
|
||||
// they are synthesized sections, we need to handle them differently than
|
||||
// other regular sections.
|
||||
if (s == mainPart->gnuHashTab)
|
||||
mainPart->gnuHashTab = nullptr;
|
||||
if (s == mainPart->hashTab)
|
||||
mainPart->hashTab = nullptr;
|
||||
// You can discard .hash and .gnu.hash sections by linker scripts. Since
|
||||
// they are synthesized sections, we need to handle them differently than
|
||||
// other regular sections.
|
||||
if (s == mainPart->gnuHashTab)
|
||||
mainPart->gnuHashTab = nullptr;
|
||||
if (s == mainPart->hashTab)
|
||||
mainPart->hashTab = nullptr;
|
||||
|
||||
s->assigned = false;
|
||||
s->markDead();
|
||||
discard(s->dependentSections);
|
||||
}
|
||||
s->markDead();
|
||||
s->parent = nullptr;
|
||||
for (InputSection *ds : s->dependentSections)
|
||||
discard(ds);
|
||||
}
|
||||
|
||||
std::vector<InputSection *>
|
||||
std::vector<InputSectionBase *>
|
||||
LinkerScript::createInputSectionList(OutputSection &outCmd) {
|
||||
std::vector<InputSection *> ret;
|
||||
std::vector<InputSectionBase *> ret;
|
||||
|
||||
for (BaseCommand *base : outCmd.sectionCommands) {
|
||||
if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
|
||||
cmd->sections = computeInputSections(cmd);
|
||||
ret.insert(ret.end(), cmd->sections.begin(), cmd->sections.end());
|
||||
cmd->sectionBases = computeInputSections(cmd);
|
||||
for (InputSectionBase *s : cmd->sectionBases)
|
||||
s->parent = &outCmd;
|
||||
ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end());
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Create output sections described by SECTIONS commands.
|
||||
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;
|
||||
// Add input sections to output sections.
|
||||
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)) {
|
||||
std::vector<InputSection *> v = createInputSectionList(*sec);
|
||||
std::vector<InputSectionBase *> v = createInputSectionList(*sec);
|
||||
|
||||
// The output section name `/DISCARD/' is special.
|
||||
// Any input section assigned to it is discarded.
|
||||
if (sec->name == "/DISCARD/") {
|
||||
discard(v);
|
||||
for (InputSectionBase *s : v)
|
||||
discard(s);
|
||||
sec->sectionCommands.clear();
|
||||
continue;
|
||||
}
|
||||
@ -493,17 +499,11 @@ void LinkerScript::processSectionCommands() {
|
||||
// way to "make it as if it wasn't present" is to make it empty.
|
||||
if (!matchConstraints(v, sec->constraint)) {
|
||||
for (InputSectionBase *s : v)
|
||||
s->assigned = false;
|
||||
s->parent = nullptr;
|
||||
sec->sectionCommands.clear();
|
||||
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
|
||||
// is given, input sections are aligned to that value, whether the
|
||||
// given value is larger or smaller than the original section alignment.
|
||||
@ -513,17 +513,40 @@ void LinkerScript::processSectionCommands() {
|
||||
s->alignment = subalign;
|
||||
}
|
||||
|
||||
// Add input sections to an output section.
|
||||
for (InputSection *s : v)
|
||||
sec->addSection(s);
|
||||
// Set the partition field the same way OutputSection::recordSection()
|
||||
// does. Partitions cannot be used with the SECTIONS command, so this is
|
||||
// always 1.
|
||||
sec->partition = 1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -539,7 +562,7 @@ static OutputSection *findByName(ArrayRef<BaseCommand *> vec,
|
||||
static OutputSection *createSection(InputSectionBase *isec,
|
||||
StringRef outsecName) {
|
||||
OutputSection *sec = script->createOutputSection(outsecName, "<internal>");
|
||||
sec->addSection(cast<InputSection>(isec));
|
||||
sec->recordSection(isec);
|
||||
return sec;
|
||||
}
|
||||
|
||||
@ -568,7 +591,7 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
|
||||
OutputSection *out = sec->getRelocatedSection()->getOutputSection();
|
||||
|
||||
if (out->relocationSection) {
|
||||
out->relocationSection->addSection(sec);
|
||||
out->relocationSection->recordSection(sec);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -576,12 +599,6 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
|
||||
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
|
||||
// ----------------------------------------------------------------
|
||||
// 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) {
|
||||
if (sec->partition != isec->partition)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -642,25 +673,30 @@ void LinkerScript::addOrphanSections() {
|
||||
StringMap<TinyPtrVector<OutputSection *>> map;
|
||||
std::vector<OutputSection *> v;
|
||||
|
||||
auto add = [&](InputSectionBase *s) {
|
||||
if (!s->isLive() || s->parent)
|
||||
return;
|
||||
std::function<void(InputSectionBase *)> add;
|
||||
add = [&](InputSectionBase *s) {
|
||||
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)
|
||||
error(toString(s) + " is being placed in '" + name + "'");
|
||||
else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
|
||||
warn(toString(s) + " is being placed in '" + name + "'");
|
||||
|
||||
if (OutputSection *sec = findByName(sectionCommands, name)) {
|
||||
sec->addSection(cast<InputSection>(s));
|
||||
return;
|
||||
if (OutputSection *sec = findByName(sectionCommands, name)) {
|
||||
sec->recordSection(s);
|
||||
} else {
|
||||
if (OutputSection *os = addInputSec(map, s, name))
|
||||
v.push_back(os);
|
||||
assert(isa<MergeInputSection>(s) ||
|
||||
s->getOutputSection()->sectionIndex == UINT32_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
if (OutputSection *os = addInputSec(map, s, name))
|
||||
v.push_back(os);
|
||||
assert(s->getOutputSection()->sectionIndex == UINT32_MAX);
|
||||
if (config->relocatable)
|
||||
for (InputSectionBase *depSec : s->dependentSections)
|
||||
if (depSec->flags & SHF_LINK_ORDER)
|
||||
add(depSec);
|
||||
};
|
||||
|
||||
// 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
|
||||
// for synthetic sections because them are special.
|
||||
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 (InputSectionBase *rel = sec->getRelocatedSection())
|
||||
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)
|
||||
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);
|
||||
|
||||
if (sec->lmaExpr)
|
||||
@ -972,17 +1022,13 @@ static uint64_t computeBase(uint64_t min, bool allocateHeaders) {
|
||||
return alignDown(min, config->maxPageSize);
|
||||
}
|
||||
|
||||
// Try to find an address for the file and program headers output sections,
|
||||
// which were unconditionally added to the first PT_LOAD segment earlier.
|
||||
// When the SECTIONS command is used, try to find an address for the file and
|
||||
// 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
|
||||
// allocated section. When using a linker script, we also check if the headers
|
||||
// are covered by the output section. This allows omitting the headers by not
|
||||
// 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.
|
||||
// We check if the headers fit below the first allocated section. 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) {
|
||||
uint64_t min = std::numeric_limits<uint64_t>::max();
|
||||
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
|
||||
// sub-commands. Doing that allows us to use final VA values, so here
|
||||
// we also handle rest commands like symbol assignments and ASSERTs.
|
||||
void LinkerScript::assignAddresses() {
|
||||
dot = getInitialDot();
|
||||
// Returns a symbol that has changed its section or value, or nullptr if no
|
||||
// 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();
|
||||
errorOnMissingSection = true;
|
||||
switchTo(aether);
|
||||
|
||||
SymbolAssignmentMap oldValues = getSymbolAssignmentValues(sectionCommands);
|
||||
for (BaseCommand *base : sectionCommands) {
|
||||
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
|
||||
cmd->addr = dot;
|
||||
@ -1063,7 +1107,9 @@ void LinkerScript::assignAddresses() {
|
||||
}
|
||||
assignOffsets(cast<OutputSection>(base));
|
||||
}
|
||||
|
||||
ctx = nullptr;
|
||||
return getChangedSymbolAssignment(oldValues);
|
||||
}
|
||||
|
||||
// Creates program headers as instructed by PHDRS linker script command.
|
||||
@ -1156,3 +1202,6 @@ std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *cmd) {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -168,6 +168,12 @@ struct InputSectionDescription : BaseCommand {
|
||||
// will be associated with this InputSectionDescription.
|
||||
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;
|
||||
|
||||
// Temporary record of synthetic ThunkSection instances and the pass that
|
||||
@ -226,10 +232,10 @@ class LinkerScript final {
|
||||
void expandOutputSection(uint64_t size);
|
||||
void expandMemoryRegions(uint64_t size);
|
||||
|
||||
std::vector<InputSection *>
|
||||
std::vector<InputSectionBase *>
|
||||
computeInputSections(const InputSectionDescription *);
|
||||
|
||||
std::vector<InputSection *> createInputSectionList(OutputSection &cmd);
|
||||
std::vector<InputSectionBase *> createInputSectionList(OutputSection &cmd);
|
||||
|
||||
std::vector<size_t> getPhdrIndices(OutputSection *sec);
|
||||
|
||||
@ -259,7 +265,7 @@ class LinkerScript final {
|
||||
|
||||
bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
|
||||
uint64_t getDot() { return dot; }
|
||||
void discard(ArrayRef<InputSection *> v);
|
||||
void discard(InputSectionBase *s);
|
||||
|
||||
ExprValue getSymbolValue(StringRef name, const Twine &loc);
|
||||
|
||||
@ -271,9 +277,10 @@ class LinkerScript final {
|
||||
bool needsInterpSection();
|
||||
|
||||
bool shouldKeep(InputSectionBase *s);
|
||||
void assignAddresses();
|
||||
const Defined *assignAddresses();
|
||||
void allocateHeaders(std::vector<PhdrEntry *> &phdrs);
|
||||
void processSectionCommands();
|
||||
void processSymbolAssignments();
|
||||
void declareSymbols();
|
||||
|
||||
// Used to handle INSERT AFTER statements.
|
||||
|
@ -34,13 +34,12 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
|
||||
|
||||
static const std::string indent8 = " "; // 8 spaces
|
||||
static const std::string indent16 = " "; // 16 spaces
|
||||
static constexpr char indent8[] = " "; // 8 spaces
|
||||
static constexpr char indent16[] = " "; // 16 spaces
|
||||
|
||||
// Print out the first three columns of a line.
|
||||
static void writeHeader(raw_ostream &os, uint64_t 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())
|
||||
return;
|
||||
|
||||
// Open a map file for writing.
|
||||
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) {
|
||||
error("cannot open " + config->mapFile + ": " + ec.message());
|
||||
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
|
||||
// files.
|
||||
void elf::writeCrossReferenceTable() {
|
||||
void writeCrossReferenceTable() {
|
||||
if (!config->cref)
|
||||
return;
|
||||
|
||||
@ -259,3 +258,6 @@ void elf::writeCrossReferenceTable() {
|
||||
print("", toString(file));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -37,11 +37,11 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
namespace endian = llvm::support::endian;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
namespace {
|
||||
template <class ELFT> class MarkLive {
|
||||
public:
|
||||
@ -141,7 +141,7 @@ void MarkLive<ELFT>::scanEhFrameSection(EhInputSection &eh,
|
||||
if (firstRelI == (unsigned)-1)
|
||||
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
|
||||
// known to point to the personality function.
|
||||
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
|
||||
// loaded) and TLS symbols (because we only know how to correctly process TLS
|
||||
// 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() {
|
||||
for (InputFile *file : objectFiles)
|
||||
for (Symbol *s : file->getSymbols())
|
||||
@ -299,13 +303,21 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
|
||||
d->section->isLive())
|
||||
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();
|
||||
}
|
||||
|
||||
// Before calling this function, Live bits are off for all
|
||||
// input sections. This function make some or all of them on
|
||||
// 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 (!config->gcSections) {
|
||||
for (InputSectionBase *sec : inputSections)
|
||||
@ -367,7 +379,10 @@ template <class ELFT> void elf::markLive() {
|
||||
message("removing unused section " + toString(sec));
|
||||
}
|
||||
|
||||
template void elf::markLive<ELF32LE>();
|
||||
template void elf::markLive<ELF32BE>();
|
||||
template void elf::markLive<ELF64LE>();
|
||||
template void elf::markLive<ELF64BE>();
|
||||
template void markLive<ELF32LE>();
|
||||
template void markLive<ELF32BE>();
|
||||
template void markLive<ELF64LE>();
|
||||
template void markLive<ELF64BE>();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -171,6 +171,9 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
|
||||
def fix_cortex_a53_843419: F<"fix-cortex-a53-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
|
||||
// is not complete.
|
||||
def require_cet: F<"require-cet">;
|
||||
@ -306,7 +309,7 @@ def push_state: F<"push-state">,
|
||||
def print_map: F<"print-map">,
|
||||
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">;
|
||||
|
||||
@ -478,6 +481,7 @@ def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
|
||||
HelpText<"Perform context senstive PGO instrumentation">;
|
||||
def lto_cs_profile_file: J<"lto-cs-profile-file=">,
|
||||
HelpText<"Context sensitive profile file path">;
|
||||
def lto_obj_path_eq: J<"lto-obj-path=">;
|
||||
def lto_sample_profile: J<"lto-sample-profile=">,
|
||||
HelpText<"Sample profile file path">;
|
||||
def disable_verify: F<"disable-verify">;
|
||||
@ -495,7 +499,12 @@ def save_temps: F<"save-temps">;
|
||||
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
|
||||
HelpText<"Path to ThinLTO cached object file directory">;
|
||||
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_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: 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: F<"plugin-opt=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">,
|
||||
Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">;
|
||||
def: J<"plugin-opt=cs-profile-path=">,
|
||||
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=">,
|
||||
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 plugin_opt_thinlto_emit_imports_files: F<"plugin-opt=thinlto-emit-imports-files">;
|
||||
def plugin_opt_thinlto_index_only: F<"plugin-opt=thinlto-index-only">;
|
||||
def plugin_opt_thinlto_index_only_eq: J<"plugin-opt=thinlto-index-only=">;
|
||||
def plugin_opt_thinlto_object_suffix_replace_eq: J<"plugin-opt=thinlto-object-suffix-replace=">;
|
||||
def plugin_opt_thinlto_prefix_replace_eq: J<"plugin-opt=thinlto-prefix-replace=">;
|
||||
def: F<"plugin-opt=thinlto-emit-imports-files">,
|
||||
Alias<thinlto_emit_imports_files>,
|
||||
HelpText<"Alias for -thinlto-emit-imports-files">;
|
||||
def: F<"plugin-opt=thinlto-index-only">,
|
||||
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.
|
||||
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
|
||||
|
@ -27,9 +27,8 @@ using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
uint8_t *Out::bufferStart;
|
||||
uint8_t Out::first;
|
||||
PhdrEntry *Out::tlsPhdr;
|
||||
@ -39,7 +38,7 @@ OutputSection *Out::preinitArray;
|
||||
OutputSection *Out::initArray;
|
||||
OutputSection *Out::finiArray;
|
||||
|
||||
std::vector<OutputSection *> elf::outputSections;
|
||||
std::vector<OutputSection *> outputSections;
|
||||
|
||||
uint32_t OutputSection::getPhdrFlags() const {
|
||||
uint32_t ret = 0;
|
||||
@ -83,12 +82,32 @@ static bool canMergeToProgbits(unsigned type) {
|
||||
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 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;
|
||||
partition = isec->partition;
|
||||
type = isec->type;
|
||||
entsize = isec->entsize;
|
||||
flags = isec->flags;
|
||||
@ -110,6 +129,8 @@ void OutputSection::addSection(InputSection *isec) {
|
||||
type = SHT_PROGBITS;
|
||||
}
|
||||
}
|
||||
if (noload)
|
||||
type = SHT_NOBITS;
|
||||
|
||||
isec->parent = this;
|
||||
uint64_t andMask =
|
||||
@ -118,6 +139,8 @@ void OutputSection::addSection(InputSection *isec) {
|
||||
uint64_t andFlags = (flags & isec->flags) & andMask;
|
||||
uint64_t orFlags = (flags | isec->flags) & orMask;
|
||||
flags = andFlags | orFlags;
|
||||
if (nonAlloc)
|
||||
flags &= ~(uint64_t)SHF_ALLOC;
|
||||
|
||||
alignment = std::max(alignment, isec->alignment);
|
||||
|
||||
@ -126,15 +149,69 @@ void OutputSection::addSection(InputSection *isec) {
|
||||
// set sh_entsize to 0.
|
||||
if (entsize != isec->entsize)
|
||||
entsize = 0;
|
||||
}
|
||||
|
||||
if (!isec->assigned) {
|
||||
isec->assigned = true;
|
||||
if (sectionCommands.empty() ||
|
||||
!isa<InputSectionDescription>(sectionCommands.back()))
|
||||
sectionCommands.push_back(make<InputSectionDescription>(""));
|
||||
auto *isd = cast<InputSectionDescription>(sectionCommands.back());
|
||||
isd->sections.push_back(isec);
|
||||
// This function scans over the InputSectionBase list sectionBases to create
|
||||
// InputSectionDescription::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 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,
|
||||
@ -148,7 +225,7 @@ static void sortByOrder(MutableArrayRef<InputSection *> in,
|
||||
in[i] = v[i].second;
|
||||
}
|
||||
|
||||
uint64_t elf::getHeaderSize() {
|
||||
uint64_t getHeaderSize() {
|
||||
if (config->oFormatBinary)
|
||||
return 0;
|
||||
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,
|
||||
// return N. Otherwise, returns 65536, which is one greater than the
|
||||
// lowest priority.
|
||||
int elf::getPriority(StringRef s) {
|
||||
int getPriority(StringRef s) {
|
||||
size_t pos = s.rfind('.');
|
||||
if (pos == StringRef::npos)
|
||||
return 65536;
|
||||
@ -378,7 +455,7 @@ int elf::getPriority(StringRef s) {
|
||||
return v;
|
||||
}
|
||||
|
||||
std::vector<InputSection *> elf::getInputSections(OutputSection *os) {
|
||||
std::vector<InputSection *> getInputSections(OutputSection *os) {
|
||||
std::vector<InputSection *> ret;
|
||||
for (BaseCommand *base : os->sectionCommands)
|
||||
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<ELF64LE>();
|
||||
template void OutputSection::maybeCompress<ELF64BE>();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -71,7 +71,9 @@ class OutputSection final : public BaseCommand, public SectionBase {
|
||||
uint64_t addr = 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.
|
||||
MemoryRegion *memRegion = nullptr;
|
||||
|
@ -62,9 +62,8 @@ using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
|
||||
for (BaseCommand *base : script->sectionCommands)
|
||||
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
|
||||
// TLS variables uses GOT differently than the regular variables.
|
||||
static bool needsGot(RelExpr expr) {
|
||||
return oneof<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
|
||||
R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
|
||||
R_GOT_PC, R_GOTPLT>(expr);
|
||||
return oneof<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
|
||||
R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT>(
|
||||
expr);
|
||||
}
|
||||
|
||||
// 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,
|
||||
InputSectionBase &s, uint64_t relOff) {
|
||||
// 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_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
|
||||
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.verdefIndex = old.verdefIndex;
|
||||
sym.ppc64BranchltIndex = old.ppc64BranchltIndex;
|
||||
sym.isPreemptible = true;
|
||||
sym.exportDynamic = true;
|
||||
sym.isUsedInRegularObj = true;
|
||||
sym.used = true;
|
||||
}
|
||||
|
||||
// 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);
|
||||
BssSection *sec =
|
||||
make<BssSection>(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment);
|
||||
if (isRO)
|
||||
in.bssRelRo->getParent()->addSection(sec);
|
||||
else
|
||||
in.bss->getParent()->addSection(sec);
|
||||
OutputSection *osec = (isRO ? in.bssRelRo : in.bss)->getParent();
|
||||
|
||||
// At this point, sectionBases has been migrated to sections. Append sec to
|
||||
// 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
|
||||
// dynamic symbol for each one. This causes the copy relocation to correctly
|
||||
@ -693,8 +696,75 @@ struct UndefinedDiag {
|
||||
|
||||
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>
|
||||
static void reportUndefinedSymbol(const UndefinedDiag &undef) {
|
||||
static void reportUndefinedSymbol(const UndefinedDiag &undef,
|
||||
bool correctSpelling) {
|
||||
Symbol &sym = *undef.sym;
|
||||
|
||||
auto visibility = [&]() -> std::string {
|
||||
@ -734,6 +804,14 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef) {
|
||||
msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times")
|
||||
.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"))
|
||||
msg += "\nthe vtable symbol may be undefined because the class is missing "
|
||||
"its key function (see https://lld.llvm.org/missingkeyfunction)";
|
||||
@ -744,7 +822,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef) {
|
||||
error(msg);
|
||||
}
|
||||
|
||||
template <class ELFT> void elf::reportUndefinedSymbols() {
|
||||
template <class ELFT> void reportUndefinedSymbols() {
|
||||
// Find the first "undefined symbol" diagnostic for each diagnostic, and
|
||||
// collect all "referenced from" lines at the first diagnostic.
|
||||
DenseMap<Symbol *, UndefinedDiag *> firstRef;
|
||||
@ -757,23 +835,21 @@ template <class ELFT> void elf::reportUndefinedSymbols() {
|
||||
firstRef[undef.sym] = &undef;
|
||||
}
|
||||
|
||||
for (const UndefinedDiag &undef : undefs) {
|
||||
if (!undef.locs.empty())
|
||||
reportUndefinedSymbol<ELFT>(undef);
|
||||
}
|
||||
// Enable spell corrector for the first 2 diagnostics.
|
||||
for (auto it : enumerate(undefs))
|
||||
if (!it.value().locs.empty())
|
||||
reportUndefinedSymbol<ELFT>(it.value(), it.index() < 2);
|
||||
undefs.clear();
|
||||
}
|
||||
|
||||
// Report an undefined symbol if necessary.
|
||||
// Returns true if the undefined symbol will produce an error message.
|
||||
template <class ELFT>
|
||||
static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
|
||||
uint64_t offset) {
|
||||
if (!sym.isUndefined() || sym.isWeak())
|
||||
return false;
|
||||
|
||||
bool canBeExternal = !sym.isLocal() && sym.computeBinding() != STB_LOCAL &&
|
||||
sym.visibility == STV_DEFAULT;
|
||||
bool canBeExternal = !sym.isLocal() && sym.visibility == STV_DEFAULT;
|
||||
if (config->unresolvedSymbols == UnresolvedPolicy::Ignore && canBeExternal)
|
||||
return false;
|
||||
|
||||
@ -997,56 +1073,29 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
|
||||
}
|
||||
}
|
||||
|
||||
if (!canWrite && (config->isPic && !isRelExpr(expr))) {
|
||||
error(
|
||||
"can't create dynamic relocation " + toString(type) + " against " +
|
||||
(sym.getName().empty() ? "local symbol" : "symbol: " + toString(sym)) +
|
||||
" in readonly segment; recompile object files with -fPIC "
|
||||
"or pass '-Wl,-z,notext' to allow text relocations in the output" +
|
||||
getLocation(sec, sym, offset));
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy relocations (for STT_OBJECT) and canonical PLT (for STT_FUNC) are only
|
||||
// possible in an executable.
|
||||
//
|
||||
// Among R_ABS relocatoin types, symbolicRel has the same size as the word
|
||||
// size. Others have fewer bits and may cause runtime overflow in -pie/-shared
|
||||
// mode. Disallow them.
|
||||
if (config->shared ||
|
||||
(config->pie && expr == R_ABS && type != target->symbolicRel)) {
|
||||
errorOrWarn(
|
||||
"relocation " + toString(type) + " cannot be used against " +
|
||||
(sym.getName().empty() ? "local symbol" : "symbol " + toString(sym)) +
|
||||
"; 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);
|
||||
// When producing an executable, we can perform copy relocations (for
|
||||
// STT_OBJECT) and canonical PLT (for STT_FUNC).
|
||||
if (!config->shared) {
|
||||
if (!canDefineSymbolInExecutable(sym)) {
|
||||
errorOrWarn("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;
|
||||
}
|
||||
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
|
||||
// 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
|
||||
@ -1074,18 +1123,37 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
|
||||
// compiled without -fPIE/-fPIC and doesn't maintain ebx.
|
||||
// * If a library definition gets preempted to the executable, it will have
|
||||
// the wrong ebx value.
|
||||
if (config->pie && config->emachine == EM_386)
|
||||
errorOrWarn("symbol '" + toString(sym) +
|
||||
"' cannot be preempted; recompile with -fPIE" +
|
||||
getLocation(sec, sym, offset));
|
||||
if (!sym.isInPlt())
|
||||
addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
|
||||
if (!sym.isDefined())
|
||||
replaceWithDefined(
|
||||
sym, in.plt,
|
||||
target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
|
||||
sym.needsPltAddr = true;
|
||||
sec.relocations.push_back({expr, type, offset, addend, &sym});
|
||||
if (sym.isFunc()) {
|
||||
if (config->pie && config->emachine == EM_386)
|
||||
errorOrWarn("symbol '" + toString(sym) +
|
||||
"' cannot be preempted; recompile with -fPIE" +
|
||||
getLocation(sec, sym, offset));
|
||||
if (!sym.isInPlt())
|
||||
addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
|
||||
if (!sym.isDefined())
|
||||
replaceWithDefined(
|
||||
sym, in.plt,
|
||||
target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1093,15 +1161,6 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
|
||||
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>
|
||||
static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
|
||||
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
|
||||
// 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;
|
||||
|
||||
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
|
||||
// statically linked executable's startup code must enumerate using the
|
||||
// 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()) {
|
||||
// Create PLT and GOTPLT slots for the symbol.
|
||||
sym.isInIplt = true;
|
||||
@ -1291,17 +1344,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
|
||||
*directSym);
|
||||
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)) {
|
||||
// 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)
|
||||
scanRelocs<ELFT>(s, s.relas<ELFT>());
|
||||
else
|
||||
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) {
|
||||
// std::merge requires a strict weak ordering.
|
||||
if (a->outSecOff < b->outSecOff)
|
||||
@ -1745,11 +1772,6 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
|
||||
if (pass == 0 && target->getThunkSectionSpacing())
|
||||
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
|
||||
// ThunkSections are later inserted back into InputSectionDescriptions.
|
||||
// We separate the creation of ThunkSections from the insertion of the
|
||||
@ -1809,11 +1831,14 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
|
||||
return addressesChanged;
|
||||
}
|
||||
|
||||
template void elf::scanRelocations<ELF32LE>(InputSectionBase &);
|
||||
template void elf::scanRelocations<ELF32BE>(InputSectionBase &);
|
||||
template void elf::scanRelocations<ELF64LE>(InputSectionBase &);
|
||||
template void elf::scanRelocations<ELF64BE>(InputSectionBase &);
|
||||
template void elf::reportUndefinedSymbols<ELF32LE>();
|
||||
template void elf::reportUndefinedSymbols<ELF32BE>();
|
||||
template void elf::reportUndefinedSymbols<ELF64LE>();
|
||||
template void elf::reportUndefinedSymbols<ELF64BE>();
|
||||
template void scanRelocations<ELF32LE>(InputSectionBase &);
|
||||
template void scanRelocations<ELF32BE>(InputSectionBase &);
|
||||
template void scanRelocations<ELF64LE>(InputSectionBase &);
|
||||
template void scanRelocations<ELF64BE>(InputSectionBase &);
|
||||
template void reportUndefinedSymbols<ELF32LE>();
|
||||
template void reportUndefinedSymbols<ELF32BE>();
|
||||
template void reportUndefinedSymbols<ELF64LE>();
|
||||
template void reportUndefinedSymbols<ELF64BE>();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -82,7 +82,6 @@ enum RelExpr {
|
||||
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
|
||||
R_AARCH64_TLSDESC_PAGE,
|
||||
R_ARM_SBREL,
|
||||
R_HEXAGON_GOT,
|
||||
R_MIPS_GOTREL,
|
||||
R_MIPS_GOT_GP,
|
||||
R_MIPS_GOT_GP_PC,
|
||||
|
@ -36,9 +36,9 @@
|
||||
#include "llvm/ADT/Twine.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
// Returns a whole line containing the current token.
|
||||
StringRef ScriptLexer::getLine() {
|
||||
StringRef s = getCurrentMB().getBuffer();
|
||||
@ -298,3 +298,6 @@ MemoryBufferRef ScriptLexer::getCurrentMB() {
|
||||
return mb;
|
||||
llvm_unreachable("getCurrentMB: failed to find a token");
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -37,9 +37,9 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::ELF;
|
||||
using namespace llvm::support::endian;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
namespace {
|
||||
class ScriptParser final : ScriptLexer {
|
||||
public:
|
||||
@ -720,7 +720,7 @@ Expr ScriptParser::readAssert() {
|
||||
|
||||
return [=] {
|
||||
if (!e().getValue())
|
||||
error(msg);
|
||||
errorOrWarn(msg);
|
||||
return script->getDot();
|
||||
};
|
||||
}
|
||||
@ -1268,7 +1268,7 @@ Expr ScriptParser::readPrimary() {
|
||||
return [=] { return cmd->size; };
|
||||
}
|
||||
if (tok == "SIZEOF_HEADERS")
|
||||
return [=] { return elf::getHeaderSize(); };
|
||||
return [=] { return getHeaderSize(); };
|
||||
|
||||
// Tok is the dot.
|
||||
if (tok == ".")
|
||||
@ -1344,16 +1344,10 @@ void ScriptParser::readAnonymousDeclaration() {
|
||||
std::vector<SymbolVersion> locals;
|
||||
std::vector<SymbolVersion> globals;
|
||||
std::tie(locals, globals) = readSymbols();
|
||||
|
||||
for (SymbolVersion v : locals) {
|
||||
if (v.name == "*")
|
||||
config->defaultSymbolVersion = VER_NDX_LOCAL;
|
||||
else
|
||||
config->versionScriptLocals.push_back(v);
|
||||
}
|
||||
|
||||
for (SymbolVersion v : globals)
|
||||
config->versionScriptGlobals.push_back(v);
|
||||
for (const SymbolVersion &pat : locals)
|
||||
config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat);
|
||||
for (const SymbolVersion &pat : globals)
|
||||
config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(pat);
|
||||
|
||||
expect(";");
|
||||
}
|
||||
@ -1365,22 +1359,14 @@ void ScriptParser::readVersionDeclaration(StringRef verStr) {
|
||||
std::vector<SymbolVersion> locals;
|
||||
std::vector<SymbolVersion> globals;
|
||||
std::tie(locals, globals) = readSymbols();
|
||||
|
||||
for (SymbolVersion v : locals) {
|
||||
if (v.name == "*")
|
||||
config->defaultSymbolVersion = VER_NDX_LOCAL;
|
||||
else
|
||||
config->versionScriptLocals.push_back(v);
|
||||
}
|
||||
for (const SymbolVersion &pat : locals)
|
||||
config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat);
|
||||
|
||||
// Create a new version definition and add that to the global symbols.
|
||||
VersionDefinition ver;
|
||||
ver.name = verStr;
|
||||
ver.globals = globals;
|
||||
|
||||
// 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;
|
||||
ver.patterns = globals;
|
||||
ver.id = config->versionDefinitions.size();
|
||||
config->versionDefinitions.push_back(ver);
|
||||
|
||||
// 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};
|
||||
}
|
||||
|
||||
void elf::readLinkerScript(MemoryBufferRef mb) {
|
||||
void readLinkerScript(MemoryBufferRef mb) {
|
||||
ScriptParser(mb).readLinkerScript();
|
||||
}
|
||||
|
||||
void elf::readVersionScript(MemoryBufferRef mb) {
|
||||
void readVersionScript(MemoryBufferRef mb) {
|
||||
ScriptParser(mb).readVersionScript();
|
||||
}
|
||||
|
||||
void elf::readDynamicList(MemoryBufferRef mb) {
|
||||
ScriptParser(mb).readDynamicList();
|
||||
}
|
||||
void readDynamicList(MemoryBufferRef mb) { ScriptParser(mb).readDynamicList(); }
|
||||
|
||||
void elf::readDefsym(StringRef name, MemoryBufferRef mb) {
|
||||
void readDefsym(StringRef name, MemoryBufferRef mb) {
|
||||
ScriptParser(mb).readDefsym(name);
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -27,10 +27,9 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
SymbolTable *elf::symtab;
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
SymbolTable *symtab;
|
||||
|
||||
void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
|
||||
// Swap symbols as instructed by -wrap.
|
||||
@ -71,21 +70,26 @@ Symbol *SymbolTable::insert(StringRef name) {
|
||||
Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
||||
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->symbolKind = Symbol::PlaceholderKind;
|
||||
sym->versionId = config->defaultSymbolVersion;
|
||||
sym->versionId = VER_NDX_GLOBAL;
|
||||
sym->visibility = STV_DEFAULT;
|
||||
sym->isUsedInRegularObj = false;
|
||||
sym->exportDynamic = false;
|
||||
sym->inDynamicList = false;
|
||||
sym->canInline = true;
|
||||
sym->referenced = false;
|
||||
sym->traced = false;
|
||||
sym->scriptDefined = false;
|
||||
sym->partition = 1;
|
||||
return sym;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addSymbol(const Symbol &New) {
|
||||
Symbol *sym = symtab->insert(New.getName());
|
||||
sym->resolve(New);
|
||||
Symbol *SymbolTable::addSymbol(const Symbol &newSym) {
|
||||
Symbol *sym = symtab->insert(newSym.getName());
|
||||
sym->resolve(newSym);
|
||||
return sym;
|
||||
}
|
||||
|
||||
@ -118,10 +122,7 @@ StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
|
||||
for (Symbol *sym : symVector) {
|
||||
if (!sym->isDefined() && !sym->isCommon())
|
||||
continue;
|
||||
if (Optional<std::string> s = demangleItanium(sym->getName()))
|
||||
(*demangledSyms)[*s].push_back(sym);
|
||||
else
|
||||
(*demangledSyms)[sym->getName()].push_back(sym);
|
||||
(*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
|
||||
}
|
||||
}
|
||||
return *demangledSyms;
|
||||
@ -162,12 +163,8 @@ void SymbolTable::handleDynamicList() {
|
||||
else
|
||||
syms = findByVersion(ver);
|
||||
|
||||
for (Symbol *b : syms) {
|
||||
if (!config->shared)
|
||||
b->exportDynamic = true;
|
||||
else if (b->includeInDynsym())
|
||||
b->isPreemptible = true;
|
||||
}
|
||||
for (Symbol *sym : syms)
|
||||
sym->inDynamicList = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,7 +189,7 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
|
||||
return "VER_NDX_LOCAL";
|
||||
if (ver == 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.
|
||||
@ -203,8 +200,12 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
|
||||
if (sym->getName().contains('@'))
|
||||
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;
|
||||
}
|
||||
if (sym->versionId == versionId)
|
||||
continue;
|
||||
|
||||
@ -214,15 +215,14 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
|
||||
}
|
||||
|
||||
void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
|
||||
if (!ver.hasWildcard)
|
||||
return;
|
||||
|
||||
// Exact matching takes precendence over fuzzy matching,
|
||||
// so we set a version to a symbol only if no version has been assigned
|
||||
// to the symbol. This behavior is compatible with GNU.
|
||||
for (Symbol *b : findAllByVersion(ver))
|
||||
if (b->versionId == config->defaultSymbolVersion)
|
||||
b->versionId = versionId;
|
||||
for (Symbol *sym : findAllByVersion(ver))
|
||||
if (sym->verdefIndex == UINT32_C(-1)) {
|
||||
sym->verdefIndex = 0;
|
||||
sym->versionId = 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() {
|
||||
// First, we assign versions to exact matching symbols,
|
||||
// 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 (SymbolVersion &ver : v.globals)
|
||||
assignExactVersion(ver, v.id, v.name);
|
||||
for (SymbolVersion &pat : v.patterns)
|
||||
assignExactVersion(pat, v.id, v.name);
|
||||
|
||||
// Next, we assign versions to fuzzy matching symbols,
|
||||
// i.e. version definitions containing glob meta-characters.
|
||||
for (SymbolVersion &ver : config->versionScriptGlobals)
|
||||
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.
|
||||
// Next, assign versions to wildcards that are not "*". 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 (SymbolVersion &ver : v.globals)
|
||||
assignWildcardVersion(ver, v.id);
|
||||
for (SymbolVersion &pat : v.patterns)
|
||||
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
|
||||
// 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
|
||||
// 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
|
||||
// compute symbol versions before handling --dynamic-list.
|
||||
// VER_NDX_LOCAL or not. Compute symbol versions before handling
|
||||
// --dynamic-list.
|
||||
handleDynamicList();
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -43,7 +43,7 @@ class SymbolTable {
|
||||
|
||||
Symbol *insert(StringRef name);
|
||||
|
||||
Symbol *addSymbol(const Symbol &New);
|
||||
Symbol *addSymbol(const Symbol &newSym);
|
||||
|
||||
void scanVersionScript();
|
||||
|
||||
|
@ -23,9 +23,20 @@ using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
namespace lld {
|
||||
// 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::etext1;
|
||||
Defined *ElfSym::etext2;
|
||||
@ -42,20 +53,6 @@ Defined *ElfSym::relaIpltEnd;
|
||||
Defined *ElfSym::riscvGlobalPointer;
|
||||
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) {
|
||||
switch (sym.kind()) {
|
||||
case Symbol::DefinedKind: {
|
||||
@ -227,7 +224,7 @@ void Symbol::parseSymbolVersion() {
|
||||
if (isDefault)
|
||||
verstr = verstr.substr(1);
|
||||
|
||||
for (VersionDefinition &ver : config->versionDefinitions) {
|
||||
for (const VersionDefinition &ver : namedVersionDefs()) {
|
||||
if (ver.name != verstr)
|
||||
continue;
|
||||
|
||||
@ -276,9 +273,8 @@ MemoryBufferRef LazyArchive::getMemberBuffer() {
|
||||
uint8_t Symbol::computeBinding() const {
|
||||
if (config->relocatable)
|
||||
return binding;
|
||||
if (visibility != STV_DEFAULT && visibility != STV_PROTECTED)
|
||||
return STB_LOCAL;
|
||||
if (versionId == VER_NDX_LOCAL && isDefined() && !isPreemptible)
|
||||
if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
|
||||
versionId == VER_NDX_LOCAL)
|
||||
return STB_LOCAL;
|
||||
if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
|
||||
return STB_GLOBAL;
|
||||
@ -296,11 +292,11 @@ bool Symbol::includeInDynsym() const {
|
||||
if (isUndefWeak() && config->pie && sharedFiles.empty())
|
||||
return false;
|
||||
|
||||
return isUndefined() || isShared() || exportDynamic;
|
||||
return isUndefined() || isShared() || exportDynamic || inDynamicList;
|
||||
}
|
||||
|
||||
// Print out a log message for --trace-symbol.
|
||||
void elf::printTraceSymbol(const Symbol *sym) {
|
||||
void printTraceSymbol(const Symbol *sym) {
|
||||
std::string s;
|
||||
if (sym->isUndefined())
|
||||
s = ": reference to ";
|
||||
@ -316,7 +312,7 @@ void elf::printTraceSymbol(const Symbol *sym) {
|
||||
message(toString(sym->file) + s + sym->getName());
|
||||
}
|
||||
|
||||
void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
|
||||
void maybeWarnUnorderableSymbol(const Symbol *sym) {
|
||||
if (!config->warnSymbolOrdering)
|
||||
return;
|
||||
|
||||
@ -492,17 +488,13 @@ void Symbol::resolveUndefined(const Undefined &other) {
|
||||
if (dyn_cast_or_null<SharedFile>(other.file))
|
||||
return;
|
||||
|
||||
if (isUndefined()) {
|
||||
// The binding may "upgrade" from weak to non-weak.
|
||||
if (other.binding != STB_WEAK)
|
||||
if (isUndefined() || isShared()) {
|
||||
// The binding 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 || !referenced)
|
||||
binding = other.binding;
|
||||
} else if (auto *s = dyn_cast<SharedSymbol>(this)) {
|
||||
// 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;
|
||||
referenced = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,7 +552,7 @@ int Symbol::compare(const Symbol *other) const {
|
||||
auto *oldSym = cast<Defined>(this);
|
||||
auto *newSym = cast<Defined>(other);
|
||||
|
||||
if (other->file && isa<BitcodeFile>(other->file))
|
||||
if (dyn_cast_or_null<BitcodeFile>(other->file))
|
||||
return 0;
|
||||
|
||||
if (!oldSym->section && !newSym->section && oldSym->value == newSym->value &&
|
||||
@ -658,6 +650,9 @@ void Symbol::resolveShared(const SharedSymbol &other) {
|
||||
uint8_t bind = binding;
|
||||
replace(other);
|
||||
binding = bind;
|
||||
cast<SharedSymbol>(this)->referenced = true;
|
||||
referenced = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -21,6 +21,13 @@
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
||||
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 {
|
||||
class CommonSymbol;
|
||||
class Defined;
|
||||
@ -30,16 +37,6 @@ class LazyObject;
|
||||
class SharedSymbol;
|
||||
class Symbol;
|
||||
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().
|
||||
//
|
||||
@ -108,29 +105,43 @@ class Symbol {
|
||||
|
||||
// Symbol visibility. This is the computed minimum visibility of all
|
||||
// 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
|
||||
// output file's symbol table. This is true for all symbols except for
|
||||
// unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
|
||||
// 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
|
||||
// will appear in .dynsym. This flag is set by interposable DSO symbols in
|
||||
// executables, by most symbols in DSOs and executables built with
|
||||
// --export-dynamic, and by dynamic lists.
|
||||
uint8_t exportDynamic : 1;
|
||||
// Used by a Defined symbol with protected or default visibility, to record
|
||||
// whether it is required to be exported into .dynsym. This is set when any of
|
||||
// the following conditions hold:
|
||||
//
|
||||
// - 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
|
||||
// is overwritten after LTO, LTO shouldn't inline the symbol because it
|
||||
// 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.
|
||||
uint8_t traced : 1;
|
||||
unsigned traced : 1;
|
||||
|
||||
inline void replace(const Symbol &New);
|
||||
inline void replace(const Symbol &newSym);
|
||||
|
||||
bool includeInDynsym() const;
|
||||
uint8_t computeBinding() const;
|
||||
@ -228,36 +239,37 @@ class Symbol {
|
||||
: file(file), nameData(name.data), nameSize(name.size), binding(binding),
|
||||
type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3),
|
||||
isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind),
|
||||
exportDynamic(isExportDynamic(k, visibility)), canInline(false),
|
||||
traced(false), needsPltAddr(false), isInIplt(false), gotInIgot(false),
|
||||
isPreemptible(false), used(!config->gcSections), needsTocRestore(false),
|
||||
exportDynamic(isExportDynamic(k, visibility)), inDynamicList(false),
|
||||
canInline(false), referenced(false), traced(false), needsPltAddr(false),
|
||||
isInIplt(false), gotInIgot(false), isPreemptible(false),
|
||||
used(!config->gcSections), needsTocRestore(false),
|
||||
scriptDefined(false) {}
|
||||
|
||||
public:
|
||||
// True the symbol should point to its PLT entry.
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
uint8_t isPreemptible : 1;
|
||||
unsigned isPreemptible : 1;
|
||||
|
||||
// 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
|
||||
// PPC64 toc pointer.
|
||||
uint8_t needsTocRestore : 1;
|
||||
unsigned needsTocRestore : 1;
|
||||
|
||||
// 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.
|
||||
uint8_t partition = 1;
|
||||
@ -367,11 +379,6 @@ class SharedSymbol : public Symbol {
|
||||
uint64_t value; // st_value
|
||||
uint64_t size; // st_size
|
||||
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,
|
||||
@ -511,7 +518,7 @@ size_t Symbol::getSymbolSize() const {
|
||||
// replace() replaces "this" object with a given symbol by memcpy'ing
|
||||
// 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.
|
||||
void Symbol::replace(const Symbol &New) {
|
||||
void Symbol::replace(const Symbol &newSym) {
|
||||
using llvm::ELF::STT_TLS;
|
||||
|
||||
// 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
|
||||
// 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.
|
||||
if (symbolKind != PlaceholderKind && !isLazy() && !New.isLazy()) {
|
||||
bool tlsMismatch = (type == STT_TLS && New.type != STT_TLS) ||
|
||||
(type != STT_TLS && New.type == STT_TLS);
|
||||
if (tlsMismatch)
|
||||
error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
|
||||
toString(New.file) + "\n>>> defined in " + toString(file));
|
||||
}
|
||||
if (symbolKind != PlaceholderKind && !isLazy() && !newSym.isLazy() &&
|
||||
(type == STT_TLS) != (newSym.type == STT_TLS))
|
||||
error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
|
||||
toString(newSym.file) + "\n>>> defined in " + toString(file));
|
||||
|
||||
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;
|
||||
visibility = old.visibility;
|
||||
isUsedInRegularObj = old.isUsedInRegularObj;
|
||||
exportDynamic = old.exportDynamic;
|
||||
inDynamicList = old.inDynamicList;
|
||||
canInline = old.canInline;
|
||||
referenced = old.referenced;
|
||||
traced = old.traced;
|
||||
isPreemptible = old.isPreemptible;
|
||||
scriptDefined = old.scriptDefined;
|
||||
|
@ -45,13 +45,12 @@ using namespace llvm::ELF;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::support;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
using llvm::support::endian::read32le;
|
||||
using llvm::support::endian::write32le;
|
||||
using llvm::support::endian::write64le;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
constexpr size_t MergeNoTailSection::numShards;
|
||||
|
||||
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
|
||||
// by "readelf --string-dump .comment <file>".
|
||||
// The returned object is a mergeable string section.
|
||||
MergeInputSection *elf::createCommentSection() {
|
||||
MergeInputSection *createCommentSection() {
|
||||
return make<MergeInputSection>(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1,
|
||||
getVersion(), ".comment");
|
||||
}
|
||||
@ -138,7 +137,7 @@ MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
|
||||
flags.ases |= s->ases;
|
||||
flags.flags1 |= s->flags1;
|
||||
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)
|
||||
@ -252,19 +251,17 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
|
||||
return make<MipsReginfoSection<ELFT>>(reginfo);
|
||||
}
|
||||
|
||||
InputSection *elf::createInterpSection() {
|
||||
InputSection *createInterpSection() {
|
||||
// StringSaver guarantees that the returned string ends with '\0'.
|
||||
StringRef s = saver.save(config->dynamicLinker);
|
||||
ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
|
||||
|
||||
auto *sec = make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents,
|
||||
".interp");
|
||||
sec->markLive();
|
||||
return sec;
|
||||
return make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents,
|
||||
".interp");
|
||||
}
|
||||
|
||||
Defined *elf::addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
|
||||
uint64_t size, InputSectionBase §ion) {
|
||||
Defined *addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
|
||||
uint64_t size, InputSectionBase §ion) {
|
||||
auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type,
|
||||
value, size, §ion);
|
||||
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
|
||||
// one and associates FDEs to the CIE.
|
||||
template <class ELFT, class RelTy>
|
||||
void EhFrameSection::addSectionAux(EhInputSection *sec, ArrayRef<RelTy> rels) {
|
||||
void EhFrameSection::addRecords(EhInputSection *sec, ArrayRef<RelTy> rels) {
|
||||
offsetToCie.clear();
|
||||
for (EhSectionPiece &piece : sec->pieces) {
|
||||
// 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) {
|
||||
auto *sec = cast<EhInputSection>(c);
|
||||
template <class ELFT>
|
||||
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;
|
||||
|
||||
alignment = std::max(alignment, sec->alignment);
|
||||
@ -437,14 +443,6 @@ template <class ELFT> void EhFrameSection::addSection(InputSectionBase *c) {
|
||||
|
||||
for (auto *ds : sec->dependentSections)
|
||||
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) {
|
||||
@ -461,6 +459,28 @@ static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
|
||||
|
||||
void EhFrameSection::finalizeContents() {
|
||||
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;
|
||||
for (CieRecord *rec : cieRecords) {
|
||||
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
|
||||
// is for the version definition itself, it is the number of versioned symbols
|
||||
// plus one. Note that we don't support multiple versions yet.
|
||||
static unsigned getVerDefNum() { return config->versionDefinitions.size() + 1; }
|
||||
// Returns the number of entries in .gnu.version_d: the number of
|
||||
// non-VER_NDX_LOCAL-non-VER_NDX_GLOBAL definitions, plus 1.
|
||||
// Note that we don't support vd_cnt > 1 yet.
|
||||
static unsigned getVerDefNum() {
|
||||
return namedVersionDefs().size() + 1;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
DynamicSection<ELFT>::DynamicSection()
|
||||
@ -1218,6 +1240,25 @@ void DynamicSection<ELFT>::addSym(int32_t tag, Symbol *sym) {
|
||||
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
|
||||
// output section. When this occurs we cannot just use the OutputSection
|
||||
// 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.
|
||||
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
|
||||
elf::Partition &part = getPartition();
|
||||
Partition &part = getPartition();
|
||||
bool isMain = part.name.empty();
|
||||
|
||||
for (StringRef s : config->filterList)
|
||||
@ -1306,9 +1347,11 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
|
||||
if (OutputSection *sec = part.dynStrTab->getParent())
|
||||
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);
|
||||
addSize(part.relaDyn->sizeDynamicTag, part.relaDyn->getParent());
|
||||
entries.push_back({part.relaDyn->sizeDynamicTag, addRelaSz(part.relaDyn)});
|
||||
|
||||
bool isRela = config->isRela;
|
||||
addInt(isRela ? DT_RELAENT : DT_RELENT,
|
||||
@ -1679,6 +1722,56 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
|
||||
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 =
|
||||
config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
|
||||
|
||||
@ -1733,14 +1826,23 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
|
||||
}
|
||||
}
|
||||
|
||||
// Finally the non-relative relocations.
|
||||
llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
|
||||
return a.r_offset < b.r_offset;
|
||||
});
|
||||
if (!nonRelatives.empty()) {
|
||||
add(nonRelatives.size());
|
||||
// Grouped non-relatives.
|
||||
for (ArrayRef<Elf_Rela> g : nonRelativeGroups) {
|
||||
add(g.size());
|
||||
add(RELOCATION_GROUPED_BY_INFO_FLAG);
|
||||
add(g[0].r_info);
|
||||
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);
|
||||
for (Elf_Rela &r : nonRelatives) {
|
||||
for (Elf_Rela &r : ungroupedNonRelatives) {
|
||||
add(r.r_offset - offset);
|
||||
offset = r.r_offset;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -2452,6 +2562,10 @@ readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
|
||||
|
||||
uint32_t cuIdx = 0;
|
||||
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();
|
||||
if (!ranges) {
|
||||
error(toString(sec) + ": " + toString(ranges.takeError()));
|
||||
@ -2481,9 +2595,9 @@ readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
|
||||
template <class ELFT>
|
||||
static std::vector<GdbIndexSection::NameAttrEntry>
|
||||
readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
|
||||
const std::vector<GdbIndexSection::CuEntry> &cUs) {
|
||||
const DWARFSection &pubNames = obj.getGnuPubNamesSection();
|
||||
const DWARFSection &pubTypes = obj.getGnuPubTypesSection();
|
||||
const std::vector<GdbIndexSection::CuEntry> &cus) {
|
||||
const DWARFSection &pubNames = obj.getGnuPubnamesSection();
|
||||
const DWARFSection &pubTypes = obj.getGnuPubtypesSection();
|
||||
|
||||
std::vector<GdbIndexSection::NameAttrEntry> ret;
|
||||
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
|
||||
// cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add
|
||||
// the number of preceding compilation units later.
|
||||
uint32_t i =
|
||||
lower_bound(cUs, set.Offset,
|
||||
[](GdbIndexSection::CuEntry cu, uint32_t offset) {
|
||||
return cu.cuOffset < offset;
|
||||
}) -
|
||||
cUs.begin();
|
||||
uint32_t i = llvm::partition_point(cus,
|
||||
[&](GdbIndexSection::CuEntry cu) {
|
||||
return cu.cuOffset < set.Offset;
|
||||
}) -
|
||||
cus.begin();
|
||||
for (const DWARFDebugPubTable::Entry &ent : set.Entries)
|
||||
ret.push_back({{ent.Name, computeGdbHash(ent.Name)},
|
||||
(ent.Descriptor.toBits() << 24) | i});
|
||||
@ -2603,7 +2716,7 @@ template <class ELFT> GdbIndexSection *GdbIndexSection::create() {
|
||||
|
||||
parallelForEachN(0, sections.size(), [&](size_t i) {
|
||||
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].compilationUnits = readCuList(dwarf);
|
||||
@ -2750,7 +2863,7 @@ StringRef VersionDefinitionSection::getFileDefName() {
|
||||
|
||||
void VersionDefinitionSection::finalizeContents() {
|
||||
fileDefNameOff = getPartition().dynStrTab->addString(getFileDefName());
|
||||
for (VersionDefinition &v : config->versionDefinitions)
|
||||
for (const VersionDefinition &v : namedVersionDefs())
|
||||
verDefNameOffs.push_back(getPartition().dynStrTab->addString(v.name));
|
||||
|
||||
if (OutputSection *sec = getPartition().dynStrTab->getParent())
|
||||
@ -2784,7 +2897,7 @@ void VersionDefinitionSection::writeTo(uint8_t *buf) {
|
||||
writeOne(buf, 1, getFileDefName(), fileDefNameOff);
|
||||
|
||||
auto nameOffIt = verDefNameOffs.begin();
|
||||
for (VersionDefinition &v : config->versionDefinitions) {
|
||||
for (const VersionDefinition &v : namedVersionDefs()) {
|
||||
buf += EntrySize;
|
||||
writeOne(buf, v.id, v.name, *nameOffIt++);
|
||||
}
|
||||
@ -2826,7 +2939,7 @@ bool VersionTableSection::isNeeded() const {
|
||||
return getPartition().verDef || getPartition().verNeed->isNeeded();
|
||||
}
|
||||
|
||||
void elf::addVerneed(Symbol *ss) {
|
||||
void addVerneed(Symbol *ss) {
|
||||
auto &file = cast<SharedFile>(*ss->file);
|
||||
if (ss->verdefIndex == VER_NDX_GLOBAL) {
|
||||
ss->versionId = VER_NDX_GLOBAL;
|
||||
@ -3009,17 +3122,16 @@ void MergeNoTailSection::finalizeContents() {
|
||||
});
|
||||
}
|
||||
|
||||
static MergeSyntheticSection *createMergeSynthetic(StringRef name,
|
||||
uint32_t type,
|
||||
uint64_t flags,
|
||||
uint32_t alignment) {
|
||||
MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
|
||||
uint64_t flags,
|
||||
uint32_t alignment) {
|
||||
bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2;
|
||||
if (shouldTailMerge)
|
||||
return make<MergeTailSection>(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
|
||||
// before calling finalizeContents().
|
||||
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()
|
||||
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
|
||||
".rld_map") {}
|
||||
@ -3102,17 +3157,23 @@ static InputSection *findExidxSection(InputSection *isec) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool isValidExidxSectionDep(InputSection *isec) {
|
||||
return (isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) &&
|
||||
isec->getSize() > 0;
|
||||
}
|
||||
|
||||
bool ARMExidxSyntheticSection::addSection(InputSection *isec) {
|
||||
if (isec->type == SHT_ARM_EXIDX) {
|
||||
exidxSections.push_back(isec);
|
||||
return true;
|
||||
if (InputSection* dep = isec->getLinkOrderDep())
|
||||
if (isValidExidxSectionDep(dep)) {
|
||||
exidxSections.push_back(isec);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) &&
|
||||
isec->getSize() > 0) {
|
||||
if (isValidExidxSectionDep(isec)) {
|
||||
executableSections.push_back(isec);
|
||||
if (empty && findExidxSection(isec))
|
||||
empty = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -3182,17 +3243,14 @@ static bool isDuplicateArmExidxSec(InputSection *prev, InputSection *cur) {
|
||||
// with the highest address and any InputSections that have mergeable
|
||||
// .ARM.exidx table entries are removed from it.
|
||||
void ARMExidxSyntheticSection::finalizeContents() {
|
||||
if (script->hasSectionsCommand) {
|
||||
// The executableSections and exidxSections that we use to derive the
|
||||
// final contents of this SyntheticSection are populated before the
|
||||
// linker script assigns InputSections to OutputSections. The linker script
|
||||
// SECTIONS command may have a /DISCARD/ entry that removes executable
|
||||
// InputSections and their dependent .ARM.exidx section that we recorded
|
||||
// earlier.
|
||||
auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); };
|
||||
llvm::erase_if(executableSections, isDiscarded);
|
||||
llvm::erase_if(exidxSections, isDiscarded);
|
||||
}
|
||||
// The executableSections and exidxSections that we use to derive the final
|
||||
// contents of this SyntheticSection are populated before
|
||||
// processSectionCommands() and ICF. A /DISCARD/ entry in SECTIONS command or
|
||||
// ICF may remove executable InputSections and their dependent .ARM.exidx
|
||||
// section that we recorded earlier.
|
||||
auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); };
|
||||
llvm::erase_if(executableSections, isDiscarded);
|
||||
llvm::erase_if(exidxSections, isDiscarded);
|
||||
|
||||
// Sort the executable sections that may or may not have associated
|
||||
// .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);
|
||||
}
|
||||
|
||||
bool ARMExidxSyntheticSection::isNeeded() const {
|
||||
return llvm::find_if(exidxSections, [](InputSection *isec) {
|
||||
return isec->isLive();
|
||||
}) != exidxSections.end();
|
||||
}
|
||||
|
||||
bool ARMExidxSyntheticSection::classof(const SectionBase *d) {
|
||||
return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX;
|
||||
}
|
||||
@ -3401,23 +3465,6 @@ bool PPC64LongBranchTargetSection::isNeeded() const {
|
||||
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() {
|
||||
// MIPS non-PIC executable gets ABI version 1.
|
||||
if (config->emachine == EM_MIPS) {
|
||||
@ -3438,7 +3485,7 @@ static uint8_t getAbiVersion() {
|
||||
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
|
||||
// the header. Setting Elf header bytes to zero ensures that any unused bytes
|
||||
// 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.
|
||||
auto *hBuf = reinterpret_cast<typename ELFT::Phdr *>(buf);
|
||||
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;
|
||||
Partition *elf::mainPart;
|
||||
std::vector<Partition> partitions;
|
||||
Partition *mainPart;
|
||||
|
||||
template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
|
||||
template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
|
||||
template GdbIndexSection *GdbIndexSection::create<ELF64LE>();
|
||||
template GdbIndexSection *GdbIndexSection::create<ELF64BE>();
|
||||
|
||||
template void elf::splitSections<ELF32LE>();
|
||||
template void elf::splitSections<ELF32BE>();
|
||||
template void elf::splitSections<ELF64LE>();
|
||||
template void elf::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 splitSections<ELF32LE>();
|
||||
template void splitSections<ELF32BE>();
|
||||
template void splitSections<ELF64LE>();
|
||||
template void splitSections<ELF64BE>();
|
||||
|
||||
template void PltSection::addEntry<ELF32LE>(Symbol &Sym);
|
||||
template void PltSection::addEntry<ELF32BE>(Symbol &Sym);
|
||||
template void PltSection::addEntry<ELF64LE>(Symbol &Sym);
|
||||
template void PltSection::addEntry<ELF64BE>(Symbol &Sym);
|
||||
|
||||
template class elf::MipsAbiFlagsSection<ELF32LE>;
|
||||
template class elf::MipsAbiFlagsSection<ELF32BE>;
|
||||
template class elf::MipsAbiFlagsSection<ELF64LE>;
|
||||
template class elf::MipsAbiFlagsSection<ELF64BE>;
|
||||
template class MipsAbiFlagsSection<ELF32LE>;
|
||||
template class MipsAbiFlagsSection<ELF32BE>;
|
||||
template class MipsAbiFlagsSection<ELF64LE>;
|
||||
template class MipsAbiFlagsSection<ELF64BE>;
|
||||
|
||||
template class elf::MipsOptionsSection<ELF32LE>;
|
||||
template class elf::MipsOptionsSection<ELF32BE>;
|
||||
template class elf::MipsOptionsSection<ELF64LE>;
|
||||
template class elf::MipsOptionsSection<ELF64BE>;
|
||||
template class MipsOptionsSection<ELF32LE>;
|
||||
template class MipsOptionsSection<ELF32BE>;
|
||||
template class MipsOptionsSection<ELF64LE>;
|
||||
template class MipsOptionsSection<ELF64BE>;
|
||||
|
||||
template class elf::MipsReginfoSection<ELF32LE>;
|
||||
template class elf::MipsReginfoSection<ELF32BE>;
|
||||
template class elf::MipsReginfoSection<ELF64LE>;
|
||||
template class elf::MipsReginfoSection<ELF64BE>;
|
||||
template class MipsReginfoSection<ELF32LE>;
|
||||
template class MipsReginfoSection<ELF32BE>;
|
||||
template class MipsReginfoSection<ELF64LE>;
|
||||
template class MipsReginfoSection<ELF64BE>;
|
||||
|
||||
template class elf::DynamicSection<ELF32LE>;
|
||||
template class elf::DynamicSection<ELF32BE>;
|
||||
template class elf::DynamicSection<ELF64LE>;
|
||||
template class elf::DynamicSection<ELF64BE>;
|
||||
template class DynamicSection<ELF32LE>;
|
||||
template class DynamicSection<ELF32BE>;
|
||||
template class DynamicSection<ELF64LE>;
|
||||
template class DynamicSection<ELF64BE>;
|
||||
|
||||
template class elf::RelocationSection<ELF32LE>;
|
||||
template class elf::RelocationSection<ELF32BE>;
|
||||
template class elf::RelocationSection<ELF64LE>;
|
||||
template class elf::RelocationSection<ELF64BE>;
|
||||
template class RelocationSection<ELF32LE>;
|
||||
template class RelocationSection<ELF32BE>;
|
||||
template class RelocationSection<ELF64LE>;
|
||||
template class RelocationSection<ELF64BE>;
|
||||
|
||||
template class elf::AndroidPackedRelocationSection<ELF32LE>;
|
||||
template class elf::AndroidPackedRelocationSection<ELF32BE>;
|
||||
template class elf::AndroidPackedRelocationSection<ELF64LE>;
|
||||
template class elf::AndroidPackedRelocationSection<ELF64BE>;
|
||||
template class AndroidPackedRelocationSection<ELF32LE>;
|
||||
template class AndroidPackedRelocationSection<ELF32BE>;
|
||||
template class AndroidPackedRelocationSection<ELF64LE>;
|
||||
template class AndroidPackedRelocationSection<ELF64BE>;
|
||||
|
||||
template class elf::RelrSection<ELF32LE>;
|
||||
template class elf::RelrSection<ELF32BE>;
|
||||
template class elf::RelrSection<ELF64LE>;
|
||||
template class elf::RelrSection<ELF64BE>;
|
||||
template class RelrSection<ELF32LE>;
|
||||
template class RelrSection<ELF32BE>;
|
||||
template class RelrSection<ELF64LE>;
|
||||
template class RelrSection<ELF64BE>;
|
||||
|
||||
template class elf::SymbolTableSection<ELF32LE>;
|
||||
template class elf::SymbolTableSection<ELF32BE>;
|
||||
template class elf::SymbolTableSection<ELF64LE>;
|
||||
template class elf::SymbolTableSection<ELF64BE>;
|
||||
template class SymbolTableSection<ELF32LE>;
|
||||
template class SymbolTableSection<ELF32BE>;
|
||||
template class SymbolTableSection<ELF64LE>;
|
||||
template class SymbolTableSection<ELF64BE>;
|
||||
|
||||
template class elf::VersionNeedSection<ELF32LE>;
|
||||
template class elf::VersionNeedSection<ELF32BE>;
|
||||
template class elf::VersionNeedSection<ELF64LE>;
|
||||
template class elf::VersionNeedSection<ELF64BE>;
|
||||
template class VersionNeedSection<ELF32LE>;
|
||||
template class VersionNeedSection<ELF32BE>;
|
||||
template class VersionNeedSection<ELF64LE>;
|
||||
template class VersionNeedSection<ELF64BE>;
|
||||
|
||||
template void elf::writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part);
|
||||
template void elf::writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part);
|
||||
template void elf::writeEhdr<ELF64LE>(uint8_t *Buf, Partition &Part);
|
||||
template void elf::writeEhdr<ELF64BE>(uint8_t *Buf, Partition &Part);
|
||||
template void writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part);
|
||||
template void writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part);
|
||||
template void writeEhdr<ELF64LE>(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 elf::writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
|
||||
template void elf::writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
|
||||
template void elf::writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
|
||||
template void writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part);
|
||||
template void writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
|
||||
template void writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
|
||||
template void writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
|
||||
|
||||
template class elf::PartitionElfHeaderSection<ELF32LE>;
|
||||
template class elf::PartitionElfHeaderSection<ELF32BE>;
|
||||
template class elf::PartitionElfHeaderSection<ELF64LE>;
|
||||
template class elf::PartitionElfHeaderSection<ELF64BE>;
|
||||
template class PartitionElfHeaderSection<ELF32LE>;
|
||||
template class PartitionElfHeaderSection<ELF32BE>;
|
||||
template class PartitionElfHeaderSection<ELF64LE>;
|
||||
template class PartitionElfHeaderSection<ELF64BE>;
|
||||
|
||||
template class elf::PartitionProgramHeadersSection<ELF32LE>;
|
||||
template class elf::PartitionProgramHeadersSection<ELF32BE>;
|
||||
template class elf::PartitionProgramHeadersSection<ELF64LE>;
|
||||
template class elf::PartitionProgramHeadersSection<ELF64BE>;
|
||||
template class PartitionProgramHeadersSection<ELF32LE>;
|
||||
template class PartitionProgramHeadersSection<ELF32BE>;
|
||||
template class PartitionProgramHeadersSection<ELF64LE>;
|
||||
template class PartitionProgramHeadersSection<ELF64BE>;
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -76,7 +76,7 @@ class EhFrameSection final : public SyntheticSection {
|
||||
return SyntheticSection::classof(d) && d->name == ".eh_frame";
|
||||
}
|
||||
|
||||
template <class ELFT> void addSection(InputSectionBase *s);
|
||||
void addSection(EhInputSection *sec);
|
||||
|
||||
std::vector<EhInputSection *> sections;
|
||||
size_t numFdes = 0;
|
||||
@ -97,7 +97,9 @@ class EhFrameSection final : public SyntheticSection {
|
||||
uint64_t size = 0;
|
||||
|
||||
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>
|
||||
CieRecord *addCie(EhSectionPiece &piece, ArrayRef<RelTy> rels);
|
||||
@ -992,7 +994,7 @@ class ARMExidxSyntheticSection : public SyntheticSection {
|
||||
|
||||
size_t getSize() const override { return size; }
|
||||
void writeTo(uint8_t *buf) override;
|
||||
bool isNeeded() const override { return !empty; }
|
||||
bool isNeeded() const override;
|
||||
// Sort and remove duplicate entries.
|
||||
void finalizeContents() override;
|
||||
InputSection *getLinkOrderDep() const;
|
||||
@ -1006,9 +1008,6 @@ class ARMExidxSyntheticSection : public SyntheticSection {
|
||||
private:
|
||||
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
|
||||
// InputObjects, we store pointers to the executable sections that need
|
||||
// .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;
|
||||
};
|
||||
|
||||
// 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();
|
||||
MergeInputSection *createCommentSection();
|
||||
MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
|
||||
uint64_t flags, uint32_t alignment);
|
||||
template <class ELFT> void splitSections();
|
||||
void mergeSections();
|
||||
|
||||
template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part);
|
||||
template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part);
|
||||
@ -1171,7 +1162,6 @@ struct InStruct {
|
||||
PltSection *plt;
|
||||
PltSection *iplt;
|
||||
PPC32Got2Section *ppc32Got2;
|
||||
RISCVSdataSection *riscvSdata;
|
||||
RelocationBaseSection *relaPlt;
|
||||
RelocationBaseSection *relaIplt;
|
||||
StringTableSection *shStrTab;
|
||||
|
@ -34,19 +34,19 @@
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace llvm::ELF;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
const TargetInfo *elf::target;
|
||||
|
||||
std::string lld::toString(RelType type) {
|
||||
namespace lld {
|
||||
std::string toString(elf::RelType type) {
|
||||
StringRef s = getELFRelocationTypeName(elf::config->emachine, type);
|
||||
if (s == "Unknown")
|
||||
return ("Unknown (" + Twine(type) + ")").str();
|
||||
return s;
|
||||
}
|
||||
|
||||
TargetInfo *elf::getTarget() {
|
||||
namespace elf {
|
||||
const TargetInfo *target;
|
||||
|
||||
TargetInfo *getTarget() {
|
||||
switch (config->emachine) {
|
||||
case EM_386:
|
||||
case EM_IAMCU:
|
||||
@ -91,6 +91,9 @@ TargetInfo *elf::getTarget() {
|
||||
}
|
||||
|
||||
template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
|
||||
if (!Out::bufferStart)
|
||||
return {};
|
||||
|
||||
for (InputSectionBase *d : inputSections) {
|
||||
auto *isec = cast<InputSection>(d);
|
||||
if (!isec->getParent())
|
||||
@ -103,7 +106,7 @@ template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
|
||||
ErrorPlace getErrorPlace(const uint8_t *loc) {
|
||||
switch (config->ekind) {
|
||||
case ELF32LEKind:
|
||||
return getErrPlace<ELF32LE>(loc);
|
||||
@ -179,3 +182,6 @@ uint64_t TargetInfo::getImageBase() const {
|
||||
return *config->imageBase;
|
||||
return config->isPic ? 0 : defaultImageBase;
|
||||
}
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "Writer.h"
|
||||
#include "AArch64ErrataFix.h"
|
||||
#include "ARMErrataFix.h"
|
||||
#include "CallGraphSort.h"
|
||||
#include "Config.h"
|
||||
#include "LinkerScript.h"
|
||||
@ -35,9 +36,8 @@ using namespace llvm::object;
|
||||
using namespace llvm::support;
|
||||
using namespace llvm::support::endian;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
namespace {
|
||||
// The writer writes a SymbolTable result to a file.
|
||||
template <class ELFT> class Writer {
|
||||
@ -62,7 +62,6 @@ template <class ELFT> class Writer {
|
||||
void setReservedSymbolSections();
|
||||
|
||||
std::vector<PhdrEntry *> createPhdrs(Partition &part);
|
||||
void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrEntry);
|
||||
void addPhdrForSection(Partition &part, unsigned shType, unsigned pType,
|
||||
unsigned pFlags);
|
||||
void assignFileOffsets();
|
||||
@ -92,7 +91,7 @@ static bool isSectionPrefix(StringRef prefix, StringRef name) {
|
||||
return name.startswith(prefix) || name == prefix.drop_back();
|
||||
}
|
||||
|
||||
StringRef elf::getOutputSectionName(const InputSectionBase *s) {
|
||||
StringRef getOutputSectionName(const InputSectionBase *s) {
|
||||
if (config->relocatable)
|
||||
return s->name;
|
||||
|
||||
@ -140,10 +139,9 @@ static bool needsInterpSection() {
|
||||
script->needsInterpSection();
|
||||
}
|
||||
|
||||
template <class ELFT> void elf::writeResult() { Writer<ELFT>().run(); }
|
||||
template <class ELFT> void writeResult() { Writer<ELFT>().run(); }
|
||||
|
||||
template <class ELFT>
|
||||
void Writer<ELFT>::removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
|
||||
static void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
|
||||
llvm::erase_if(phdrs, [&](const PhdrEntry *p) {
|
||||
if (p->p_type != PT_LOAD)
|
||||
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;
|
||||
for (unsigned part = 2; part != partitions.size() + 1; ++part) {
|
||||
for (InputSectionBase *s : inputSections) {
|
||||
@ -176,7 +174,7 @@ template <class ELFT> static void copySectionsIntoPartitions() {
|
||||
newSections.end());
|
||||
}
|
||||
|
||||
template <class ELFT> static void combineEhSections() {
|
||||
void combineEhSections() {
|
||||
for (InputSectionBase *&s : inputSections) {
|
||||
// Ignore dead sections and the partition end marker (.part.end),
|
||||
// whose partition number is out of bounds.
|
||||
@ -185,7 +183,7 @@ template <class ELFT> static void combineEhSections() {
|
||||
|
||||
Partition &part = s->getPartition();
|
||||
if (auto *es = dyn_cast<EhInputSection>(s)) {
|
||||
part.ehFrame->addSection<ELFT>(es);
|
||||
part.ehFrame->addSection(es);
|
||||
s = nullptr;
|
||||
} else if (s->kind() == SectionBase::Regular && part.armExidx &&
|
||||
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 linking result. This function defines such symbols.
|
||||
void elf::addReservedSymbols() {
|
||||
void addReservedSymbols() {
|
||||
if (config->emachine == EM_MIPS) {
|
||||
// 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
|
||||
@ -310,13 +308,23 @@ static OutputSection *findSection(StringRef name, unsigned partition = 1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Initialize Out members.
|
||||
template <class ELFT> static void createSyntheticSections() {
|
||||
template <class ELFT> void createSyntheticSections() {
|
||||
// Initialize all pointers with NULL. This is needed because
|
||||
// you can call lld::elf::main more than once as a library.
|
||||
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);
|
||||
|
||||
@ -355,8 +363,10 @@ template <class ELFT> static void createSyntheticSections() {
|
||||
add(sec);
|
||||
}
|
||||
|
||||
StringRef relaDynName = config->isRela ? ".rela.dyn" : ".rel.dyn";
|
||||
|
||||
for (Partition &part : partitions) {
|
||||
auto add = [&](InputSectionBase *sec) {
|
||||
auto add = [&](SyntheticSection *sec) {
|
||||
sec->partition = part.getNumber();
|
||||
inputSections.push_back(sec);
|
||||
};
|
||||
@ -378,16 +388,11 @@ template <class ELFT> static void createSyntheticSections() {
|
||||
part.dynStrTab = make<StringTableSection>(".dynstr", true);
|
||||
part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
|
||||
part.dynamic = make<DynamicSection<ELFT>>();
|
||||
if (config->androidPackDynRelocs) {
|
||||
part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>(
|
||||
config->isRela ? ".rela.dyn" : ".rel.dyn");
|
||||
} else {
|
||||
part.relaDyn = make<RelocationSection<ELFT>>(
|
||||
config->isRela ? ".rela.dyn" : ".rel.dyn", config->zCombreloc);
|
||||
}
|
||||
|
||||
if (needsInterpSection())
|
||||
add(createInterpSection());
|
||||
if (config->androidPackDynRelocs)
|
||||
part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>(relaDynName);
|
||||
else
|
||||
part.relaDyn =
|
||||
make<RelocationSection<ELFT>>(relaDynName, config->zCombreloc);
|
||||
|
||||
if (config->hasDynSymTab) {
|
||||
part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
|
||||
@ -396,7 +401,7 @@ template <class ELFT> static void createSyntheticSections() {
|
||||
part.verSym = make<VersionTableSection>();
|
||||
add(part.verSym);
|
||||
|
||||
if (!config->versionDefinitions.empty()) {
|
||||
if (!namedVersionDefs().empty()) {
|
||||
part.verDef = make<VersionDefinitionSection>();
|
||||
add(part.verDef);
|
||||
}
|
||||
@ -476,11 +481,6 @@ template <class ELFT> static void createSyntheticSections() {
|
||||
add(in.ppc64LongBranchTarget);
|
||||
}
|
||||
|
||||
if (config->emachine == EM_RISCV) {
|
||||
in.riscvSdata = make<RISCVSdataSection>();
|
||||
add(in.riscvSdata);
|
||||
}
|
||||
|
||||
in.gotPlt = make<GotPltSection>();
|
||||
add(in.gotPlt);
|
||||
in.igotPlt = make<IgotPltSection>();
|
||||
@ -504,16 +504,14 @@ template <class ELFT> static void createSyntheticSections() {
|
||||
config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false);
|
||||
add(in.relaPlt);
|
||||
|
||||
// The relaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure
|
||||
// that the IRelative relocations are processed last by the dynamic loader.
|
||||
// We cannot place the iplt section in .rel.dyn when Android relocation
|
||||
// packing is enabled because that would cause a section type mismatch.
|
||||
// However, because the Android dynamic loader reads .rel.plt after .rel.dyn,
|
||||
// we can get the desired behaviour by placing the iplt section in .rel.plt.
|
||||
// The relaIplt immediately follows .rel[a].dyn to ensure that the IRelative
|
||||
// relocations are processed last by the dynamic loader. We cannot place the
|
||||
// iplt section in .rel.dyn when Android relocation packing is enabled because
|
||||
// that would cause a section type mismatch. However, because the Android
|
||||
// dynamic loader reads .rel.plt after .rel.dyn, we can get the desired
|
||||
// behaviour by placing the iplt section in .rel.plt.
|
||||
in.relaIplt = make<RelocationSection<ELFT>>(
|
||||
(config->emachine == EM_ARM && !config->androidPackDynRelocs)
|
||||
? ".rel.dyn"
|
||||
: in.relaPlt->name,
|
||||
config->androidPackDynRelocs ? in.relaPlt->name : relaDynName,
|
||||
/*sort=*/false);
|
||||
add(in.relaIplt);
|
||||
|
||||
@ -544,29 +542,6 @@ template <class ELFT> static void createSyntheticSections() {
|
||||
|
||||
// The main function of the writer.
|
||||
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)
|
||||
copyLocalSymbols();
|
||||
|
||||
@ -582,15 +557,14 @@ template <class ELFT> void Writer<ELFT>::run() {
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
script->assignAddresses();
|
||||
|
||||
// If -compressed-debug-sections is specified, we need to compress
|
||||
// .debug_* sections. Do it right now because it changes the size of
|
||||
// output sections.
|
||||
for (OutputSection *sec : outputSections)
|
||||
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
|
||||
// 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;
|
||||
|
||||
if (!config->oFormatBinary) {
|
||||
writeTrapInstr();
|
||||
if (config->zSeparate != SeparateSegmentKind::None)
|
||||
writeTrapInstr();
|
||||
writeHeader();
|
||||
writeSections();
|
||||
} else {
|
||||
@ -738,7 +713,7 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
|
||||
});
|
||||
if (i == sec->sectionCommands.end())
|
||||
continue;
|
||||
InputSection *isec = cast<InputSectionDescription>(*i)->sections[0];
|
||||
InputSectionBase *isec = cast<InputSectionDescription>(*i)->sections[0];
|
||||
|
||||
// Relocations are not using REL[A] section symbols.
|
||||
if (isec->type == SHT_REL || isec->type == SHT_RELA)
|
||||
@ -1070,7 +1045,7 @@ template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
|
||||
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()) {
|
||||
ElfSym::relaIpltStart->section = in.relaIplt;
|
||||
ElfSym::relaIpltEnd->section = in.relaIplt;
|
||||
@ -1298,10 +1273,7 @@ sortISDBySectionOrder(InputSectionDescription *isd,
|
||||
}
|
||||
orderedSections.push_back({isec, i->second});
|
||||
}
|
||||
llvm::sort(orderedSections, [&](std::pair<InputSection *, int> a,
|
||||
std::pair<InputSection *, int> b) {
|
||||
return a.second < b.second;
|
||||
});
|
||||
llvm::sort(orderedSections, llvm::less_second());
|
||||
|
||||
// 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
|
||||
@ -1536,6 +1508,12 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
|
||||
if (!(sec->flags & SHF_LINK_ORDER))
|
||||
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
|
||||
// but sort must consider them all at once.
|
||||
std::vector<InputSection **> scriptSections;
|
||||
@ -1545,14 +1523,16 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
|
||||
for (InputSection *&isec : isd->sections) {
|
||||
scriptSections.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
|
||||
// this processing inside the ARMExidxsyntheticsection::finalizeContents().
|
||||
if (!config->relocatable && config->emachine == EM_ARM &&
|
||||
sec->type == SHT_ARM_EXIDX)
|
||||
if (errorCount())
|
||||
continue;
|
||||
|
||||
llvm::stable_sort(sections, compareByFilePosition);
|
||||
@ -1569,21 +1549,30 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
|
||||
template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
|
||||
ThunkCreator tc;
|
||||
AArch64Err843419Patcher a64p;
|
||||
ARMErr657417Patcher a32p;
|
||||
script->assignAddresses();
|
||||
|
||||
// For some targets, like x86, this loop iterates only once.
|
||||
int assignPasses = 0;
|
||||
for (;;) {
|
||||
bool changed = false;
|
||||
bool changed = target->needsThunks && tc.createThunks(outputSections);
|
||||
|
||||
script->assignAddresses();
|
||||
|
||||
if (target->needsThunks)
|
||||
changed |= tc.createThunks(outputSections);
|
||||
// With Thunk Size much smaller than branch range we expect to
|
||||
// converge quickly; if we get to 10 something has gone wrong.
|
||||
if (changed && tc.pass >= 10) {
|
||||
error("thunk creation not converged");
|
||||
break;
|
||||
}
|
||||
|
||||
if (config->fixCortexA53Errata843419) {
|
||||
if (changed)
|
||||
script->assignAddresses();
|
||||
changed |= a64p.createFixes();
|
||||
}
|
||||
if (config->fixCortexA8) {
|
||||
if (changed)
|
||||
script->assignAddresses();
|
||||
changed |= a32p.createFixes();
|
||||
}
|
||||
|
||||
if (in.mipsGot)
|
||||
in.mipsGot->updateAllocSize();
|
||||
@ -1594,8 +1583,19 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
|
||||
changed |= part.relrDyn->updateAllocSize();
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
return;
|
||||
const Defined *changedSym = script->assignAddresses();
|
||||
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())
|
||||
return true;
|
||||
|
||||
// If we have a dynamic list it specifies which local symbols are preemptible.
|
||||
if (config->hasDynamicList)
|
||||
return false;
|
||||
|
||||
if (!config->shared)
|
||||
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.
|
||||
if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc()))
|
||||
return false;
|
||||
@ -1696,12 +1696,16 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
|
||||
// Define __rel[a]_iplt_{start,end} symbols if needed.
|
||||
addRelIpltSymbols();
|
||||
|
||||
// RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800 if not defined.
|
||||
// This symbol should only be defined in an executable.
|
||||
if (config->emachine == EM_RISCV && !config->shared)
|
||||
// RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800. This symbol
|
||||
// should only be defined in an executable. If .sdata does not exist, its
|
||||
// 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 =
|
||||
addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800,
|
||||
STV_DEFAULT, STB_GLOBAL);
|
||||
addOptionalRegular("__global_pointer$", sec ? sec : Out::elfHeader,
|
||||
0x800, STV_DEFAULT, STB_GLOBAL);
|
||||
}
|
||||
|
||||
if (config->emachine == EM_X86_64) {
|
||||
// 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)
|
||||
finalizeSynthetic(part.ehFrame);
|
||||
|
||||
symtab->forEachSymbol([](Symbol *s) {
|
||||
if (!s->isPreemptible)
|
||||
s->isPreemptible = computeIsPreemptible(*s);
|
||||
});
|
||||
symtab->forEachSymbol(
|
||||
[](Symbol *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
|
||||
// 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) {
|
||||
forEachRelSec(scanRelocations<ELFT>);
|
||||
reportUndefinedSymbols<ELFT>();
|
||||
}
|
||||
|
||||
addIRelativeRelocs();
|
||||
|
||||
if (in.plt && in.plt->isNeeded())
|
||||
in.plt->addSymbols();
|
||||
if (in.iplt && in.iplt->isNeeded())
|
||||
@ -1880,7 +1886,6 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
|
||||
finalizeSynthetic(in.plt);
|
||||
finalizeSynthetic(in.iplt);
|
||||
finalizeSynthetic(in.ppc32Got2);
|
||||
finalizeSynthetic(in.riscvSdata);
|
||||
finalizeSynthetic(in.partIndex);
|
||||
|
||||
// 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
|
||||
// known but before addresses are allocated.
|
||||
resolveShfLinkOrder();
|
||||
if (errorCount())
|
||||
return;
|
||||
|
||||
// This is used to:
|
||||
// 1) Create "thunks":
|
||||
@ -2049,27 +2056,32 @@ std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs(Partition &part) {
|
||||
unsigned partNo = part.getNumber();
|
||||
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.
|
||||
uint64_t flags = computeFlags(PF_R);
|
||||
PhdrEntry *load = nullptr;
|
||||
|
||||
// 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);
|
||||
// nmagic or omagic output does not have PT_PHDR, PT_INTERP, or the readonly
|
||||
// PT_LOAD.
|
||||
if (!config->nmagic && !config->omagic) {
|
||||
// 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 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
|
||||
@ -2208,21 +2220,68 @@ void Writer<ELFT>::addPhdrForSection(Partition &part, unsigned shType,
|
||||
part.phdrs.push_back(entry);
|
||||
}
|
||||
|
||||
// The first section of each PT_LOAD, the first section in PT_GNU_RELRO and the
|
||||
// first section after PT_GNU_RELRO have to be page aligned so that the dynamic
|
||||
// linker can set the permissions.
|
||||
// Place the first section of each PT_LOAD to a different page (of maxPageSize).
|
||||
// This is achieved by assigning an alignment expression to addrExpr of each
|
||||
// such section.
|
||||
template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
|
||||
auto pageAlign = [](OutputSection *cmd) {
|
||||
if (cmd && !cmd->addrExpr)
|
||||
cmd->addrExpr = [=] {
|
||||
return alignTo(script->getDot(), config->maxPageSize);
|
||||
};
|
||||
const PhdrEntry *prev;
|
||||
auto pageAlign = [&](const PhdrEntry *p) {
|
||||
OutputSection *cmd = p->firstSec;
|
||||
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) {
|
||||
prev = nullptr;
|
||||
for (const PhdrEntry *p : part.phdrs)
|
||||
if (p->p_type == PT_LOAD && p->firstSec)
|
||||
pageAlign(p->firstSec);
|
||||
if (p->p_type == PT_LOAD && p->firstSec) {
|
||||
pageAlign(p);
|
||||
prev = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2231,12 +2290,9 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
|
||||
// load executables without any address adjustment.
|
||||
static uint64_t computeFileOffset(OutputSection *os, uint64_t off) {
|
||||
// The first section in a PT_LOAD has to have congruent offset and address
|
||||
// module the page size.
|
||||
if (os->ptLoad && os->ptLoad->firstSec == os) {
|
||||
uint64_t alignment =
|
||||
std::max<uint64_t>(os->ptLoad->p_align, config->maxPageSize);
|
||||
return alignTo(off, alignment, os->addr);
|
||||
}
|
||||
// modulo the maximum page size.
|
||||
if (os->ptLoad && os->ptLoad->firstSec == os)
|
||||
return alignTo(off, os->ptLoad->p_align, os->addr);
|
||||
|
||||
// File offsets are not significant for .bss sections other than the first one
|
||||
// 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) {
|
||||
off = setFileOffset(sec, off);
|
||||
if (script->hasSectionsCommand)
|
||||
continue;
|
||||
|
||||
// If this is a last section of the last executable segment and that
|
||||
// segment is the last loadable segment, align the offset of the
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -2348,14 +2403,13 @@ template <class ELFT> void Writer<ELFT>::setPhdrs(Partition &part) {
|
||||
p->p_paddr = first->getLMA();
|
||||
}
|
||||
|
||||
if (p->p_type == PT_LOAD) {
|
||||
p->p_align = std::max<uint64_t>(p->p_align, config->maxPageSize);
|
||||
} else if (p->p_type == PT_GNU_RELRO) {
|
||||
if (p->p_type == PT_GNU_RELRO) {
|
||||
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
|
||||
// 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
|
||||
// overwritten by output sections.
|
||||
template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
|
||||
if (script->hasSectionsCommand)
|
||||
return;
|
||||
|
||||
for (Partition &part : partitions) {
|
||||
// Fill the last page.
|
||||
for (PhdrEntry *p : part.phdrs)
|
||||
@ -2685,7 +2736,15 @@ template <class ELFT> void Writer<ELFT>::writeBuildId() {
|
||||
part.buildId->writeBuildId(buildId);
|
||||
}
|
||||
|
||||
template void elf::writeResult<ELF32LE>();
|
||||
template void elf::writeResult<ELF32BE>();
|
||||
template void elf::writeResult<ELF64LE>();
|
||||
template void elf::writeResult<ELF64BE>();
|
||||
template void createSyntheticSections<ELF32LE>();
|
||||
template void createSyntheticSections<ELF32BE>();
|
||||
template void createSyntheticSections<ELF64LE>();
|
||||
template void createSyntheticSections<ELF64BE>();
|
||||
|
||||
template void writeResult<ELF32LE>();
|
||||
template void writeResult<ELF32BE>();
|
||||
template void writeResult<ELF64LE>();
|
||||
template void writeResult<ELF64BE>();
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
@ -9,6 +9,7 @@
|
||||
#ifndef LLD_ELF_WRITER_H
|
||||
#define LLD_ELF_WRITER_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cstdint>
|
||||
@ -19,13 +20,18 @@ namespace elf {
|
||||
class InputFile;
|
||||
class OutputSection;
|
||||
class InputSectionBase;
|
||||
void copySectionsIntoPartitions();
|
||||
template <class ELFT> void createSyntheticSections();
|
||||
void combineEhSections();
|
||||
template <class ELFT> void writeResult();
|
||||
|
||||
// This describes a program header entry.
|
||||
// Each contains type, access flags and range of output sections that will be
|
||||
// placed in it.
|
||||
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);
|
||||
|
||||
uint64_t p_paddr = 0;
|
||||
|
@ -1,19 +1,22 @@
|
||||
=======================
|
||||
lld 9.0.0 Release Notes
|
||||
=======================
|
||||
========================
|
||||
lld 10.0.0 Release Notes
|
||||
========================
|
||||
|
||||
.. contents::
|
||||
: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
|
||||
============
|
||||
|
||||
lld is a high-performance linker that supports ELF (Unix), COFF
|
||||
(Windows), Mach-O (macOS), MinGW and WebAssembly. lld is
|
||||
command-line-compatible with GNU linkers and Microsoft link.exe and is
|
||||
significantly faster than the system default linkers.
|
||||
|
||||
lld 9 has lots of feature improvements and bug fixes.
|
||||
This document contains the release notes for the lld linker, release 10.0.0.
|
||||
Here we describe the status of lld, including major improvements
|
||||
from the previous release. All lld releases may be downloaded
|
||||
from the `LLVM releases web site <https://llvm.org/releases/>`_.
|
||||
|
||||
Non-comprehensive list of changes in this release
|
||||
=================================================
|
||||
@ -21,211 +24,31 @@ Non-comprehensive list of changes in this release
|
||||
ELF Improvements
|
||||
----------------
|
||||
|
||||
* ld.lld now has typo suggestions for flags:
|
||||
``$ ld.lld --call-shared`` now prints
|
||||
``unknown argument '--call-shared', did you mean '--call_shared'``.
|
||||
(`r361518 <https://reviews.llvm.org/rL361518>`_)
|
||||
|
||||
* ``--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>`_)
|
||||
|
||||
* Glob pattern, which you can use in linker scripts or version scripts,
|
||||
now supports `\` and `[!...]`. Except character classes
|
||||
(e.g. `[[:digit:]]`), lld's glob pattern should be fully compatible
|
||||
with GNU now. (`r375051
|
||||
<https://github.com/llvm/llvm-project/commit/48993d5ab9413f0e5b94dfa292a233ce55b09e3e>`_)
|
||||
|
||||
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
|
||||
------------------
|
||||
|
||||
* 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
|
||||
with libgcc.
|
||||
MachO Improvements
|
||||
------------------
|
||||
|
||||
* PDB output can be requested without manually specifying the PDB file
|
||||
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.)
|
||||
* Item 1.
|
||||
|
||||
* ``--no-insert-timestamp`` option is added as an alias to ``/timestamp:0``.
|
||||
(`r353145 <https://reviews.llvm.org/rL353145>`_)
|
||||
WebAssembly Improvements
|
||||
------------------------
|
||||
|
||||
* Many more GNU ld options are now supported, which e.g. allows the lld
|
||||
MinGW frontend to be called by GCC.
|
||||
|
||||
* The following options are added: ``--exclude-all-symbols``,
|
||||
``--appcontainer``, ``--undefined``
|
||||
* `__data_end` and `__heap_base` are no longer exported by default,
|
||||
as it's best to keep them internal when possible. They can be
|
||||
explicitly exported with `--export=__data_end` and
|
||||
`--export=__heap_base`, respectively.
|
||||
|
@ -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.
|
||||
|
||||
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.
|
||||
|
||||
Imports and Exports
|
||||
|
@ -48,9 +48,9 @@
|
||||
# built documents.
|
||||
#
|
||||
# The short version.
|
||||
version = '9'
|
||||
version = '10'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '9'
|
||||
release = '10'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -14,7 +14,6 @@
|
||||
.Nm ld.lld
|
||||
.Op Ar options
|
||||
.Ar objfile ...
|
||||
|
||||
.Sh DESCRIPTION
|
||||
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
|
||||
@ -40,7 +39,6 @@ All these targets are always supported however
|
||||
was built, so you can always use
|
||||
.Nm
|
||||
as a native linker as well as a cross linker.
|
||||
|
||||
.Sh OPTIONS
|
||||
Many options have both a single-letter and long form.
|
||||
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
|
||||
Report unresolved symbols as errors.
|
||||
.It Fl -execute-only
|
||||
Mark executable sections unreadable. This option is currently only
|
||||
supported on AArch64.
|
||||
Mark executable sections unreadable.
|
||||
This option is currently only supported on AArch64.
|
||||
.It Fl -exclude-libs Ns = Ns Ar value
|
||||
Exclude static libraries from automatic export.
|
||||
.It Fl -export-dynamic , Fl E
|
||||
@ -232,13 +230,16 @@ Enable safe identical code folding.
|
||||
Disable identical code folding.
|
||||
.It Fl -ignore-data-address-equality
|
||||
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
|
||||
that happen to have the same value.
|
||||
.It Fl -ignore-function-address-equality
|
||||
Ignore address equality of functions. This option allows non-PIC calls to a
|
||||
function with non-default visibility in a shared object. The function may have
|
||||
different addresses within the executable and within the shared object.
|
||||
Ignore address equality of functions.
|
||||
This option allows non-PIC calls to a function with non-default visibility in
|
||||
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
|
||||
Set the base address to
|
||||
.Ar value .
|
||||
@ -344,7 +345,8 @@ is
|
||||
.Cm binary ,
|
||||
which produces output with no ELF header.
|
||||
.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
|
||||
Write optimization remarks in YAML format to
|
||||
.Ar file .
|
||||
@ -354,8 +356,8 @@ Filter optimization remarks by only allowing the passes matching
|
||||
.It Fl -opt-remarks-with-hotness
|
||||
Include hotness information in the optimization remarks file.
|
||||
.It Fl -orphan-handling Ns = Ns Ar mode
|
||||
Control how orphan sections are handled. An orphan section is one not
|
||||
specifically mentioned in a linker script.
|
||||
Control how orphan sections are handled.
|
||||
An orphan section is one not specifically mentioned in a linker script.
|
||||
.Ar mode
|
||||
may be:
|
||||
.Pp
|
||||
@ -381,17 +383,21 @@ may be:
|
||||
.Pp
|
||||
.Bl -tag -width 2n -compact
|
||||
.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
|
||||
Pack dynamic relocations in SHT_ANDROID_REL(A).
|
||||
.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
|
||||
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
|
||||
.Pp
|
||||
.Cm none
|
||||
is the default. If
|
||||
is the default.
|
||||
If
|
||||
.Fl -use-android-relr-tags
|
||||
is specified, use SHT_ANDROID_RELR instead of SHT_RELR.
|
||||
.Pp
|
||||
@ -418,8 +424,14 @@ Undo the effect of
|
||||
.Fl -push-state.
|
||||
.It Fl -relocatable , Fl r
|
||||
Create relocatable object file.
|
||||
.It Fl -reproduce Ns = Ns Ar value
|
||||
Dump linker invocation and input files for debugging.
|
||||
.It Fl -reproduce Ns = Ns Ar path
|
||||
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
|
||||
Retain only the symbols listed in the file.
|
||||
.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
|
||||
Read linker script from
|
||||
.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
|
||||
Set address of section.
|
||||
.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
|
||||
Synonym for
|
||||
.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 *
|
||||
matches zero or more characters,
|
||||
.Cm ?
|
||||
matches any single character, and
|
||||
.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
|
||||
.Fl -undefined .
|
||||
.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.
|
||||
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
|
||||
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
|
||||
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
|
||||
an older glibc version. Otherwise, there is no need to use it, as the default
|
||||
value does not give a 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.
|
||||
an older glibc version.
|
||||
Otherwise, there is no need to use it, as the default value does not give a
|
||||
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
|
||||
Report unresolved symbols as warnings.
|
||||
.It Fl -whole-archive
|
||||
@ -571,18 +591,21 @@ Force load of all members in a static library.
|
||||
Use wrapper functions for symbol.
|
||||
.It Fl z Ar option
|
||||
Linker option extensions.
|
||||
.Bl -tag -width indent
|
||||
.Bl -tag -width indent -compact
|
||||
.Pp
|
||||
.It Cm execstack
|
||||
Make the main stack executable.
|
||||
Stack permissions are recorded in the
|
||||
.Dv PT_GNU_STACK
|
||||
segment.
|
||||
.Pp
|
||||
.It Cm global
|
||||
Sets the
|
||||
.Dv DF_1_GLOBAL flag in the
|
||||
.Dv DYNAMIC
|
||||
section.
|
||||
Different loaders can decide how to handle this flag on their own.
|
||||
.Pp
|
||||
.It Cm ifunc-noplt
|
||||
Do not emit PLT entries for ifunc symbols.
|
||||
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
|
||||
.Fl z Li notext
|
||||
option.
|
||||
.Pp
|
||||
.It Cm initfirst
|
||||
Sets the
|
||||
.Dv DF_1_INITFIRST
|
||||
flag to indicate the module should be initialized first.
|
||||
.Pp
|
||||
.It Cm interpose
|
||||
Set the
|
||||
.Dv DF_1_INTERPOSE
|
||||
flag to indicate to the runtime linker that the object is an interposer.
|
||||
During symbol resolution interposers are searched after the application
|
||||
but before other dependencies.
|
||||
.Pp
|
||||
.It Cm muldefs
|
||||
Do not error if a symbol is defined multiple times.
|
||||
The first definition will be used.
|
||||
This is a synonym for
|
||||
.Fl -allow-multiple-definition.
|
||||
.Pp
|
||||
.It Cm nocombreloc
|
||||
Disable combining and sorting multiple relocation sections.
|
||||
.Pp
|
||||
.It Cm nocopyreloc
|
||||
Disable the creation of copy relocations.
|
||||
.Pp
|
||||
.It Cm nodefaultlib
|
||||
Set the
|
||||
.Dv DF_1_NODEFLIB
|
||||
flag to indicate that default library search paths should be ignored.
|
||||
.Pp
|
||||
.It Cm nodelete
|
||||
Set the
|
||||
.Dv DF_1_NODELETE
|
||||
flag to indicate that the object cannot be unloaded from a process.
|
||||
.Pp
|
||||
.It Cm nodlopen
|
||||
Set the
|
||||
.Dv DF_1_NOOPEN
|
||||
flag to indicate that the object may not be opened by
|
||||
.Xr dlopen 3 .
|
||||
.Pp
|
||||
.It Cm norelro
|
||||
Do not indicate that portions of the object shold be mapped read-only
|
||||
after initial relocation processing.
|
||||
The object will omit the
|
||||
.Dv PT_GNU_RELRO
|
||||
segment.
|
||||
.Pp
|
||||
.It Cm notext
|
||||
Allow relocations against read-only segments.
|
||||
Sets the
|
||||
.Dv DT_TEXTREL flag in the
|
||||
.Dv DYNAMIC
|
||||
section.
|
||||
.Pp
|
||||
.It Cm now
|
||||
Set the
|
||||
.Dv DF_BIND_NOW
|
||||
flag to indicate that the run-time loader should perform all relocation
|
||||
processing as part of object initialization.
|
||||
By default relocations may be performed on demand.
|
||||
.Pp
|
||||
.It Cm origin
|
||||
Set the
|
||||
.Dv DF_ORIGIN
|
||||
flag to indicate that the object requires
|
||||
$ORIGIN
|
||||
processing.
|
||||
.Pp
|
||||
.It Cm retpolineplt
|
||||
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
|
||||
.Pp
|
||||
.It Cm rodynamic
|
||||
Make the
|
||||
.Li .dynamic
|
||||
@ -656,6 +693,18 @@ section read-only.
|
||||
The
|
||||
.Dv DT_DEBUG
|
||||
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
|
||||
Set the main thread's stack size to
|
||||
.Ar size .
|
||||
@ -663,9 +712,11 @@ The stack size is recorded as the size of the
|
||||
.Ar size .
|
||||
.Dv PT_GNU_STACK
|
||||
program segment.
|
||||
.Pp
|
||||
.It Cm text
|
||||
Do not allow relocations against read-only segments.
|
||||
This is the default.
|
||||
.Pp
|
||||
.It Cm wxneeded
|
||||
Create a
|
||||
.Dv PT_OPENBSD_WXNEEDED
|
||||
|
47
contrib/llvm-project/lld/include/lld/Common/DWARF.h
Normal file
47
contrib/llvm-project/lld/include/lld/Common/DWARF.h
Normal 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
|
@ -87,7 +87,6 @@ class ErrorHandler {
|
||||
StringRef errorLimitExceededMsg = "too many errors emitted, stopping now";
|
||||
StringRef logName = "lld";
|
||||
llvm::raw_ostream *errorOS = &llvm::errs();
|
||||
bool colorDiagnostics = llvm::errs().has_colors();
|
||||
bool exitEarly = true;
|
||||
bool fatalWarnings = false;
|
||||
bool verbose = false;
|
||||
@ -102,12 +101,16 @@ class ErrorHandler {
|
||||
std::unique_ptr<llvm::FileOutputBuffer> outputBuffer;
|
||||
|
||||
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.
|
||||
ErrorHandler &errorHandler();
|
||||
|
||||
void enableColors(bool enable);
|
||||
|
||||
inline void error(const Twine &msg) { errorHandler().error(msg); }
|
||||
inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg) {
|
||||
errorHandler().fatal(msg);
|
||||
|
@ -17,6 +17,7 @@
|
||||
// This should be the only #include, force #includes of all the others on
|
||||
// clients.
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include <utility>
|
||||
|
||||
|
@ -18,9 +18,8 @@
|
||||
|
||||
namespace lld {
|
||||
// Returns a demangled C++ symbol name. If Name is not a mangled
|
||||
// name, it returns Optional::None.
|
||||
llvm::Optional<std::string> demangleItanium(llvm::StringRef name);
|
||||
llvm::Optional<std::string> demangleMSVC(llvm::StringRef s);
|
||||
// name, it returns name.
|
||||
std::string demangleItanium(llvm::StringRef name);
|
||||
|
||||
std::vector<uint8_t> parseHex(llvm::StringRef s);
|
||||
bool isValidCIdentifier(llvm::StringRef s);
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
namespace lld {
|
||||
llvm::TargetOptions initTargetOptionsFromCodeGenFlags();
|
||||
llvm::Optional<llvm::Reloc::Model> getRelocModelFromCMModel();
|
||||
llvm::Optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
|
||||
std::string getCPUStr();
|
||||
std::vector<std::string> getMAttrs();
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
@ -101,7 +101,7 @@ class MachOLinkingContext : public LinkingContext {
|
||||
auto file = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
auto *filePtr = file.get();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ class DarwinLdOptTable : public llvm::opt::OptTable {
|
||||
static std::vector<std::unique_ptr<File>>
|
||||
makeErrorFile(StringRef path, std::error_code ec) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -160,7 +160,7 @@ static void addFile(StringRef path, MachOLinkingContext &ctx,
|
||||
std::vector<std::unique_ptr<File>> files =
|
||||
loadFile(ctx, path, loadWholeArchive, upwardDylib);
|
||||
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.
|
||||
@ -1138,7 +1138,7 @@ static void createFiles(MachOLinkingContext &ctx, bool Implicit) {
|
||||
ctx.createInternalFiles(Files);
|
||||
for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) {
|
||||
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)";
|
||||
errorHandler().errorOS = &Error;
|
||||
errorHandler().exitEarly = CanExitEarly;
|
||||
errorHandler().colorDiagnostics = Error.has_colors();
|
||||
enableColors(Error.has_colors());
|
||||
|
||||
MachOLinkingContext ctx;
|
||||
if (!parse(args, ctx))
|
||||
@ -1185,7 +1185,7 @@ bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
|
||||
merged = mergedFile.get();
|
||||
auto &members = ctx.getNodes();
|
||||
members.insert(members.begin(),
|
||||
llvm::make_unique<FileNode>(std::move(mergedFile)));
|
||||
std::make_unique<FileNode>(std::move(mergedFile)));
|
||||
}
|
||||
resolveTask.end();
|
||||
|
||||
|
@ -210,7 +210,7 @@ class ArchiveReader : public Reader {
|
||||
const Registry ®) const override {
|
||||
StringRef path = mb->getBufferIdentifier();
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,20 @@ class ArchHandler_x86_64 : public ArchHandler {
|
||||
FindAddressForAtom addressForAtom,
|
||||
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:
|
||||
static const Registry::KindStrings _sKindStrings[];
|
||||
static const StubInfo _sStubInfo;
|
||||
@ -188,6 +202,9 @@ class ArchHandler_x86_64 : public ArchHandler {
|
||||
enum X86_64Kind: Reference::KindValue {
|
||||
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:
|
||||
branch32, /// ex: call _foo
|
||||
ripRel32, /// ex: movq _foo(%rip), %rax
|
||||
@ -242,24 +259,34 @@ class ArchHandler_x86_64 : public ArchHandler {
|
||||
};
|
||||
|
||||
const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
|
||||
LLD_KIND_STRING_ENTRY(invalid), 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(invalid),
|
||||
LLD_KIND_STRING_ENTRY(modeCode),
|
||||
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(ripRel32Minus1Anon),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon),
|
||||
LLD_KIND_STRING_ENTRY(ripRel32GotLoad),
|
||||
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(lazyImmediateLocation),
|
||||
LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon),
|
||||
LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64),
|
||||
LLD_KIND_STRING_ENTRY(delta32Anon), LLD_KIND_STRING_ENTRY(delta64Anon),
|
||||
LLD_KIND_STRING_ENTRY(pointer64),
|
||||
LLD_KIND_STRING_ENTRY(pointer64Anon),
|
||||
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(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(unwindInfoToEhFrame),
|
||||
LLD_KIND_STRING_ENTRY(tlvInitSectionOffset),
|
||||
@ -601,6 +628,8 @@ void ArchHandler_x86_64::applyFixupFinal(
|
||||
case negDelta32:
|
||||
*loc32 = fixupAddress - targetAddress + ref.addend();
|
||||
return;
|
||||
case modeCode:
|
||||
case modeData:
|
||||
case lazyPointer:
|
||||
// Do nothing
|
||||
return;
|
||||
@ -720,6 +749,8 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,
|
||||
case unwindInfoToEhFrame:
|
||||
llvm_unreachable("fixup implies __unwind_info");
|
||||
return;
|
||||
case modeCode:
|
||||
case modeData:
|
||||
case unwindFDEToFunction:
|
||||
// Do nothing for now
|
||||
return;
|
||||
@ -743,6 +774,9 @@ void ArchHandler_x86_64::appendSectionRelocations(
|
||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
|
||||
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
||||
case modeCode:
|
||||
case modeData:
|
||||
return;
|
||||
case branch32:
|
||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||
X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4);
|
||||
|
@ -573,7 +573,7 @@ class CompactUnwindPass : public Pass {
|
||||
|
||||
void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
assert(ctx.needsCompactUnwindPass());
|
||||
pm.add(llvm::make_unique<CompactUnwindPass>(ctx));
|
||||
pm.add(std::make_unique<CompactUnwindPass>(ctx));
|
||||
}
|
||||
|
||||
} // end namesapce mach_o
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "lld/Core/Atom.h"
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/Support/Allocator.h"
|
||||
#include "llvm/Support/Format.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
|
@ -176,7 +176,7 @@ class GOTPass : public Pass {
|
||||
|
||||
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||
assert(ctx.needsGOTPass());
|
||||
pm.add(llvm::make_unique<GOTPass>(ctx));
|
||||
pm.add(std::make_unique<GOTPass>(ctx));
|
||||
}
|
||||
|
||||
} // end namesapce mach_o
|
||||
|
@ -478,7 +478,7 @@ llvm::Error LayoutPass::perform(SimpleFile &mergedFile) {
|
||||
}
|
||||
|
||||
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,
|
||||
bool & leftBeforeRight) ->bool {
|
||||
return ctx.customAtomOrderer(left, right, leftBeforeRight);
|
||||
|
@ -802,9 +802,9 @@ void MachOLinkingContext::addSectCreateSection(
|
||||
std::unique_ptr<MemoryBuffer> content) {
|
||||
|
||||
if (!_sectCreateFile) {
|
||||
auto sectCreateFile = llvm::make_unique<mach_o::SectCreateFile>();
|
||||
auto sectCreateFile = std::make_unique<mach_o::SectCreateFile>();
|
||||
_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.");
|
||||
@ -897,8 +897,8 @@ static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
|
||||
|
||||
std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
|
||||
std::error_code ec;
|
||||
_dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(new
|
||||
llvm::raw_fd_ostream(path, ec, llvm::sys::fs::F_None));
|
||||
_dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(
|
||||
new llvm::raw_fd_ostream(path, ec, llvm::sys::fs::OF_None));
|
||||
if (ec) {
|
||||
_dependencyInfo.reset();
|
||||
return ec;
|
||||
@ -1019,7 +1019,7 @@ void MachOLinkingContext::finalizeInputFiles() {
|
||||
return !isLibrary(a) && isLibrary(b);
|
||||
});
|
||||
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) {
|
||||
|
@ -542,7 +542,7 @@ class MachOObjectReader : public Reader {
|
||||
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
||||
const Registry ®istry) const override {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -568,7 +568,7 @@ class MachODylibReader : public Reader {
|
||||
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
||||
const Registry ®istry) const override {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -626,17 +626,19 @@ llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) {
|
||||
+ _file.sections.size() * sizeof(typename T::section);
|
||||
uint8_t *next = lc + seg->cmdsize;
|
||||
memset(seg->segname, 0, 16);
|
||||
seg->flags = 0;
|
||||
seg->vmaddr = 0;
|
||||
seg->vmsize = _file.sections.back().address
|
||||
+ _file.sections.back().content.size();
|
||||
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->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
||||
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)
|
||||
swapStruct(*seg);
|
||||
typename T::section *sout = reinterpret_cast<typename T::section*>
|
||||
|
@ -717,7 +717,7 @@ llvm::Error parseStabs(MachOFile &file,
|
||||
// FIXME: Kill this off when we can move to sane yaml parsing.
|
||||
std::unique_ptr<BumpPtrAllocator> allocator;
|
||||
if (copyRefs)
|
||||
allocator = llvm::make_unique<BumpPtrAllocator>();
|
||||
allocator = std::make_unique<BumpPtrAllocator>();
|
||||
|
||||
enum { start, inBeginEnd } state = start;
|
||||
|
||||
@ -812,7 +812,7 @@ llvm::Error parseStabs(MachOFile &file,
|
||||
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.
|
||||
file.debugInfo()->setAllocator(std::move(allocator));
|
||||
@ -832,10 +832,10 @@ dataExtractorFromSection(const NormalizedFile &normalizedFile,
|
||||
|
||||
// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
|
||||
// inspection" code if possible.
|
||||
static uint32_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
|
||||
static uint64_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
|
||||
uint64_t abbrCode) {
|
||||
uint64_t curCode;
|
||||
uint32_t offset = 0;
|
||||
uint64_t offset = 0;
|
||||
while ((curCode = abbrevData.getULEB128(&offset)) != abbrCode) {
|
||||
// Tag
|
||||
abbrevData.getULEB128(&offset);
|
||||
@ -853,13 +853,13 @@ static uint32_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
|
||||
static Expected<const char *>
|
||||
getIndexedString(const NormalizedFile &normalizedFile,
|
||||
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)
|
||||
return infoData.getCStr(&infoOffset);
|
||||
if (form != llvm::dwarf::DW_FORM_strp)
|
||||
return llvm::make_error<GenericError>(
|
||||
"string field encoded without DW_FORM_strp");
|
||||
uint32_t stringOffset = infoData.getU32(&infoOffset);
|
||||
uint64_t stringOffset = infoData.getU32(&infoOffset);
|
||||
llvm::DataExtractor stringsData =
|
||||
dataExtractorFromSection(normalizedFile, stringsSection);
|
||||
return stringsData.getCStr(&stringOffset);
|
||||
@ -875,7 +875,7 @@ readCompUnit(const NormalizedFile &normalizedFile,
|
||||
StringRef path) {
|
||||
// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
|
||||
// inspection" code if possible.
|
||||
uint32_t offset = 0;
|
||||
uint64_t offset = 0;
|
||||
llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32;
|
||||
auto infoData = dataExtractorFromSection(normalizedFile, info);
|
||||
uint32_t length = infoData.getU32(&offset);
|
||||
@ -897,7 +897,7 @@ readCompUnit(const NormalizedFile &normalizedFile,
|
||||
|
||||
uint32_t abbrCode = infoData.getULEB128(&offset);
|
||||
auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev);
|
||||
uint32_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode);
|
||||
uint64_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode);
|
||||
uint64_t tag = abbrevData.getULEB128(&abbrevOffset);
|
||||
if (tag != llvm::dwarf::DW_TAG_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.
|
||||
std::unique_ptr<BumpPtrAllocator> allocator;
|
||||
if (copyRefs) {
|
||||
allocator = llvm::make_unique<BumpPtrAllocator>();
|
||||
allocator = std::make_unique<BumpPtrAllocator>();
|
||||
tuOrErr->name = copyDebugString(tuOrErr->name, *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)
|
||||
file.debugInfo()->setAllocator(std::move(allocator));
|
||||
} else
|
||||
|
@ -124,7 +124,7 @@ class ObjCPass : public Pass {
|
||||
|
||||
|
||||
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
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user