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

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

View File

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

View File

@ -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;

View File

@ -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>]

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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;

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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());
}
}

View File

@ -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.

View File

@ -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));

View File

@ -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>

View File

@ -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

View File

@ -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

View File

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

View File

@ -29,16 +29,14 @@ using namespace lld;
// but outs() or errs() are not thread-safe. We protect them using a mutex.
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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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

View File

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

View File

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

View File

@ -17,13 +17,14 @@
using namespace llvm;
using namespace llvm::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

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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 &target;
}
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

View File

@ -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

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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 &target;
}
} // namespace elf
} // namespace lld

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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

View File

@ -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.

View 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

View File

@ -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 *> &sectionCommands) {
SymbolAssignmentMap ret;
for (BaseCommand *base : sectionCommands) {
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
if (cmd->sym) // sym is nullptr for dot.
ret.try_emplace(cmd->sym,
std::make_pair(cmd->sym->section, cmd->sym->value));
continue;
}
for (BaseCommand *sub_base : cast<OutputSection>(base)->sectionCommands)
if (auto *cmd = dyn_cast<SymbolAssignment>(sub_base))
if (cmd->sym)
ret.try_emplace(cmd->sym,
std::make_pair(cmd->sym->section, cmd->sym->value));
}
return ret;
}
// Returns the lexicographical smallest (for determinism) Defined whose
// section/value has changed.
static const Defined *
getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) {
const Defined *changed = nullptr;
for (auto &it : oldValues) {
const Defined *sym = it.first;
if (std::make_pair(sym->section, sym->value) != it.second &&
(!changed || sym->getName() < changed->getName()))
changed = sym;
}
return changed;
}
// This method is used to handle INSERT AFTER statement. Here we rebuild
// 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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -171,6 +171,9 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

@ -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

View File

@ -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;

View File

@ -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 &section) {
Defined *addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
uint64_t size, InputSectionBase &section) {
auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type,
value, size, &section);
if (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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -109,7 +109,7 @@ trap at runtime (functions that contain only an ``unreachable`` instruction)
and use these stub functions at the otherwise invalid call sites.
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

View File

@ -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.

View File

@ -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

View File

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

View File

@ -87,7 +87,6 @@ class ErrorHandler {
StringRef errorLimitExceededMsg = "too many errors emitted, stopping now";
StringRef 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);

View File

@ -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>

View File

@ -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);

View File

@ -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();

View File

@ -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>

View File

@ -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;
}

View File

@ -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();

View File

@ -210,7 +210,7 @@ class ArchiveReader : public Reader {
const Registry &reg) 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);
}

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -542,7 +542,7 @@ class MachOObjectReader : public Reader {
loadFile(std::unique_ptr<MemoryBuffer> mb,
const Registry &registry) 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 &registry) 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);
}

View File

@ -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*>

View File

@ -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

View File

@ -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