Merge ^/vendor/lld/dist up to its last change, and resolve conflicts.
This commit is contained in:
commit
85868e8a1d
@ -28,8 +28,10 @@ add_lld_library(lldCOFF
|
|||||||
BinaryFormat
|
BinaryFormat
|
||||||
Core
|
Core
|
||||||
DebugInfoCodeView
|
DebugInfoCodeView
|
||||||
|
DebugInfoDWARF
|
||||||
DebugInfoMSF
|
DebugInfoMSF
|
||||||
DebugInfoPDB
|
DebugInfoPDB
|
||||||
|
Demangle
|
||||||
LibDriver
|
LibDriver
|
||||||
LTO
|
LTO
|
||||||
MC
|
MC
|
||||||
|
@ -122,6 +122,7 @@ struct Configuration {
|
|||||||
bool dll = false;
|
bool dll = false;
|
||||||
StringRef implib;
|
StringRef implib;
|
||||||
std::vector<Export> exports;
|
std::vector<Export> exports;
|
||||||
|
bool hadExplicitExports;
|
||||||
std::set<std::string> delayLoads;
|
std::set<std::string> delayLoads;
|
||||||
std::map<std::string, int> dllOrder;
|
std::map<std::string, int> dllOrder;
|
||||||
Symbol *delayLoadHelper = nullptr;
|
Symbol *delayLoadHelper = nullptr;
|
||||||
@ -189,6 +190,9 @@ struct Configuration {
|
|||||||
// Used for /thinlto-object-suffix-replace:
|
// Used for /thinlto-object-suffix-replace:
|
||||||
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
|
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
|
||||||
|
|
||||||
|
// Used for /lto-obj-path:
|
||||||
|
llvm::StringRef ltoObjPath;
|
||||||
|
|
||||||
uint64_t align = 4096;
|
uint64_t align = 4096;
|
||||||
uint64_t imageBase = -1;
|
uint64_t imageBase = -1;
|
||||||
uint64_t fileAlign = 512;
|
uint64_t fileAlign = 512;
|
||||||
|
@ -135,7 +135,7 @@ class NullChunk : public NonSectionChunk {
|
|||||||
static std::vector<std::vector<DefinedImportData *>>
|
static std::vector<std::vector<DefinedImportData *>>
|
||||||
binImports(const std::vector<DefinedImportData *> &imports) {
|
binImports(const std::vector<DefinedImportData *> &imports) {
|
||||||
// Group DLL-imported symbols by DLL name because that's how
|
// Group DLL-imported symbols by DLL name because that's how
|
||||||
// symbols are layed out in the import descriptor table.
|
// symbols are laid out in the import descriptor table.
|
||||||
auto less = [](const std::string &a, const std::string &b) {
|
auto less = [](const std::string &a, const std::string &b) {
|
||||||
return config->dllOrder[a] < config->dllOrder[b];
|
return config->dllOrder[a] < config->dllOrder[b];
|
||||||
};
|
};
|
||||||
@ -188,7 +188,7 @@ class DelayDirectoryChunk : public NonSectionChunk {
|
|||||||
|
|
||||||
// Initial contents for delay-loaded functions.
|
// Initial contents for delay-loaded functions.
|
||||||
// This code calls __delayLoadHelper2 function to resolve a symbol
|
// This code calls __delayLoadHelper2 function to resolve a symbol
|
||||||
// and then overwrites its jump table slot with the result
|
// which then overwrites its jump table slot with the result
|
||||||
// for subsequent function calls.
|
// for subsequent function calls.
|
||||||
static const uint8_t thunkX64[] = {
|
static const uint8_t thunkX64[] = {
|
||||||
0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]
|
0x48, 0x8D, 0x05, 0, 0, 0, 0, // lea rax, [__imp_<FUNCNAME>]
|
||||||
|
@ -17,11 +17,12 @@
|
|||||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
|
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::coff;
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::codeview;
|
using namespace llvm::codeview;
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace coff {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// The TypeServerSource class represents a PDB type server, a file referenced by
|
// The TypeServerSource class represents a PDB type server, a file referenced by
|
||||||
// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
|
// OBJ files compiled with MSVC /Zi. A single PDB can be shared by several OBJ
|
||||||
@ -96,27 +97,25 @@ TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {
|
|||||||
GC.push_back(std::unique_ptr<TpiSource>(this));
|
GC.push_back(std::unique_ptr<TpiSource>(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makeTpiSource(const ObjFile *f) {
|
TpiSource *makeTpiSource(const ObjFile *f) {
|
||||||
return new TpiSource(TpiSource::Regular, f);
|
return new TpiSource(TpiSource::Regular, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makeUseTypeServerSource(const ObjFile *f,
|
TpiSource *makeUseTypeServerSource(const ObjFile *f,
|
||||||
const TypeServer2Record *ts) {
|
const TypeServer2Record *ts) {
|
||||||
TypeServerSource::enqueue(f, *ts);
|
TypeServerSource::enqueue(f, *ts);
|
||||||
return new UseTypeServerSource(f, ts);
|
return new UseTypeServerSource(f, ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makePrecompSource(const ObjFile *f) {
|
TpiSource *makePrecompSource(const ObjFile *f) {
|
||||||
return new PrecompSource(f);
|
return new PrecompSource(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TpiSource *lld::coff::makeUsePrecompSource(const ObjFile *f,
|
TpiSource *makeUsePrecompSource(const ObjFile *f,
|
||||||
const PrecompRecord *precomp) {
|
const PrecompRecord *precomp) {
|
||||||
return new UsePrecompSource(f, precomp);
|
return new UsePrecompSource(f, precomp);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace lld {
|
|
||||||
namespace coff {
|
|
||||||
template <>
|
template <>
|
||||||
const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
|
const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
|
||||||
assert(source->kind == TpiSource::UsingPCH);
|
assert(source->kind == TpiSource::UsingPCH);
|
||||||
@ -128,8 +127,6 @@ const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
|
|||||||
assert(source->kind == TpiSource::UsingPDB);
|
assert(source->kind == TpiSource::UsingPDB);
|
||||||
return ((const UseTypeServerSource *)source)->typeServerDependency;
|
return ((const UseTypeServerSource *)source)->typeServerDependency;
|
||||||
}
|
}
|
||||||
} // namespace coff
|
|
||||||
} // namespace lld
|
|
||||||
|
|
||||||
std::map<std::string, std::pair<std::string, TypeServerSource *>>
|
std::map<std::string, std::pair<std::string, TypeServerSource *>>
|
||||||
TypeServerSource::instances;
|
TypeServerSource::instances;
|
||||||
@ -210,8 +207,7 @@ TypeServerSource::findFromFile(const ObjFile *dependentFile) {
|
|||||||
|
|
||||||
// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
|
// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
|
||||||
// moved here.
|
// moved here.
|
||||||
Expected<llvm::pdb::NativeSession *>
|
Expected<llvm::pdb::NativeSession *> findTypeServerSource(const ObjFile *f) {
|
||||||
lld::coff::findTypeServerSource(const ObjFile *f) {
|
|
||||||
Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
|
Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
|
||||||
if (!ts)
|
if (!ts)
|
||||||
return ts.takeError();
|
return ts.takeError();
|
||||||
@ -231,7 +227,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
|
|||||||
if (!it.second)
|
if (!it.second)
|
||||||
return; // another OBJ already scheduled this PDB for load
|
return; // another OBJ already scheduled this PDB for load
|
||||||
|
|
||||||
driver->enqueuePath(*p, false);
|
driver->enqueuePath(*p, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an instance of TypeServerSource or an error string if the PDB couldn't
|
// Create an instance of TypeServerSource or an error string if the PDB couldn't
|
||||||
@ -239,7 +235,7 @@ void TypeServerSource::enqueue(const ObjFile *dependentFile,
|
|||||||
// will be merged in. NOTE - a PDB load failure is not a link error: some
|
// will be merged in. NOTE - a PDB load failure is not a link error: some
|
||||||
// debug info will simply be missing from the final PDB - that is the default
|
// debug info will simply be missing from the final PDB - that is the default
|
||||||
// accepted behavior.
|
// accepted behavior.
|
||||||
void lld::coff::loadTypeServerSource(llvm::MemoryBufferRef m) {
|
void loadTypeServerSource(llvm::MemoryBufferRef m) {
|
||||||
std::string path = normalizePdbPath(m.getBufferIdentifier());
|
std::string path = normalizePdbPath(m.getBufferIdentifier());
|
||||||
|
|
||||||
Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
|
Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
|
||||||
@ -266,3 +262,6 @@ Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
|
|||||||
return info.takeError();
|
return info.takeError();
|
||||||
return new TypeServerSource(m, session.release());
|
return new TypeServerSource(m, session.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace coff
|
||||||
|
} // namespace lld
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "llvm/ADT/Optional.h"
|
#include "llvm/ADT/Optional.h"
|
||||||
#include "llvm/ADT/StringSwitch.h"
|
#include "llvm/ADT/StringSwitch.h"
|
||||||
#include "llvm/BinaryFormat/Magic.h"
|
#include "llvm/BinaryFormat/Magic.h"
|
||||||
|
#include "llvm/LTO/LTO.h"
|
||||||
#include "llvm/Object/ArchiveWriter.h"
|
#include "llvm/Object/ArchiveWriter.h"
|
||||||
#include "llvm/Object/COFFImportFile.h"
|
#include "llvm/Object/COFFImportFile.h"
|
||||||
#include "llvm/Object/COFFModuleDefinition.h"
|
#include "llvm/Object/COFFModuleDefinition.h"
|
||||||
@ -63,16 +64,16 @@ LinkerDriver *driver;
|
|||||||
bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) {
|
bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &diag) {
|
||||||
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
|
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
|
||||||
errorHandler().errorOS = &diag;
|
errorHandler().errorOS = &diag;
|
||||||
errorHandler().colorDiagnostics = diag.has_colors();
|
|
||||||
errorHandler().errorLimitExceededMsg =
|
errorHandler().errorLimitExceededMsg =
|
||||||
"too many errors emitted, stopping now"
|
"too many errors emitted, stopping now"
|
||||||
" (use /errorlimit:0 to see all errors)";
|
" (use /errorlimit:0 to see all errors)";
|
||||||
errorHandler().exitEarly = canExitEarly;
|
errorHandler().exitEarly = canExitEarly;
|
||||||
|
enableColors(diag.has_colors());
|
||||||
|
|
||||||
config = make<Configuration>();
|
config = make<Configuration>();
|
||||||
|
|
||||||
symtab = make<SymbolTable>();
|
symtab = make<SymbolTable>();
|
||||||
|
|
||||||
driver = make<LinkerDriver>();
|
driver = make<LinkerDriver>();
|
||||||
|
|
||||||
driver->link(args);
|
driver->link(args);
|
||||||
|
|
||||||
// Call exit() if we can to avoid calling destructors.
|
// Call exit() if we can to avoid calling destructors.
|
||||||
@ -170,7 +171,7 @@ MemoryBufferRef LinkerDriver::takeBuffer(std::unique_ptr<MemoryBuffer> mb) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
||||||
bool wholeArchive) {
|
bool wholeArchive, bool lazy) {
|
||||||
StringRef filename = mb->getBufferIdentifier();
|
StringRef filename = mb->getBufferIdentifier();
|
||||||
|
|
||||||
MemoryBufferRef mbref = takeBuffer(std::move(mb));
|
MemoryBufferRef mbref = takeBuffer(std::move(mb));
|
||||||
@ -188,18 +189,25 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
|||||||
Archive *archive = file.get();
|
Archive *archive = file.get();
|
||||||
make<std::unique_ptr<Archive>>(std::move(file)); // take ownership
|
make<std::unique_ptr<Archive>>(std::move(file)); // take ownership
|
||||||
|
|
||||||
|
int memberIndex = 0;
|
||||||
for (MemoryBufferRef m : getArchiveMembers(archive))
|
for (MemoryBufferRef m : getArchiveMembers(archive))
|
||||||
addArchiveBuffer(m, "<whole-archive>", filename, 0);
|
addArchiveBuffer(m, "<whole-archive>", filename, memberIndex++);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
symtab->addFile(make<ArchiveFile>(mbref));
|
symtab->addFile(make<ArchiveFile>(mbref));
|
||||||
break;
|
break;
|
||||||
case file_magic::bitcode:
|
case file_magic::bitcode:
|
||||||
symtab->addFile(make<BitcodeFile>(mbref, "", 0));
|
if (lazy)
|
||||||
|
symtab->addFile(make<LazyObjFile>(mbref));
|
||||||
|
else
|
||||||
|
symtab->addFile(make<BitcodeFile>(mbref, "", 0));
|
||||||
break;
|
break;
|
||||||
case file_magic::coff_object:
|
case file_magic::coff_object:
|
||||||
case file_magic::coff_import_library:
|
case file_magic::coff_import_library:
|
||||||
symtab->addFile(make<ObjFile>(mbref));
|
if (lazy)
|
||||||
|
symtab->addFile(make<LazyObjFile>(mbref));
|
||||||
|
else
|
||||||
|
symtab->addFile(make<ObjFile>(mbref));
|
||||||
break;
|
break;
|
||||||
case file_magic::pdb:
|
case file_magic::pdb:
|
||||||
loadTypeServerSource(mbref);
|
loadTypeServerSource(mbref);
|
||||||
@ -220,7 +228,7 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> mb,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
|
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
|
||||||
auto future =
|
auto future =
|
||||||
std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
|
std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
|
||||||
std::string pathStr = path;
|
std::string pathStr = path;
|
||||||
@ -240,7 +248,7 @@ void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive) {
|
|||||||
else
|
else
|
||||||
error(msg + "; did you mean '" + nearest + "'");
|
error(msg + "; did you mean '" + nearest + "'");
|
||||||
} else
|
} else
|
||||||
driver->addBuffer(std::move(mbOrErr.first), wholeArchive);
|
driver->addBuffer(std::move(mbOrErr.first), wholeArchive, lazy);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,9 +311,10 @@ void LinkerDriver::enqueueArchiveMember(const Archive::Child &c,
|
|||||||
auto mbOrErr = future->get();
|
auto mbOrErr = future->get();
|
||||||
if (mbOrErr.second)
|
if (mbOrErr.second)
|
||||||
reportBufferError(errorCodeToError(mbOrErr.second), childName);
|
reportBufferError(errorCodeToError(mbOrErr.second), childName);
|
||||||
|
// Pass empty string as archive name so that the original filename is
|
||||||
|
// used as the buffer identifier.
|
||||||
driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
|
driver->addArchiveBuffer(takeBuffer(std::move(mbOrErr.first)),
|
||||||
toCOFFString(sym), parentName,
|
toCOFFString(sym), "", /*OffsetInArchive=*/0);
|
||||||
/*OffsetInArchive=*/0);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +368,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
|
|||||||
break;
|
break;
|
||||||
case OPT_defaultlib:
|
case OPT_defaultlib:
|
||||||
if (Optional<StringRef> path = findLib(arg->getValue()))
|
if (Optional<StringRef> path = findLib(arg->getValue()))
|
||||||
enqueuePath(*path, false);
|
enqueuePath(*path, false, false);
|
||||||
break;
|
break;
|
||||||
case OPT_entry:
|
case OPT_entry:
|
||||||
config->entry = addUndefined(mangle(arg->getValue()));
|
config->entry = addUndefined(mangle(arg->getValue()));
|
||||||
@ -594,6 +603,7 @@ static std::string createResponseFile(const opt::InputArgList &args,
|
|||||||
for (auto *arg : args) {
|
for (auto *arg : args) {
|
||||||
switch (arg->getOption().getID()) {
|
switch (arg->getOption().getID()) {
|
||||||
case OPT_linkrepro:
|
case OPT_linkrepro:
|
||||||
|
case OPT_reproduce:
|
||||||
case OPT_INPUT:
|
case OPT_INPUT:
|
||||||
case OPT_defaultlib:
|
case OPT_defaultlib:
|
||||||
case OPT_libpath:
|
case OPT_libpath:
|
||||||
@ -708,8 +718,7 @@ static std::string getImplibPath() {
|
|||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// The import name is calculated as follows:
|
||||||
// The import name is caculated as the following:
|
|
||||||
//
|
//
|
||||||
// | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY
|
// | LIBRARY w/ ext | LIBRARY w/o ext | no LIBRARY
|
||||||
// -----+----------------+---------------------+------------------
|
// -----+----------------+---------------------+------------------
|
||||||
@ -991,30 +1000,37 @@ static void parsePDBAltPath(StringRef altPath) {
|
|||||||
config->pdbAltPath = buf;
|
config->pdbAltPath = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that at most one resource obj file was used.
|
/// Convert resource files and potentially merge input resource object
|
||||||
|
/// trees into one resource tree.
|
||||||
/// Call after ObjFile::Instances is complete.
|
/// Call after ObjFile::Instances is complete.
|
||||||
static void diagnoseMultipleResourceObjFiles() {
|
void LinkerDriver::convertResources() {
|
||||||
// The .rsrc$01 section in a resource obj file contains a tree description
|
std::vector<ObjFile *> resourceObjFiles;
|
||||||
// of resources. Merging multiple resource obj files would require merging
|
|
||||||
// the trees instead of using usual linker section merging semantics.
|
|
||||||
// Since link.exe disallows linking more than one resource obj file with
|
|
||||||
// LNK4078, mirror that. The normal use of resource files is to give the
|
|
||||||
// linker many .res files, which are then converted to a single resource obj
|
|
||||||
// file internally, so this is not a big restriction in practice.
|
|
||||||
ObjFile *resourceObjFile = nullptr;
|
|
||||||
for (ObjFile *f : ObjFile::instances) {
|
for (ObjFile *f : ObjFile::instances) {
|
||||||
if (!f->isResourceObjFile)
|
if (f->isResourceObjFile())
|
||||||
continue;
|
resourceObjFiles.push_back(f);
|
||||||
|
|
||||||
if (!resourceObjFile) {
|
|
||||||
resourceObjFile = f;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
error(toString(f) +
|
|
||||||
": more than one resource obj file not allowed, already got " +
|
|
||||||
toString(resourceObjFile));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!config->mingw &&
|
||||||
|
(resourceObjFiles.size() > 1 ||
|
||||||
|
(resourceObjFiles.size() == 1 && !resources.empty()))) {
|
||||||
|
error((!resources.empty() ? "internal .obj file created from .res files"
|
||||||
|
: toString(resourceObjFiles[1])) +
|
||||||
|
": more than one resource obj file not allowed, already got " +
|
||||||
|
toString(resourceObjFiles.front()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resources.empty() && resourceObjFiles.size() <= 1) {
|
||||||
|
// No resources to convert, and max one resource object file in
|
||||||
|
// the input. Keep that preconverted resource section as is.
|
||||||
|
for (ObjFile *f : resourceObjFiles)
|
||||||
|
f->includeResourceChunks();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ObjFile *f = make<ObjFile>(convertResToCOFF(resources, resourceObjFiles));
|
||||||
|
symtab->addFile(f);
|
||||||
|
f->includeResourceChunks();
|
||||||
}
|
}
|
||||||
|
|
||||||
// In MinGW, if no symbols are chosen to be exported, then all symbols are
|
// In MinGW, if no symbols are chosen to be exported, then all symbols are
|
||||||
@ -1055,11 +1071,25 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *libcallRoutineNames[] = {
|
// lld has a feature to create a tar file containing all input files as well as
|
||||||
#define HANDLE_LIBCALL(code, name) name,
|
// all command line options, so that other people can run lld again with exactly
|
||||||
#include "llvm/IR/RuntimeLibcalls.def"
|
// the same inputs. This feature is accessible via /linkrepro and /reproduce.
|
||||||
#undef HANDLE_LIBCALL
|
//
|
||||||
};
|
// /linkrepro and /reproduce are very similar, but /linkrepro takes a directory
|
||||||
|
// name while /reproduce takes a full path. We have /linkrepro for compatibility
|
||||||
|
// with Microsoft link.exe.
|
||||||
|
Optional<std::string> getReproduceFile(const opt::InputArgList &args) {
|
||||||
|
if (auto *arg = args.getLastArg(OPT_reproduce))
|
||||||
|
return std::string(arg->getValue());
|
||||||
|
|
||||||
|
if (auto *arg = args.getLastArg(OPT_linkrepro)) {
|
||||||
|
SmallString<64> path = StringRef(arg->getValue());
|
||||||
|
sys::path::append(path, "repro.tar");
|
||||||
|
return path.str().str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
||||||
// Needed for LTO.
|
// Needed for LTO.
|
||||||
@ -1079,7 +1109,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
|
|
||||||
// Parse command line options.
|
// Parse command line options.
|
||||||
ArgParser parser;
|
ArgParser parser;
|
||||||
opt::InputArgList args = parser.parseLINK(argsArr);
|
opt::InputArgList args = parser.parse(argsArr);
|
||||||
|
|
||||||
// Parse and evaluate -mllvm options.
|
// Parse and evaluate -mllvm options.
|
||||||
std::vector<const char *> v;
|
std::vector<const char *> v;
|
||||||
@ -1123,22 +1153,20 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
// options are handled.
|
// options are handled.
|
||||||
config->mingw = args.hasArg(OPT_lldmingw);
|
config->mingw = args.hasArg(OPT_lldmingw);
|
||||||
|
|
||||||
if (auto *arg = args.getLastArg(OPT_linkrepro)) {
|
// Handle /linkrepro and /reproduce.
|
||||||
SmallString<64> path = StringRef(arg->getValue());
|
if (Optional<std::string> path = getReproduceFile(args)) {
|
||||||
sys::path::append(path, "repro.tar");
|
|
||||||
|
|
||||||
Expected<std::unique_ptr<TarWriter>> errOrWriter =
|
Expected<std::unique_ptr<TarWriter>> errOrWriter =
|
||||||
TarWriter::create(path, "repro");
|
TarWriter::create(*path, sys::path::stem(*path));
|
||||||
|
|
||||||
if (errOrWriter) {
|
if (errOrWriter) {
|
||||||
tar = std::move(*errOrWriter);
|
tar = std::move(*errOrWriter);
|
||||||
} else {
|
} else {
|
||||||
error("/linkrepro: failed to open " + path + ": " +
|
error("/linkrepro: failed to open " + *path + ": " +
|
||||||
toString(errOrWriter.takeError()));
|
toString(errOrWriter.takeError()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) {
|
if (!args.hasArg(OPT_INPUT)) {
|
||||||
if (args.hasArg(OPT_deffile))
|
if (args.hasArg(OPT_deffile))
|
||||||
config->noEntry = true;
|
config->noEntry = true;
|
||||||
else
|
else
|
||||||
@ -1149,7 +1177,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
searchPaths.push_back("");
|
searchPaths.push_back("");
|
||||||
for (auto *arg : args.filtered(OPT_libpath))
|
for (auto *arg : args.filtered(OPT_libpath))
|
||||||
searchPaths.push_back(arg->getValue());
|
searchPaths.push_back(arg->getValue());
|
||||||
addLibSearchPaths();
|
if (!args.hasArg(OPT_lldignoreenv))
|
||||||
|
addLibSearchPaths();
|
||||||
|
|
||||||
// Handle /ignore
|
// Handle /ignore
|
||||||
for (auto *arg : args.filtered(OPT_ignore)) {
|
for (auto *arg : args.filtered(OPT_ignore)) {
|
||||||
@ -1481,6 +1510,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
getOldNewOptions(args, OPT_thinlto_prefix_replace);
|
getOldNewOptions(args, OPT_thinlto_prefix_replace);
|
||||||
config->thinLTOObjectSuffixReplace =
|
config->thinLTOObjectSuffixReplace =
|
||||||
getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
|
getOldNewOptions(args, OPT_thinlto_object_suffix_replace);
|
||||||
|
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path);
|
||||||
// Handle miscellaneous boolean flags.
|
// Handle miscellaneous boolean flags.
|
||||||
config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
|
config->allowBind = args.hasFlag(OPT_allowbind, OPT_allowbind_no, true);
|
||||||
config->allowIsolation =
|
config->allowIsolation =
|
||||||
@ -1545,19 +1575,45 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a list of input files. Files can be given as arguments
|
// Create a list of input files. These can be given as OPT_INPUT options
|
||||||
// for /defaultlib option.
|
// and OPT_wholearchive_file options, and we also need to track OPT_start_lib
|
||||||
for (auto *arg : args.filtered(OPT_INPUT, OPT_wholearchive_file))
|
// and OPT_end_lib.
|
||||||
if (Optional<StringRef> path = findFile(arg->getValue()))
|
bool inLib = false;
|
||||||
enqueuePath(*path, isWholeArchive(*path));
|
for (auto *arg : args) {
|
||||||
|
switch (arg->getOption().getID()) {
|
||||||
|
case OPT_end_lib:
|
||||||
|
if (!inLib)
|
||||||
|
error("stray " + arg->getSpelling());
|
||||||
|
inLib = false;
|
||||||
|
break;
|
||||||
|
case OPT_start_lib:
|
||||||
|
if (inLib)
|
||||||
|
error("nested " + arg->getSpelling());
|
||||||
|
inLib = true;
|
||||||
|
break;
|
||||||
|
case OPT_wholearchive_file:
|
||||||
|
if (Optional<StringRef> path = findFile(arg->getValue()))
|
||||||
|
enqueuePath(*path, true, inLib);
|
||||||
|
break;
|
||||||
|
case OPT_INPUT:
|
||||||
|
if (Optional<StringRef> path = findFile(arg->getValue()))
|
||||||
|
enqueuePath(*path, isWholeArchive(*path), inLib);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Ignore other options.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process files specified as /defaultlib. These should be enequeued after
|
||||||
|
// other files, which is why they are in a separate loop.
|
||||||
for (auto *arg : args.filtered(OPT_defaultlib))
|
for (auto *arg : args.filtered(OPT_defaultlib))
|
||||||
if (Optional<StringRef> path = findLib(arg->getValue()))
|
if (Optional<StringRef> path = findLib(arg->getValue()))
|
||||||
enqueuePath(*path, false);
|
enqueuePath(*path, false, false);
|
||||||
|
|
||||||
// Windows specific -- Create a resource file containing a manifest file.
|
// Windows specific -- Create a resource file containing a manifest file.
|
||||||
if (config->manifest == Configuration::Embed)
|
if (config->manifest == Configuration::Embed)
|
||||||
addBuffer(createManifestRes(), false);
|
addBuffer(createManifestRes(), false, false);
|
||||||
|
|
||||||
// Read all input files given via the command line.
|
// Read all input files given via the command line.
|
||||||
run();
|
run();
|
||||||
@ -1582,12 +1638,6 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
|
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
|
||||||
parseFunctionPadMin(arg, config->machine);
|
parseFunctionPadMin(arg, config->machine);
|
||||||
|
|
||||||
// Input files can be Windows resource files (.res files). We use
|
|
||||||
// WindowsResource to convert resource files to a regular COFF file,
|
|
||||||
// then link the resulting file normally.
|
|
||||||
if (!resources.empty())
|
|
||||||
symtab->addFile(make<ObjFile>(convertResToCOFF(resources)));
|
|
||||||
|
|
||||||
if (tar)
|
if (tar)
|
||||||
tar->append("response.txt",
|
tar->append("response.txt",
|
||||||
createResponseFile(args, filePaths,
|
createResponseFile(args, filePaths,
|
||||||
@ -1626,7 +1676,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle generation of import library from a def file.
|
// Handle generation of import library from a def file.
|
||||||
if (!args.hasArg(OPT_INPUT, OPT_wholearchive_file)) {
|
if (!args.hasArg(OPT_INPUT)) {
|
||||||
fixupExports();
|
fixupExports();
|
||||||
createImportLibrary(/*asLib=*/true);
|
createImportLibrary(/*asLib=*/true);
|
||||||
return;
|
return;
|
||||||
@ -1672,8 +1722,8 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
|
|
||||||
// Set default image name if neither /out or /def set it.
|
// Set default image name if neither /out or /def set it.
|
||||||
if (config->outputFile.empty()) {
|
if (config->outputFile.empty()) {
|
||||||
config->outputFile = getOutputPath(
|
config->outputFile =
|
||||||
(*args.filtered(OPT_INPUT, OPT_wholearchive_file).begin())->getValue());
|
getOutputPath((*args.filtered(OPT_INPUT).begin())->getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail early if an output file is not writable.
|
// Fail early if an output file is not writable.
|
||||||
@ -1769,7 +1819,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
// bitcode file in an archive member, we need to arrange to use LTO to
|
// bitcode file in an archive member, we need to arrange to use LTO to
|
||||||
// compile those archive members by adding them to the link beforehand.
|
// compile those archive members by adding them to the link beforehand.
|
||||||
if (!BitcodeFile::instances.empty())
|
if (!BitcodeFile::instances.empty())
|
||||||
for (const char *s : libcallRoutineNames)
|
for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
|
||||||
symtab->addLibcall(s);
|
symtab->addLibcall(s);
|
||||||
|
|
||||||
// Windows specific -- if __load_config_used can be resolved, resolve it.
|
// Windows specific -- if __load_config_used can be resolved, resolve it.
|
||||||
@ -1777,28 +1827,10 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
addUndefined(mangle("_load_config_used"));
|
addUndefined(mangle("_load_config_used"));
|
||||||
} while (run());
|
} while (run());
|
||||||
|
|
||||||
if (errorCount())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
|
||||||
// link those files (unless -thinlto-index-only was given, in which case we
|
|
||||||
// resolve symbols and write indices, but don't generate native code or link).
|
|
||||||
symtab->addCombinedLTOObjects();
|
|
||||||
|
|
||||||
// If -thinlto-index-only is given, we should create only "index
|
|
||||||
// files" and not object files. Index file creation is already done
|
|
||||||
// in addCombinedLTOObject, so we are done if that's the case.
|
|
||||||
if (config->thinLTOIndexOnly)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If we generated native object files from bitcode files, this resolves
|
|
||||||
// references to the symbols we use from them.
|
|
||||||
run();
|
|
||||||
|
|
||||||
if (args.hasArg(OPT_include_optional)) {
|
if (args.hasArg(OPT_include_optional)) {
|
||||||
// Handle /includeoptional
|
// Handle /includeoptional
|
||||||
for (auto *arg : args.filtered(OPT_include_optional))
|
for (auto *arg : args.filtered(OPT_include_optional))
|
||||||
if (dyn_cast_or_null<Lazy>(symtab->find(arg->getValue())))
|
if (dyn_cast_or_null<LazyArchive>(symtab->find(arg->getValue())))
|
||||||
addUndefined(arg->getValue());
|
addUndefined(arg->getValue());
|
||||||
while (run());
|
while (run());
|
||||||
}
|
}
|
||||||
@ -1821,11 +1853,36 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we have resolved all symbols.
|
// At this point, we should not have any symbols that cannot be resolved.
|
||||||
symtab->reportRemainingUndefines();
|
// If we are going to do codegen for link-time optimization, check for
|
||||||
|
// unresolvable symbols first, so we don't spend time generating code that
|
||||||
|
// will fail to link anyway.
|
||||||
|
if (!BitcodeFile::instances.empty() && !config->forceUnresolved)
|
||||||
|
symtab->reportUnresolvable();
|
||||||
if (errorCount())
|
if (errorCount())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
||||||
|
// link those files (unless -thinlto-index-only was given, in which case we
|
||||||
|
// resolve symbols and write indices, but don't generate native code or link).
|
||||||
|
symtab->addCombinedLTOObjects();
|
||||||
|
|
||||||
|
// If -thinlto-index-only is given, we should create only "index
|
||||||
|
// files" and not object files. Index file creation is already done
|
||||||
|
// in addCombinedLTOObject, so we are done if that's the case.
|
||||||
|
if (config->thinLTOIndexOnly)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If we generated native object files from bitcode files, this resolves
|
||||||
|
// references to the symbols we use from them.
|
||||||
|
run();
|
||||||
|
|
||||||
|
// Resolve remaining undefined symbols and warn about imported locals.
|
||||||
|
symtab->resolveRemainingUndefines();
|
||||||
|
if (errorCount())
|
||||||
|
return;
|
||||||
|
|
||||||
|
config->hadExplicitExports = !config->exports.empty();
|
||||||
if (config->mingw) {
|
if (config->mingw) {
|
||||||
// In MinGW, all symbols are automatically exported if no symbols
|
// In MinGW, all symbols are automatically exported if no symbols
|
||||||
// are chosen to be exported.
|
// are chosen to be exported.
|
||||||
@ -1849,10 +1906,12 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Windows specific -- when we are creating a .dll file, we also
|
// Windows specific -- when we are creating a .dll file, we also
|
||||||
// need to create a .lib file.
|
// need to create a .lib file. In MinGW mode, we only do that when the
|
||||||
|
// -implib option is given explicitly, for compatibility with GNU ld.
|
||||||
if (!config->exports.empty() || config->dll) {
|
if (!config->exports.empty() || config->dll) {
|
||||||
fixupExports();
|
fixupExports();
|
||||||
createImportLibrary(/*asLib=*/false);
|
if (!config->mingw || !config->implib.empty())
|
||||||
|
createImportLibrary(/*asLib=*/false);
|
||||||
assignExportOrdinals();
|
assignExportOrdinals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1896,7 +1955,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
|
|||||||
markLive(symtab->getChunks());
|
markLive(symtab->getChunks());
|
||||||
|
|
||||||
// Needs to happen after the last call to addFile().
|
// Needs to happen after the last call to addFile().
|
||||||
diagnoseMultipleResourceObjFiles();
|
convertResources();
|
||||||
|
|
||||||
// Identify identical COMDAT sections to merge them.
|
// Identify identical COMDAT sections to merge them.
|
||||||
if (config->doICF) {
|
if (config->doICF) {
|
||||||
|
@ -43,8 +43,8 @@ class COFFOptTable : public llvm::opt::OptTable {
|
|||||||
|
|
||||||
class ArgParser {
|
class ArgParser {
|
||||||
public:
|
public:
|
||||||
// Concatenate LINK environment variable and given arguments and parse them.
|
// Parses command line options.
|
||||||
llvm::opt::InputArgList parseLINK(std::vector<const char *> args);
|
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
|
||||||
|
|
||||||
// Tokenizes a given string and then parses as command line options.
|
// Tokenizes a given string and then parses as command line options.
|
||||||
llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); }
|
llvm::opt::InputArgList parse(StringRef s) { return parse(tokenize(s)); }
|
||||||
@ -56,8 +56,8 @@ class ArgParser {
|
|||||||
parseDirectives(StringRef s);
|
parseDirectives(StringRef s);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Parses command line options.
|
// Concatenate LINK environment variable.
|
||||||
llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> args);
|
void addLINK(SmallVector<const char *, 256> &argv);
|
||||||
|
|
||||||
std::vector<const char *> tokenize(StringRef s);
|
std::vector<const char *> tokenize(StringRef s);
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ class LinkerDriver {
|
|||||||
|
|
||||||
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
|
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
|
||||||
|
|
||||||
void enqueuePath(StringRef path, bool wholeArchive);
|
void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
|
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
|
||||||
@ -98,6 +98,10 @@ class LinkerDriver {
|
|||||||
// Library search path. The first element is always "" (current directory).
|
// Library search path. The first element is always "" (current directory).
|
||||||
std::vector<StringRef> searchPaths;
|
std::vector<StringRef> searchPaths;
|
||||||
|
|
||||||
|
// Convert resource files and potentially merge input resource object
|
||||||
|
// trees into one resource tree.
|
||||||
|
void convertResources();
|
||||||
|
|
||||||
void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args);
|
void maybeExportMinGWSymbols(const llvm::opt::InputArgList &args);
|
||||||
|
|
||||||
// We don't want to add the same file more than once.
|
// We don't want to add the same file more than once.
|
||||||
@ -120,7 +124,8 @@ class LinkerDriver {
|
|||||||
StringRef findDefaultEntry();
|
StringRef findDefaultEntry();
|
||||||
WindowsSubsystem inferSubsystem();
|
WindowsSubsystem inferSubsystem();
|
||||||
|
|
||||||
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive);
|
void addBuffer(std::unique_ptr<MemoryBuffer> mb, bool wholeArchive,
|
||||||
|
bool lazy);
|
||||||
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
|
void addArchiveBuffer(MemoryBufferRef mbref, StringRef symName,
|
||||||
StringRef parentName, uint64_t offsetInArchive);
|
StringRef parentName, uint64_t offsetInArchive);
|
||||||
|
|
||||||
@ -184,7 +189,8 @@ void assignExportOrdinals();
|
|||||||
void checkFailIfMismatch(StringRef arg, InputFile *source);
|
void checkFailIfMismatch(StringRef arg, InputFile *source);
|
||||||
|
|
||||||
// Convert Windows resource files (.res files) to a .obj file.
|
// Convert Windows resource files (.res files) to a .obj file.
|
||||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs);
|
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
|
||||||
|
ArrayRef<ObjFile *> objs);
|
||||||
|
|
||||||
void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);
|
void runMSVCLinker(std::string rsp, ArrayRef<StringRef> objects);
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ class TemporaryFile {
|
|||||||
|
|
||||||
if (!contents.empty()) {
|
if (!contents.empty()) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
raw_fd_ostream os(path, ec, sys::fs::F_None);
|
raw_fd_ostream os(path, ec, sys::fs::OF_None);
|
||||||
if (ec)
|
if (ec)
|
||||||
fatal("failed to open " + path + ": " + ec.message());
|
fatal("failed to open " + path + ": " + ec.message());
|
||||||
os << contents;
|
os << contents;
|
||||||
@ -410,7 +410,7 @@ static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
|
|||||||
// Create the default manifest file as a temporary file.
|
// Create the default manifest file as a temporary file.
|
||||||
TemporaryFile Default("defaultxml", "manifest");
|
TemporaryFile Default("defaultxml", "manifest");
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
raw_fd_ostream os(Default.path, ec, sys::fs::F_Text);
|
raw_fd_ostream os(Default.path, ec, sys::fs::OF_Text);
|
||||||
if (ec)
|
if (ec)
|
||||||
fatal("failed to open " + Default.path + ": " + ec.message());
|
fatal("failed to open " + Default.path + ": " + ec.message());
|
||||||
os << defaultXml;
|
os << defaultXml;
|
||||||
@ -511,7 +511,7 @@ void createSideBySideManifest() {
|
|||||||
if (path == "")
|
if (path == "")
|
||||||
path = config->outputFile + ".manifest";
|
path = config->outputFile + ".manifest";
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
raw_fd_ostream out(path, ec, sys::fs::F_Text);
|
raw_fd_ostream out(path, ec, sys::fs::OF_Text);
|
||||||
if (ec)
|
if (ec)
|
||||||
fatal("failed to create manifest: " + ec.message());
|
fatal("failed to create manifest: " + ec.message());
|
||||||
out << createManifestXml();
|
out << createManifestXml();
|
||||||
@ -700,26 +700,42 @@ void checkFailIfMismatch(StringRef arg, InputFile *source) {
|
|||||||
|
|
||||||
// Convert Windows resource files (.res files) to a .obj file.
|
// Convert Windows resource files (.res files) to a .obj file.
|
||||||
// Does what cvtres.exe does, but in-process and cross-platform.
|
// Does what cvtres.exe does, but in-process and cross-platform.
|
||||||
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs) {
|
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
|
||||||
object::WindowsResourceParser parser;
|
ArrayRef<ObjFile *> objs) {
|
||||||
|
object::WindowsResourceParser parser(/* MinGW */ config->mingw);
|
||||||
|
|
||||||
|
std::vector<std::string> duplicates;
|
||||||
for (MemoryBufferRef mb : mbs) {
|
for (MemoryBufferRef mb : mbs) {
|
||||||
std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
|
std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));
|
||||||
object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
|
object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());
|
||||||
if (!rf)
|
if (!rf)
|
||||||
fatal("cannot compile non-resource file as resource");
|
fatal("cannot compile non-resource file as resource");
|
||||||
|
|
||||||
std::vector<std::string> duplicates;
|
|
||||||
if (auto ec = parser.parse(rf, duplicates))
|
if (auto ec = parser.parse(rf, duplicates))
|
||||||
fatal(toString(std::move(ec)));
|
fatal(toString(std::move(ec)));
|
||||||
|
|
||||||
for (const auto &dupeDiag : duplicates)
|
|
||||||
if (config->forceMultipleRes)
|
|
||||||
warn(dupeDiag);
|
|
||||||
else
|
|
||||||
error(dupeDiag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: This processes all .res files before all objs. Ideally they'd be
|
||||||
|
// handled in the same order they were linked (to keep the right one, if
|
||||||
|
// there are duplicates that are tolerated due to forceMultipleRes).
|
||||||
|
for (ObjFile *f : objs) {
|
||||||
|
object::ResourceSectionRef rsf;
|
||||||
|
if (auto ec = rsf.load(f->getCOFFObj()))
|
||||||
|
fatal(toString(f) + ": " + toString(std::move(ec)));
|
||||||
|
|
||||||
|
if (auto ec = parser.parse(rsf, f->getName(), duplicates))
|
||||||
|
fatal(toString(std::move(ec)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->mingw)
|
||||||
|
parser.cleanUpManifests(duplicates);
|
||||||
|
|
||||||
|
for (const auto &dupeDiag : duplicates)
|
||||||
|
if (config->forceMultipleRes)
|
||||||
|
warn(dupeDiag);
|
||||||
|
else
|
||||||
|
error(dupeDiag);
|
||||||
|
|
||||||
Expected<std::unique_ptr<MemoryBuffer>> e =
|
Expected<std::unique_ptr<MemoryBuffer>> e =
|
||||||
llvm::object::writeWindowsResourceCOFF(config->machine, parser,
|
llvm::object::writeWindowsResourceCOFF(config->machine, parser,
|
||||||
config->timestamp);
|
config->timestamp);
|
||||||
@ -757,15 +773,15 @@ static void handleColorDiagnostics(opt::InputArgList &args) {
|
|||||||
if (!arg)
|
if (!arg)
|
||||||
return;
|
return;
|
||||||
if (arg->getOption().getID() == OPT_color_diagnostics) {
|
if (arg->getOption().getID() == OPT_color_diagnostics) {
|
||||||
errorHandler().colorDiagnostics = true;
|
enableColors(true);
|
||||||
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
|
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
|
||||||
errorHandler().colorDiagnostics = false;
|
enableColors(false);
|
||||||
} else {
|
} else {
|
||||||
StringRef s = arg->getValue();
|
StringRef s = arg->getValue();
|
||||||
if (s == "always")
|
if (s == "always")
|
||||||
errorHandler().colorDiagnostics = true;
|
enableColors(true);
|
||||||
else if (s == "never")
|
else if (s == "never")
|
||||||
errorHandler().colorDiagnostics = false;
|
enableColors(false);
|
||||||
else if (s != "auto")
|
else if (s != "auto")
|
||||||
error("unknown option: --color-diagnostics=" + s);
|
error("unknown option: --color-diagnostics=" + s);
|
||||||
}
|
}
|
||||||
@ -792,13 +808,17 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {
|
|||||||
|
|
||||||
// We need to get the quoting style for response files before parsing all
|
// We need to get the quoting style for response files before parsing all
|
||||||
// options so we parse here before and ignore all the options but
|
// options so we parse here before and ignore all the options but
|
||||||
// --rsp-quoting.
|
// --rsp-quoting and /lldignoreenv.
|
||||||
|
// (This means --rsp-quoting can't be added through %LINK%.)
|
||||||
opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
|
opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
|
||||||
|
|
||||||
// Expand response files (arguments in the form of @<filename>)
|
|
||||||
// and then parse the argument again.
|
// Expand response files (arguments in the form of @<filename>) and insert
|
||||||
|
// flags from %LINK% and %_LINK_%, and then parse the argument again.
|
||||||
SmallVector<const char *, 256> expandedArgv(argv.data(),
|
SmallVector<const char *, 256> expandedArgv(argv.data(),
|
||||||
argv.data() + argv.size());
|
argv.data() + argv.size());
|
||||||
|
if (!args.hasArg(OPT_lldignoreenv))
|
||||||
|
addLINK(expandedArgv);
|
||||||
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
|
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
|
||||||
args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
|
args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
|
||||||
missingCount);
|
missingCount);
|
||||||
@ -868,7 +888,7 @@ ArgParser::parseDirectives(StringRef s) {
|
|||||||
// link.exe has an interesting feature. If LINK or _LINK_ environment
|
// link.exe has an interesting feature. If LINK or _LINK_ environment
|
||||||
// variables exist, their contents are handled as command line strings.
|
// variables exist, their contents are handled as command line strings.
|
||||||
// So you can pass extra arguments using them.
|
// So you can pass extra arguments using them.
|
||||||
opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
|
void ArgParser::addLINK(SmallVector<const char *, 256> &argv) {
|
||||||
// Concatenate LINK env and command line arguments, and then parse them.
|
// Concatenate LINK env and command line arguments, and then parse them.
|
||||||
if (Optional<std::string> s = Process::GetEnv("LINK")) {
|
if (Optional<std::string> s = Process::GetEnv("LINK")) {
|
||||||
std::vector<const char *> v = tokenize(*s);
|
std::vector<const char *> v = tokenize(*s);
|
||||||
@ -878,7 +898,6 @@ opt::InputArgList ArgParser::parseLINK(std::vector<const char *> argv) {
|
|||||||
std::vector<const char *> v = tokenize(*s);
|
std::vector<const char *> v = tokenize(*s);
|
||||||
argv.insert(std::next(argv.begin()), v.begin(), v.end());
|
argv.insert(std::next(argv.begin()), v.begin(), v.end());
|
||||||
}
|
}
|
||||||
return parse(argv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const char *> ArgParser::tokenize(StringRef s) {
|
std::vector<const char *> ArgParser::tokenize(StringRef s) {
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
//
|
//
|
||||||
// On Windows, ICF is enabled by default.
|
// On Windows, ICF is enabled by default.
|
||||||
//
|
//
|
||||||
// See ELF/ICF.cpp for the details about the algortihm.
|
// See ELF/ICF.cpp for the details about the algorithm.
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ class ICF {
|
|||||||
// section is insignificant to the user program and the behaviour matches that
|
// section is insignificant to the user program and the behaviour matches that
|
||||||
// of the Visual C++ linker.
|
// of the Visual C++ linker.
|
||||||
bool ICF::isEligible(SectionChunk *c) {
|
bool ICF::isEligible(SectionChunk *c) {
|
||||||
// Non-comdat chunks, dead chunks, and writable chunks are not elegible.
|
// Non-comdat chunks, dead chunks, and writable chunks are not eligible.
|
||||||
bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
|
bool writable = c->getOutputCharacteristics() & llvm::COFF::IMAGE_SCN_MEM_WRITE;
|
||||||
if (!c->isCOMDAT() || !c->live || writable)
|
if (!c->isCOMDAT() || !c->live || writable)
|
||||||
return false;
|
return false;
|
||||||
@ -274,7 +274,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
|
|||||||
for (Symbol *b : sc->symbols())
|
for (Symbol *b : sc->symbols())
|
||||||
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
|
if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
|
||||||
hash += sym->getChunk()->eqClass[cnt % 2];
|
hash += sym->getChunk()->eqClass[cnt % 2];
|
||||||
// Set MSB to 1 to avoid collisions with non-hash classs.
|
// Set MSB to 1 to avoid collisions with non-hash classes.
|
||||||
sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
|
sc->eqClass[(cnt + 1) % 2] = hash | (1U << 31);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -297,7 +297,7 @@ void ICF::run(ArrayRef<Chunk *> vec) {
|
|||||||
|
|
||||||
log("ICF needed " + Twine(cnt) + " iterations");
|
log("ICF needed " + Twine(cnt) + " iterations");
|
||||||
|
|
||||||
// Merge sections in the same classs.
|
// Merge sections in the same classes.
|
||||||
forEachClass([&](size_t begin, size_t end) {
|
forEachClass([&](size_t begin, size_t end) {
|
||||||
if (end - begin == 1)
|
if (end - begin == 1)
|
||||||
return;
|
return;
|
||||||
|
@ -47,6 +47,24 @@ using llvm::Triple;
|
|||||||
using llvm::support::ulittle32_t;
|
using llvm::support::ulittle32_t;
|
||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
|
|
||||||
|
// Returns the last element of a path, which is supposed to be a filename.
|
||||||
|
static StringRef getBasename(StringRef path) {
|
||||||
|
return sys::path::filename(path, sys::path::Style::windows);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
|
||||||
|
std::string toString(const coff::InputFile *file) {
|
||||||
|
if (!file)
|
||||||
|
return "<internal>";
|
||||||
|
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
|
||||||
|
return file->getName();
|
||||||
|
|
||||||
|
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
|
||||||
|
")")
|
||||||
|
.str();
|
||||||
|
}
|
||||||
|
|
||||||
namespace coff {
|
namespace coff {
|
||||||
|
|
||||||
std::vector<ObjFile *> ObjFile::instances;
|
std::vector<ObjFile *> ObjFile::instances;
|
||||||
@ -73,6 +91,10 @@ static void checkAndSetWeakAlias(SymbolTable *symtab, InputFile *f,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool ignoredSymbolName(StringRef name) {
|
||||||
|
return name == "@feat.00" || name == "@comp.id";
|
||||||
|
}
|
||||||
|
|
||||||
ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
|
ArchiveFile::ArchiveFile(MemoryBufferRef m) : InputFile(ArchiveKind, m) {}
|
||||||
|
|
||||||
void ArchiveFile::parse() {
|
void ArchiveFile::parse() {
|
||||||
@ -81,7 +103,7 @@ void ArchiveFile::parse() {
|
|||||||
|
|
||||||
// Read the symbol table to construct Lazy objects.
|
// Read the symbol table to construct Lazy objects.
|
||||||
for (const Archive::Symbol &sym : file->symbols())
|
for (const Archive::Symbol &sym : file->symbols())
|
||||||
symtab->addLazy(this, sym);
|
symtab->addLazyArchive(this, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a buffer pointing to a member file containing a given symbol.
|
// Returns a buffer pointing to a member file containing a given symbol.
|
||||||
@ -116,6 +138,49 @@ std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LazyObjFile::fetch() {
|
||||||
|
if (mb.getBuffer().empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
InputFile *file;
|
||||||
|
if (isBitcode(mb))
|
||||||
|
file = make<BitcodeFile>(mb, "", 0, std::move(symbols));
|
||||||
|
else
|
||||||
|
file = make<ObjFile>(mb, std::move(symbols));
|
||||||
|
mb = {};
|
||||||
|
symtab->addFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LazyObjFile::parse() {
|
||||||
|
if (isBitcode(this->mb)) {
|
||||||
|
// Bitcode file.
|
||||||
|
std::unique_ptr<lto::InputFile> obj =
|
||||||
|
CHECK(lto::InputFile::create(this->mb), this);
|
||||||
|
for (const lto::InputFile::Symbol &sym : obj->symbols()) {
|
||||||
|
if (!sym.isUndefined())
|
||||||
|
symtab->addLazyObject(this, sym.getName());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Native object file.
|
||||||
|
std::unique_ptr<Binary> coffObjPtr = CHECK(createBinary(mb), this);
|
||||||
|
COFFObjectFile *coffObj = cast<COFFObjectFile>(coffObjPtr.get());
|
||||||
|
uint32_t numSymbols = coffObj->getNumberOfSymbols();
|
||||||
|
for (uint32_t i = 0; i < numSymbols; ++i) {
|
||||||
|
COFFSymbolRef coffSym = check(coffObj->getSymbol(i));
|
||||||
|
if (coffSym.isUndefined() || !coffSym.isExternal() ||
|
||||||
|
coffSym.isWeakExternal())
|
||||||
|
continue;
|
||||||
|
StringRef name;
|
||||||
|
coffObj->getSymbolName(coffSym, name);
|
||||||
|
if (coffSym.isAbsolute() && ignoredSymbolName(name))
|
||||||
|
continue;
|
||||||
|
symtab->addLazyObject(this, name);
|
||||||
|
i += coffSym.getNumberOfAuxSymbols();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ObjFile::parse() {
|
void ObjFile::parse() {
|
||||||
// Parse a memory buffer as a COFF file.
|
// Parse a memory buffer as a COFF file.
|
||||||
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
|
std::unique_ptr<Binary> bin = CHECK(createBinary(mb), this);
|
||||||
@ -206,10 +271,6 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
|
|||||||
if (def)
|
if (def)
|
||||||
c->checksum = def->CheckSum;
|
c->checksum = def->CheckSum;
|
||||||
|
|
||||||
// link.exe uses the presence of .rsrc$01 for LNK4078, so match that.
|
|
||||||
if (name == ".rsrc$01")
|
|
||||||
isResourceObjFile = true;
|
|
||||||
|
|
||||||
// CodeView sections are stored to a different vector because they are not
|
// CodeView sections are stored to a different vector because they are not
|
||||||
// linked in the regular manner.
|
// linked in the regular manner.
|
||||||
if (c->isCodeView())
|
if (c->isCodeView())
|
||||||
@ -226,12 +287,18 @@ SectionChunk *ObjFile::readSection(uint32_t sectionNumber,
|
|||||||
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
|
// relocations, in .rdata, leader symbol name matches the MSVC name mangling
|
||||||
// for string literals) are subject to string tail merging.
|
// for string literals) are subject to string tail merging.
|
||||||
MergeChunk::addSection(c);
|
MergeChunk::addSection(c);
|
||||||
|
else if (name == ".rsrc" || name.startswith(".rsrc$"))
|
||||||
|
resourceChunks.push_back(c);
|
||||||
else
|
else
|
||||||
chunks.push_back(c);
|
chunks.push_back(c);
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjFile::includeResourceChunks() {
|
||||||
|
chunks.insert(chunks.end(), resourceChunks.begin(), resourceChunks.end());
|
||||||
|
}
|
||||||
|
|
||||||
void ObjFile::readAssociativeDefinition(
|
void ObjFile::readAssociativeDefinition(
|
||||||
COFFSymbolRef sym, const coff_aux_section_definition *def) {
|
COFFSymbolRef sym, const coff_aux_section_definition *def) {
|
||||||
readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
|
readAssociativeDefinition(sym, def, def->getNumber(sym.isBigObj()));
|
||||||
@ -315,7 +382,8 @@ Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
|
|||||||
StringRef name;
|
StringRef name;
|
||||||
coffObj->getSymbolName(sym, name);
|
coffObj->getSymbolName(sym, name);
|
||||||
if (sc)
|
if (sc)
|
||||||
return symtab->addRegular(this, name, sym.getGeneric(), sc);
|
return symtab->addRegular(this, name, sym.getGeneric(), sc,
|
||||||
|
sym.getValue());
|
||||||
// For MinGW symbols named .weak.* that point to a discarded section,
|
// For MinGW symbols named .weak.* that point to a discarded section,
|
||||||
// don't create an Undefined symbol. If nothing ever refers to the symbol,
|
// don't create an Undefined symbol. If nothing ever refers to the symbol,
|
||||||
// everything should be fine. If something actually refers to the symbol
|
// everything should be fine. If something actually refers to the symbol
|
||||||
@ -469,7 +537,7 @@ void ObjFile::handleComdatSelection(COFFSymbolRef sym, COMDATType &selection,
|
|||||||
// if the two comdat sections have e.g. different alignment.
|
// if the two comdat sections have e.g. different alignment.
|
||||||
// Match that.
|
// Match that.
|
||||||
if (leaderChunk->getContents() != newChunk.getContents())
|
if (leaderChunk->getContents() != newChunk.getContents())
|
||||||
symtab->reportDuplicate(leader, this);
|
symtab->reportDuplicate(leader, this, &newChunk, sym.getValue());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,13 +592,11 @@ Optional<Symbol *> ObjFile::createDefined(
|
|||||||
if (sym.isAbsolute()) {
|
if (sym.isAbsolute()) {
|
||||||
StringRef name = getName();
|
StringRef name = getName();
|
||||||
|
|
||||||
// Skip special symbols.
|
if (name == "@feat.00")
|
||||||
if (name == "@comp.id")
|
|
||||||
return nullptr;
|
|
||||||
if (name == "@feat.00") {
|
|
||||||
feat00Flags = sym.getValue();
|
feat00Flags = sym.getValue();
|
||||||
|
// Skip special symbols.
|
||||||
|
if (ignoredSymbolName(name))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
if (sym.isExternal())
|
if (sym.isExternal())
|
||||||
return symtab->addAbsolute(name, sym);
|
return symtab->addAbsolute(name, sym);
|
||||||
@ -552,7 +618,7 @@ Optional<Symbol *> ObjFile::createDefined(
|
|||||||
// Comdat handling.
|
// Comdat handling.
|
||||||
// A comdat symbol consists of two symbol table entries.
|
// A comdat symbol consists of two symbol table entries.
|
||||||
// The first symbol entry has the name of the section (e.g. .text), fixed
|
// The first symbol entry has the name of the section (e.g. .text), fixed
|
||||||
// values for the other fields, and one auxilliary record.
|
// values for the other fields, and one auxiliary record.
|
||||||
// The second symbol entry has the name of the comdat symbol, called the
|
// The second symbol entry has the name of the comdat symbol, called the
|
||||||
// "comdat leader".
|
// "comdat leader".
|
||||||
// When this function is called for the first symbol entry of a comdat,
|
// When this function is called for the first symbol entry of a comdat,
|
||||||
@ -622,7 +688,7 @@ ArrayRef<uint8_t> ObjFile::getDebugSection(StringRef secName) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// OBJ files systematically store critical informations in a .debug$S stream,
|
// OBJ files systematically store critical information in a .debug$S stream,
|
||||||
// even if the TU was compiled with no debug info. At least two records are
|
// even if the TU was compiled with no debug info. At least two records are
|
||||||
// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the
|
// always there. S_OBJNAME stores a 32-bit signature, which is loaded into the
|
||||||
// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is
|
// PCHSignature member. S_COMPILE3 stores compile-time cmd-line flags. This is
|
||||||
@ -723,6 +789,37 @@ void ObjFile::initializeDependencies() {
|
|||||||
debugTypesObj = makeTpiSource(this);
|
debugTypesObj = makeTpiSource(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used only for DWARF debug info, which is not common (except in MinGW
|
||||||
|
// environments). This returns an optional pair of file name and line
|
||||||
|
// number for where the variable was defined.
|
||||||
|
Optional<std::pair<StringRef, uint32_t>>
|
||||||
|
ObjFile::getVariableLocation(StringRef var) {
|
||||||
|
if (!dwarf) {
|
||||||
|
dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
|
||||||
|
if (!dwarf)
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if (config->machine == I386)
|
||||||
|
var.consume_front("_");
|
||||||
|
Optional<std::pair<std::string, unsigned>> ret = dwarf->getVariableLoc(var);
|
||||||
|
if (!ret)
|
||||||
|
return None;
|
||||||
|
return std::make_pair(saver.save(ret->first), ret->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used only for DWARF debug info, which is not common (except in MinGW
|
||||||
|
// environments).
|
||||||
|
Optional<DILineInfo> ObjFile::getDILineInfo(uint32_t offset,
|
||||||
|
uint32_t sectionIndex) {
|
||||||
|
if (!dwarf) {
|
||||||
|
dwarf = make<DWARFCache>(DWARFContext::create(*getCOFFObj()));
|
||||||
|
if (!dwarf)
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dwarf->getDILineInfo(offset, sectionIndex);
|
||||||
|
}
|
||||||
|
|
||||||
StringRef ltrim1(StringRef s, const char *chars) {
|
StringRef ltrim1(StringRef s, const char *chars) {
|
||||||
if (!s.empty() && strchr(chars, s[0]))
|
if (!s.empty() && strchr(chars, s[0]))
|
||||||
return s.substr(1);
|
return s.substr(1);
|
||||||
@ -780,8 +877,9 @@ void ImportFile::parse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||||
uint64_t offsetInArchive)
|
uint64_t offsetInArchive,
|
||||||
: InputFile(BitcodeKind, mb) {
|
std::vector<Symbol *> &&symbols)
|
||||||
|
: InputFile(BitcodeKind, mb), symbols(std::move(symbols)) {
|
||||||
std::string path = mb.getBufferIdentifier().str();
|
std::string path = mb.getBufferIdentifier().str();
|
||||||
if (config->thinLTOIndexOnly)
|
if (config->thinLTOIndexOnly)
|
||||||
path = replaceThinLTOSuffix(mb.getBufferIdentifier());
|
path = replaceThinLTOSuffix(mb.getBufferIdentifier());
|
||||||
@ -860,22 +958,6 @@ std::string replaceThinLTOSuffix(StringRef path) {
|
|||||||
return (path + repl).str();
|
return (path + repl).str();
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
} // namespace lld
|
} // namespace lld
|
||||||
|
|
||||||
// Returns the last element of a path, which is supposed to be a filename.
|
|
||||||
static StringRef getBasename(StringRef path) {
|
|
||||||
return sys::path::filename(path, sys::path::Style::windows);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
|
|
||||||
std::string lld::toString(const coff::InputFile *file) {
|
|
||||||
if (!file)
|
|
||||||
return "<internal>";
|
|
||||||
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
|
|
||||||
return file->getName();
|
|
||||||
|
|
||||||
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
|
|
||||||
")")
|
|
||||||
.str();
|
|
||||||
}
|
|
||||||
|
@ -10,10 +10,12 @@
|
|||||||
#define LLD_COFF_INPUT_FILES_H
|
#define LLD_COFF_INPUT_FILES_H
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "lld/Common/DWARF.h"
|
||||||
#include "lld/Common/LLVM.h"
|
#include "lld/Common/LLVM.h"
|
||||||
#include "llvm/ADT/ArrayRef.h"
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
#include "llvm/ADT/DenseSet.h"
|
#include "llvm/ADT/DenseSet.h"
|
||||||
|
#include "llvm/BinaryFormat/Magic.h"
|
||||||
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
|
||||||
#include "llvm/LTO/LTO.h"
|
#include "llvm/LTO/LTO.h"
|
||||||
#include "llvm/Object/Archive.h"
|
#include "llvm/Object/Archive.h"
|
||||||
@ -24,6 +26,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
struct DILineInfo;
|
||||||
namespace pdb {
|
namespace pdb {
|
||||||
class DbiModuleDescriptorBuilder;
|
class DbiModuleDescriptorBuilder;
|
||||||
}
|
}
|
||||||
@ -47,7 +50,6 @@ class Defined;
|
|||||||
class DefinedImportData;
|
class DefinedImportData;
|
||||||
class DefinedImportThunk;
|
class DefinedImportThunk;
|
||||||
class DefinedRegular;
|
class DefinedRegular;
|
||||||
class Lazy;
|
|
||||||
class SectionChunk;
|
class SectionChunk;
|
||||||
class Symbol;
|
class Symbol;
|
||||||
class Undefined;
|
class Undefined;
|
||||||
@ -56,7 +58,13 @@ class TpiSource;
|
|||||||
// The root class of input files.
|
// The root class of input files.
|
||||||
class InputFile {
|
class InputFile {
|
||||||
public:
|
public:
|
||||||
enum Kind { ArchiveKind, ObjectKind, ImportKind, BitcodeKind };
|
enum Kind {
|
||||||
|
ArchiveKind,
|
||||||
|
ObjectKind,
|
||||||
|
LazyObjectKind,
|
||||||
|
ImportKind,
|
||||||
|
BitcodeKind
|
||||||
|
};
|
||||||
Kind kind() const { return fileKind; }
|
Kind kind() const { return fileKind; }
|
||||||
virtual ~InputFile() {}
|
virtual ~InputFile() {}
|
||||||
|
|
||||||
@ -103,10 +111,28 @@ class ArchiveFile : public InputFile {
|
|||||||
llvm::DenseSet<uint64_t> seen;
|
llvm::DenseSet<uint64_t> seen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// .obj or .o file between -start-lib and -end-lib.
|
||||||
|
class LazyObjFile : public InputFile {
|
||||||
|
public:
|
||||||
|
explicit LazyObjFile(MemoryBufferRef m) : InputFile(LazyObjectKind, m) {}
|
||||||
|
static bool classof(const InputFile *f) {
|
||||||
|
return f->kind() == LazyObjectKind;
|
||||||
|
}
|
||||||
|
// Makes this object file part of the link.
|
||||||
|
void fetch();
|
||||||
|
// Adds the symbols in this file to the symbol table as LazyObject symbols.
|
||||||
|
void parse() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Symbol *> symbols;
|
||||||
|
};
|
||||||
|
|
||||||
// .obj or .o file. This may be a member of an archive file.
|
// .obj or .o file. This may be a member of an archive file.
|
||||||
class ObjFile : public InputFile {
|
class ObjFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
|
explicit ObjFile(MemoryBufferRef m) : InputFile(ObjectKind, m) {}
|
||||||
|
explicit ObjFile(MemoryBufferRef m, std::vector<Symbol *> &&symbols)
|
||||||
|
: InputFile(ObjectKind, m), symbols(std::move(symbols)) {}
|
||||||
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
|
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
|
||||||
void parse() override;
|
void parse() override;
|
||||||
MachineTypes getMachineType() override;
|
MachineTypes getMachineType() override;
|
||||||
@ -135,6 +161,10 @@ class ObjFile : public InputFile {
|
|||||||
return symbols.size() - 1;
|
return symbols.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void includeResourceChunks();
|
||||||
|
|
||||||
|
bool isResourceObjFile() const { return !resourceChunks.empty(); }
|
||||||
|
|
||||||
static std::vector<ObjFile *> instances;
|
static std::vector<ObjFile *> instances;
|
||||||
|
|
||||||
// Flags in the absolute @feat.00 symbol if it is present. These usually
|
// Flags in the absolute @feat.00 symbol if it is present. These usually
|
||||||
@ -162,9 +192,6 @@ class ObjFile : public InputFile {
|
|||||||
// precompiled object. Any difference indicates out-of-date objects.
|
// precompiled object. Any difference indicates out-of-date objects.
|
||||||
llvm::Optional<uint32_t> pchSignature;
|
llvm::Optional<uint32_t> pchSignature;
|
||||||
|
|
||||||
// Whether this is an object file created from .res files.
|
|
||||||
bool isResourceObjFile = false;
|
|
||||||
|
|
||||||
// Whether this file was compiled with /hotpatch.
|
// Whether this file was compiled with /hotpatch.
|
||||||
bool hotPatchable = false;
|
bool hotPatchable = false;
|
||||||
|
|
||||||
@ -177,6 +204,12 @@ class ObjFile : public InputFile {
|
|||||||
// The .debug$T stream if there's one.
|
// The .debug$T stream if there's one.
|
||||||
llvm::Optional<llvm::codeview::CVTypeArray> debugTypes;
|
llvm::Optional<llvm::codeview::CVTypeArray> debugTypes;
|
||||||
|
|
||||||
|
llvm::Optional<std::pair<StringRef, uint32_t>>
|
||||||
|
getVariableLocation(StringRef var);
|
||||||
|
|
||||||
|
llvm::Optional<llvm::DILineInfo> getDILineInfo(uint32_t offset,
|
||||||
|
uint32_t sectionIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const coff_section* getSection(uint32_t i);
|
const coff_section* getSection(uint32_t i);
|
||||||
const coff_section *getSection(COFFSymbolRef sym) {
|
const coff_section *getSection(COFFSymbolRef sym) {
|
||||||
@ -234,6 +267,8 @@ class ObjFile : public InputFile {
|
|||||||
// chunks and non-section chunks for common symbols.
|
// chunks and non-section chunks for common symbols.
|
||||||
std::vector<Chunk *> chunks;
|
std::vector<Chunk *> chunks;
|
||||||
|
|
||||||
|
std::vector<SectionChunk *> resourceChunks;
|
||||||
|
|
||||||
// CodeView debug info sections.
|
// CodeView debug info sections.
|
||||||
std::vector<SectionChunk *> debugChunks;
|
std::vector<SectionChunk *> debugChunks;
|
||||||
|
|
||||||
@ -258,6 +293,8 @@ class ObjFile : public InputFile {
|
|||||||
// index. Nonexistent indices (which are occupied by auxiliary
|
// index. Nonexistent indices (which are occupied by auxiliary
|
||||||
// symbols in the real symbol table) are filled with null pointers.
|
// symbols in the real symbol table) are filled with null pointers.
|
||||||
std::vector<Symbol *> symbols;
|
std::vector<Symbol *> symbols;
|
||||||
|
|
||||||
|
DWARFCache *dwarf = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This type represents import library members that contain DLL names
|
// This type represents import library members that contain DLL names
|
||||||
@ -299,7 +336,11 @@ class ImportFile : public InputFile {
|
|||||||
class BitcodeFile : public InputFile {
|
class BitcodeFile : public InputFile {
|
||||||
public:
|
public:
|
||||||
BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
|
||||||
uint64_t offsetInArchive);
|
uint64_t offsetInArchive)
|
||||||
|
: BitcodeFile(mb, archiveName, offsetInArchive, {}) {}
|
||||||
|
explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName,
|
||||||
|
uint64_t offsetInArchive,
|
||||||
|
std::vector<Symbol *> &&symbols);
|
||||||
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
|
||||||
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
ArrayRef<Symbol *> getSymbols() { return symbols; }
|
||||||
MachineTypes getMachineType() override;
|
MachineTypes getMachineType() override;
|
||||||
@ -312,6 +353,10 @@ class BitcodeFile : public InputFile {
|
|||||||
std::vector<Symbol *> symbols;
|
std::vector<Symbol *> symbols;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool isBitcode(MemoryBufferRef mb) {
|
||||||
|
return identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode;
|
||||||
|
}
|
||||||
|
|
||||||
std::string replaceThinLTOSuffix(StringRef path);
|
std::string replaceThinLTOSuffix(StringRef path);
|
||||||
} // namespace coff
|
} // namespace coff
|
||||||
|
|
||||||
|
@ -39,14 +39,14 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::coff;
|
namespace coff {
|
||||||
|
|
||||||
// Creates an empty file to and returns a raw_fd_ostream to write to it.
|
// Creates an empty file to and returns a raw_fd_ostream to write to it.
|
||||||
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
|
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
auto ret =
|
auto ret =
|
||||||
llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
|
std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
error("cannot open " + file + ": " + ec.message());
|
error("cannot open " + file + ": " + ec.message());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -105,7 +105,7 @@ BitcodeCompiler::BitcodeCompiler() {
|
|||||||
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
|
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
|
||||||
}
|
}
|
||||||
|
|
||||||
ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
|
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
|
||||||
config->ltoPartitions);
|
config->ltoPartitions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,8 +160,8 @@ std::vector<StringRef> BitcodeCompiler::compile() {
|
|||||||
|
|
||||||
checkError(ltoObj->run(
|
checkError(ltoObj->run(
|
||||||
[&](size_t task) {
|
[&](size_t task) {
|
||||||
return llvm::make_unique<lto::NativeObjectStream>(
|
return std::make_unique<lto::NativeObjectStream>(
|
||||||
llvm::make_unique<raw_svector_ostream>(buf[task]));
|
std::make_unique<raw_svector_ostream>(buf[task]));
|
||||||
},
|
},
|
||||||
cache));
|
cache));
|
||||||
|
|
||||||
@ -177,6 +177,8 @@ std::vector<StringRef> BitcodeCompiler::compile() {
|
|||||||
// files. After that, we exit from linker and ThinLTO backend runs in a
|
// files. After that, we exit from linker and ThinLTO backend runs in a
|
||||||
// distributed environment.
|
// distributed environment.
|
||||||
if (config->thinLTOIndexOnly) {
|
if (config->thinLTOIndexOnly) {
|
||||||
|
if (!config->ltoObjPath.empty())
|
||||||
|
saveBuffer(buf[0], config->ltoObjPath);
|
||||||
if (indexFile)
|
if (indexFile)
|
||||||
indexFile->close();
|
indexFile->close();
|
||||||
return {};
|
return {};
|
||||||
@ -204,3 +206,6 @@ std::vector<StringRef> BitcodeCompiler::compile() {
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace coff
|
||||||
|
} // namespace lld
|
||||||
|
@ -29,14 +29,14 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::coff;
|
namespace coff {
|
||||||
|
|
||||||
using SymbolMapTy =
|
using SymbolMapTy =
|
||||||
DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
|
DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
|
||||||
|
|
||||||
static const std::string indent8 = " "; // 8 spaces
|
static constexpr char indent8[] = " "; // 8 spaces
|
||||||
static const std::string indent16 = " "; // 16 spaces
|
static constexpr char indent16[] = " "; // 16 spaces
|
||||||
|
|
||||||
// Print out the first three columns of a line.
|
// Print out the first three columns of a line.
|
||||||
static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
|
static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
|
||||||
@ -87,12 +87,12 @@ getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
|
void writeMapFile(ArrayRef<OutputSection *> outputSections) {
|
||||||
if (config->mapFile.empty())
|
if (config->mapFile.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
|
raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
|
||||||
if (ec)
|
if (ec)
|
||||||
fatal("cannot open " + config->mapFile + ": " + ec.message());
|
fatal("cannot open " + config->mapFile + ": " + ec.message());
|
||||||
|
|
||||||
@ -122,3 +122,6 @@ void coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace coff
|
||||||
|
} // namespace lld
|
||||||
|
@ -13,11 +13,12 @@
|
|||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::coff;
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::COFF;
|
using namespace llvm::COFF;
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace coff {
|
||||||
|
|
||||||
AutoExporter::AutoExporter() {
|
AutoExporter::AutoExporter() {
|
||||||
excludeLibs = {
|
excludeLibs = {
|
||||||
"libgcc",
|
"libgcc",
|
||||||
@ -55,7 +56,7 @@ AutoExporter::AutoExporter() {
|
|||||||
// C++ symbols
|
// C++ symbols
|
||||||
"__rtti_",
|
"__rtti_",
|
||||||
"__builtin_",
|
"__builtin_",
|
||||||
// Artifical symbols such as .refptr
|
// Artificial symbols such as .refptr
|
||||||
".",
|
".",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -146,9 +147,9 @@ bool AutoExporter::shouldExport(Defined *sym) const {
|
|||||||
return !excludeObjects.count(fileName);
|
return !excludeObjects.count(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void coff::writeDefFile(StringRef name) {
|
void writeDefFile(StringRef name) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
raw_fd_ostream os(name, ec, sys::fs::F_None);
|
raw_fd_ostream os(name, ec, sys::fs::OF_None);
|
||||||
if (ec)
|
if (ec)
|
||||||
fatal("cannot open " + name + ": " + ec.message());
|
fatal("cannot open " + name + ": " + ec.message());
|
||||||
|
|
||||||
@ -164,3 +165,6 @@ void coff::writeDefFile(StringRef name) {
|
|||||||
os << "\n";
|
os << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace coff
|
||||||
|
} // namespace lld
|
||||||
|
@ -21,9 +21,9 @@ def aligncomm : P<"aligncomm", "Set common symbol alignment">;
|
|||||||
def alternatename : P<"alternatename", "Define weak alias">;
|
def alternatename : P<"alternatename", "Define weak alias">;
|
||||||
def base : P<"base", "Base address of the program">;
|
def base : P<"base", "Base address of the program">;
|
||||||
def color_diagnostics: Flag<["--"], "color-diagnostics">,
|
def color_diagnostics: Flag<["--"], "color-diagnostics">,
|
||||||
HelpText<"Use colors in diagnostics">;
|
HelpText<"Use colors in diagnostics">;
|
||||||
def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
|
def color_diagnostics_eq: Joined<["--"], "color-diagnostics=">,
|
||||||
HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">;
|
HelpText<"Use colors in diagnostics; one of 'always', 'never', 'auto'">;
|
||||||
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
|
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
|
||||||
def delayload : P<"delayload", "Delay loaded DLL name">;
|
def delayload : P<"delayload", "Delay loaded DLL name">;
|
||||||
def entry : P<"entry", "Name of entry point symbol">;
|
def entry : P<"entry", "Name of entry point symbol">;
|
||||||
@ -34,7 +34,8 @@ def export : P<"export", "Export a function">;
|
|||||||
def failifmismatch : P<"failifmismatch", "">;
|
def failifmismatch : P<"failifmismatch", "">;
|
||||||
def filealign : P<"filealign", "Section alignment in the output file">;
|
def filealign : P<"filealign", "Section alignment in the output file">;
|
||||||
def functionpadmin : F<"functionpadmin">;
|
def functionpadmin : F<"functionpadmin">;
|
||||||
def functionpadmin_opt : P<"functionpadmin", "Prepares an image for hotpatching">;
|
def functionpadmin_opt : P<"functionpadmin",
|
||||||
|
"Prepares an image for hotpatching">;
|
||||||
def guard : P<"guard", "Control flow guard">;
|
def guard : P<"guard", "Control flow guard">;
|
||||||
def heap : P<"heap", "Size of the heap">;
|
def heap : P<"heap", "Size of the heap">;
|
||||||
def ignore : P<"ignore", "Specify warning codes to ignore">;
|
def ignore : P<"ignore", "Specify warning codes to ignore">;
|
||||||
@ -42,9 +43,14 @@ def implib : P<"implib", "Import library name">;
|
|||||||
def lib : F<"lib">,
|
def lib : F<"lib">,
|
||||||
HelpText<"Act like lib.exe; must be first argument if present">;
|
HelpText<"Act like lib.exe; must be first argument if present">;
|
||||||
def libpath : P<"libpath", "Additional library search path">;
|
def libpath : P<"libpath", "Additional library search path">;
|
||||||
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
|
def linkrepro : P<"linkrepro",
|
||||||
def lldltocache : P<"lldltocache", "Path to ThinLTO cached object file directory">;
|
"Dump linker invocation and input files for debugging">;
|
||||||
def lldltocachepolicy : P<"lldltocachepolicy", "Pruning policy for the ThinLTO cache">;
|
def lldignoreenv : F<"lldignoreenv">,
|
||||||
|
HelpText<"Ignore environment variables like %LIB%">;
|
||||||
|
def lldltocache : P<"lldltocache",
|
||||||
|
"Path to ThinLTO cached object file directory">;
|
||||||
|
def lldltocachepolicy : P<"lldltocachepolicy",
|
||||||
|
"Pruning policy for the ThinLTO cache">;
|
||||||
def lldsavetemps : F<"lldsavetemps">,
|
def lldsavetemps : F<"lldsavetemps">,
|
||||||
HelpText<"Save temporary files instead of deleting them">;
|
HelpText<"Save temporary files instead of deleting them">;
|
||||||
def machine : P<"machine", "Specify target platform">;
|
def machine : P<"machine", "Specify target platform">;
|
||||||
@ -56,7 +62,7 @@ def order : P<"order", "Put functions in order">;
|
|||||||
def out : P<"out", "Path to file to write output">;
|
def out : P<"out", "Path to file to write output">;
|
||||||
def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
|
def natvis : P<"natvis", "Path to natvis file to embed in the PDB">;
|
||||||
def no_color_diagnostics: F<"no-color-diagnostics">,
|
def no_color_diagnostics: F<"no-color-diagnostics">,
|
||||||
HelpText<"Do not use colors in diagnostics">;
|
HelpText<"Do not use colors in diagnostics">;
|
||||||
def pdb : P<"pdb", "PDB file path">;
|
def pdb : P<"pdb", "PDB file path">;
|
||||||
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
|
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
|
||||||
def section : P<"section", "Specify section attributes">;
|
def section : P<"section", "Specify section attributes">;
|
||||||
@ -65,7 +71,8 @@ def stub : P<"stub", "Specify DOS stub file">;
|
|||||||
def subsystem : P<"subsystem", "Specify subsystem">;
|
def subsystem : P<"subsystem", "Specify subsystem">;
|
||||||
def timestamp : P<"timestamp", "Specify the PE header timestamp">;
|
def timestamp : P<"timestamp", "Specify the PE header timestamp">;
|
||||||
def version : P<"version", "Specify a version number in the PE header">;
|
def version : P<"version", "Specify a version number in the PE header">;
|
||||||
def wholearchive_file : P<"wholearchive", "Include all object files from this archive">;
|
def wholearchive_file : P<"wholearchive",
|
||||||
|
"Include all object files from this library">;
|
||||||
|
|
||||||
def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
|
def disallowlib : Joined<["/", "-", "/?", "-?"], "disallowlib:">,
|
||||||
Alias<nodefaultlib>;
|
Alias<nodefaultlib>;
|
||||||
@ -105,6 +112,8 @@ def noentry : F<"noentry">,
|
|||||||
def profile : F<"profile">;
|
def profile : F<"profile">;
|
||||||
def repro : F<"Brepro">,
|
def repro : F<"Brepro">,
|
||||||
HelpText<"Use a hash of the executable as the PE header timestamp">;
|
HelpText<"Use a hash of the executable as the PE header timestamp">;
|
||||||
|
def reproduce : P<"reproduce",
|
||||||
|
"Dump linker invocation and input files for debugging">;
|
||||||
def swaprun : P<"swaprun",
|
def swaprun : P<"swaprun",
|
||||||
"Comma-separated list of 'cd' or 'net'">;
|
"Comma-separated list of 'cd' or 'net'">;
|
||||||
def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
|
def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
|
||||||
@ -112,10 +121,11 @@ def swaprun_cd : F<"swaprun:cd">, Alias<swaprun>, AliasArgs<["cd"]>,
|
|||||||
def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
|
def swaprun_net : F<"swaprun:net">, Alias<swaprun>, AliasArgs<["net"]>,
|
||||||
HelpText<"Make loader run output binary from swap instead of from network">;
|
HelpText<"Make loader run output binary from swap instead of from network">;
|
||||||
def verbose : F<"verbose">;
|
def verbose : F<"verbose">;
|
||||||
def wholearchive_flag : F<"wholearchive">;
|
def wholearchive_flag : F<"wholearchive">,
|
||||||
|
HelpText<"Include all object files from all libraries">;
|
||||||
|
|
||||||
def force : F<"force">,
|
def force : F<"force">,
|
||||||
HelpText<"Allow undefined and multiply defined symbols when creating executables">;
|
HelpText<"Allow undefined and multiply defined symbols">;
|
||||||
def force_unresolved : F<"force:unresolved">,
|
def force_unresolved : F<"force:unresolved">,
|
||||||
HelpText<"Allow undefined symbols when creating executables">;
|
HelpText<"Allow undefined symbols when creating executables">;
|
||||||
def force_multiple : F<"force:multiple">,
|
def force_multiple : F<"force:multiple">,
|
||||||
@ -162,6 +172,8 @@ def help : F<"help">;
|
|||||||
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
|
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
|
||||||
|
|
||||||
// LLD extensions
|
// LLD extensions
|
||||||
|
def end_lib : F<"end-lib">,
|
||||||
|
HelpText<"Ends group of objects treated as if they were in a library">;
|
||||||
def exclude_all_symbols : F<"exclude-all-symbols">;
|
def exclude_all_symbols : F<"exclude-all-symbols">;
|
||||||
def export_all_symbols : F<"export-all-symbols">;
|
def export_all_symbols : F<"export-all-symbols">;
|
||||||
defm demangle : B<"demangle",
|
defm demangle : B<"demangle",
|
||||||
@ -173,9 +185,11 @@ def kill_at : F<"kill-at">;
|
|||||||
def lldmingw : F<"lldmingw">;
|
def lldmingw : F<"lldmingw">;
|
||||||
def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
|
def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
|
||||||
def pdb_source_path : P<"pdbsourcepath",
|
def pdb_source_path : P<"pdbsourcepath",
|
||||||
"Base path used to make relative source file path absolute in PDB">;
|
"Base path used to make relative source file path absolute in PDB">;
|
||||||
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
|
def rsp_quoting : Joined<["--"], "rsp-quoting=">,
|
||||||
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
|
HelpText<"Quoting style for response files, 'windows' (default) or 'posix'">;
|
||||||
|
def start_lib : F<"start-lib">,
|
||||||
|
HelpText<"Starts group of objects treated as if they were in a library">;
|
||||||
def thinlto_emit_imports_files :
|
def thinlto_emit_imports_files :
|
||||||
F<"thinlto-emit-imports-files">,
|
F<"thinlto-emit-imports-files">,
|
||||||
HelpText<"Emit .imports files with -thinlto-index-only">;
|
HelpText<"Emit .imports files with -thinlto-index-only">;
|
||||||
@ -191,6 +205,9 @@ def thinlto_object_suffix_replace : P<
|
|||||||
def thinlto_prefix_replace: P<
|
def thinlto_prefix_replace: P<
|
||||||
"thinlto-prefix-replace",
|
"thinlto-prefix-replace",
|
||||||
"'old;new' replace old prefix with new prefix in ThinLTO outputs">;
|
"'old;new' replace old prefix with new prefix in ThinLTO outputs">;
|
||||||
|
def lto_obj_path : P<
|
||||||
|
"lto-obj-path",
|
||||||
|
"output native object for merged LTO unit to this path">;
|
||||||
def dash_dash_version : Flag<["--"], "version">,
|
def dash_dash_version : Flag<["--"], "version">,
|
||||||
HelpText<"Print version information">;
|
HelpText<"Print version information">;
|
||||||
defm threads: B<"threads",
|
defm threads: B<"threads",
|
||||||
|
@ -51,21 +51,22 @@
|
|||||||
#include "llvm/Object/COFF.h"
|
#include "llvm/Object/COFF.h"
|
||||||
#include "llvm/Object/CVDebugRecord.h"
|
#include "llvm/Object/CVDebugRecord.h"
|
||||||
#include "llvm/Support/BinaryByteStream.h"
|
#include "llvm/Support/BinaryByteStream.h"
|
||||||
|
#include "llvm/Support/CRC.h"
|
||||||
#include "llvm/Support/Endian.h"
|
#include "llvm/Support/Endian.h"
|
||||||
#include "llvm/Support/Errc.h"
|
#include "llvm/Support/Errc.h"
|
||||||
#include "llvm/Support/FormatVariadic.h"
|
#include "llvm/Support/FormatVariadic.h"
|
||||||
#include "llvm/Support/JamCRC.h"
|
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/ScopedPrinter.h"
|
#include "llvm/Support/ScopedPrinter.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::coff;
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::codeview;
|
using namespace llvm::codeview;
|
||||||
|
|
||||||
using llvm::object::coff_section;
|
using llvm::object::coff_section;
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace coff {
|
||||||
|
|
||||||
static ExitOnError exitOnErr;
|
static ExitOnError exitOnErr;
|
||||||
|
|
||||||
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
|
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
|
||||||
@ -513,16 +514,15 @@ static bool equals_path(StringRef path1, StringRef path2) {
|
|||||||
return path1.equals(path2);
|
return path1.equals(path2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find by name an OBJ provided on the command line
|
// Find by name an OBJ provided on the command line
|
||||||
static ObjFile *findObjByName(StringRef fileNameOnly) {
|
static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly,
|
||||||
SmallString<128> currentPath;
|
uint32_t precompSignature) {
|
||||||
|
|
||||||
for (ObjFile *f : ObjFile::instances) {
|
for (ObjFile *f : ObjFile::instances) {
|
||||||
StringRef currentFileName = sys::path::filename(f->getName());
|
StringRef currentFileName = sys::path::filename(f->getName());
|
||||||
|
|
||||||
// Compare based solely on the file name (link.exe behavior)
|
if (f->pchSignature.hasValue() &&
|
||||||
if (equals_path(currentFileName, fileNameOnly))
|
f->pchSignature.getValue() == precompSignature &&
|
||||||
|
equals_path(fileNameOnly, currentFileName))
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -559,22 +559,15 @@ Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) {
|
|||||||
|
|
||||||
// link.exe requires that a precompiled headers object must always be provided
|
// link.exe requires that a precompiled headers object must always be provided
|
||||||
// on the command-line, even if that's not necessary.
|
// on the command-line, even if that's not necessary.
|
||||||
auto precompFile = findObjByName(precompFileName);
|
auto precompFile =
|
||||||
|
findObjWithPrecompSignature(precompFileName, precomp.Signature);
|
||||||
if (!precompFile)
|
if (!precompFile)
|
||||||
return createFileError(
|
return createFileError(
|
||||||
precompFileName.str(),
|
precomp.getPrecompFilePath().str(),
|
||||||
make_error<pdb::PDBError>(pdb::pdb_error_code::external_cmdline_ref));
|
make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
|
||||||
|
|
||||||
addObjFile(precompFile, &indexMap);
|
addObjFile(precompFile, &indexMap);
|
||||||
|
|
||||||
if (!precompFile->pchSignature)
|
|
||||||
fatal(precompFile->getName() + " is not a precompiled headers object");
|
|
||||||
|
|
||||||
if (precomp.getSignature() != precompFile->pchSignature.getValueOr(0))
|
|
||||||
return createFileError(
|
|
||||||
precomp.getPrecompFilePath().str(),
|
|
||||||
make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
|
|
||||||
|
|
||||||
return indexMap;
|
return indexMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -965,9 +958,7 @@ static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
|
|||||||
sc.Imod = secChunk->file->moduleDBI->getModuleIndex();
|
sc.Imod = secChunk->file->moduleDBI->getModuleIndex();
|
||||||
ArrayRef<uint8_t> contents = secChunk->getContents();
|
ArrayRef<uint8_t> contents = secChunk->getContents();
|
||||||
JamCRC crc(0);
|
JamCRC crc(0);
|
||||||
ArrayRef<char> charContents = makeArrayRef(
|
crc.update(contents);
|
||||||
reinterpret_cast<const char *>(contents.data()), contents.size());
|
|
||||||
crc.update(charContents);
|
|
||||||
sc.DataCrc = crc.getCRC();
|
sc.DataCrc = crc.getCRC();
|
||||||
} else {
|
} else {
|
||||||
sc.Characteristics = os ? os->header.Characteristics : 0;
|
sc.Characteristics = os ? os->header.Characteristics : 0;
|
||||||
@ -1150,7 +1141,7 @@ void DebugSHandler::finish() {
|
|||||||
// string table. Generally the string table subsection appears after the
|
// string table. Generally the string table subsection appears after the
|
||||||
// checksum table, so we have to do this after looping over all the
|
// checksum table, so we have to do this after looping over all the
|
||||||
// subsections.
|
// subsections.
|
||||||
auto newChecksums = make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
|
auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
|
||||||
for (FileChecksumEntry &fc : checksums) {
|
for (FileChecksumEntry &fc : checksums) {
|
||||||
SmallString<128> filename =
|
SmallString<128> filename =
|
||||||
exitOnErr(cVStrTab.getString(fc.FileNameOffset));
|
exitOnErr(cVStrTab.getString(fc.FileNameOffset));
|
||||||
@ -1599,7 +1590,7 @@ void PDBLinker::addImportFilesToPDB(ArrayRef<OutputSection *> outputSections) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creates a PDB file.
|
// Creates a PDB file.
|
||||||
void coff::createPDB(SymbolTable *symtab,
|
void createPDB(SymbolTable *symtab,
|
||||||
ArrayRef<OutputSection *> outputSections,
|
ArrayRef<OutputSection *> outputSections,
|
||||||
ArrayRef<uint8_t> sectionTable,
|
ArrayRef<uint8_t> sectionTable,
|
||||||
llvm::codeview::DebugInfo *buildId) {
|
llvm::codeview::DebugInfo *buildId) {
|
||||||
@ -1693,6 +1684,7 @@ void PDBLinker::addSections(ArrayRef<OutputSection *> outputSections,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PDBLinker::commit(codeview::GUID *guid) {
|
void PDBLinker::commit(codeview::GUID *guid) {
|
||||||
|
ExitOnError exitOnErr((config->pdbPath + ": ").str());
|
||||||
// Write to a file.
|
// Write to a file.
|
||||||
exitOnErr(builder.commit(config->pdbPath, guid));
|
exitOnErr(builder.commit(config->pdbPath, guid));
|
||||||
}
|
}
|
||||||
@ -1797,10 +1789,10 @@ static bool findLineTable(const SectionChunk *c, uint32_t addr,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use CodeView line tables to resolve a file and line number for the given
|
// Use CodeView line tables to resolve a file and line number for the given
|
||||||
// offset into the given chunk and return them, or {"", 0} if a line table was
|
// offset into the given chunk and return them, or None if a line table was
|
||||||
// not found.
|
// not found.
|
||||||
std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
|
Optional<std::pair<StringRef, uint32_t>>
|
||||||
uint32_t addr) {
|
getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
|
||||||
ExitOnError exitOnErr;
|
ExitOnError exitOnErr;
|
||||||
|
|
||||||
DebugStringTableSubsectionRef cVStrTab;
|
DebugStringTableSubsectionRef cVStrTab;
|
||||||
@ -1809,7 +1801,7 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
|
|||||||
uint32_t offsetInLinetable;
|
uint32_t offsetInLinetable;
|
||||||
|
|
||||||
if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
|
if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
|
||||||
return {"", 0};
|
return None;
|
||||||
|
|
||||||
Optional<uint32_t> nameIndex;
|
Optional<uint32_t> nameIndex;
|
||||||
Optional<uint32_t> lineNumber;
|
Optional<uint32_t> lineNumber;
|
||||||
@ -1823,14 +1815,17 @@ std::pair<StringRef, uint32_t> coff::getFileLine(const SectionChunk *c,
|
|||||||
}
|
}
|
||||||
StringRef filename =
|
StringRef filename =
|
||||||
exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
|
exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
|
||||||
return {filename, *lineNumber};
|
return std::make_pair(filename, *lineNumber);
|
||||||
}
|
}
|
||||||
nameIndex = entry.NameIndex;
|
nameIndex = entry.NameIndex;
|
||||||
lineNumber = li.getStartLine();
|
lineNumber = li.getStartLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!nameIndex)
|
if (!nameIndex)
|
||||||
return {"", 0};
|
return None;
|
||||||
StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
|
StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
|
||||||
return {filename, *lineNumber};
|
return std::make_pair(filename, *lineNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace coff
|
||||||
|
} // namespace lld
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#define LLD_COFF_PDB_H
|
#define LLD_COFF_PDB_H
|
||||||
|
|
||||||
#include "llvm/ADT/ArrayRef.h"
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
|
#include "llvm/ADT/Optional.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
@ -29,9 +30,9 @@ void createPDB(SymbolTable *symtab,
|
|||||||
llvm::ArrayRef<uint8_t> sectionTable,
|
llvm::ArrayRef<uint8_t> sectionTable,
|
||||||
llvm::codeview::DebugInfo *buildId);
|
llvm::codeview::DebugInfo *buildId);
|
||||||
|
|
||||||
std::pair<llvm::StringRef, uint32_t> getFileLine(const SectionChunk *c,
|
llvm::Optional<std::pair<llvm::StringRef, uint32_t>>
|
||||||
uint32_t addr);
|
getFileLineCodeView(const SectionChunk *c, uint32_t addr);
|
||||||
}
|
} // namespace coff
|
||||||
}
|
} // namespace lld
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "lld/Common/ErrorHandler.h"
|
#include "lld/Common/ErrorHandler.h"
|
||||||
#include "lld/Common/Memory.h"
|
#include "lld/Common/Memory.h"
|
||||||
#include "lld/Common/Timer.h"
|
#include "lld/Common/Timer.h"
|
||||||
|
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
|
||||||
#include "llvm/IR/LLVMContext.h"
|
#include "llvm/IR/LLVMContext.h"
|
||||||
#include "llvm/Object/WindowsMachineFlag.h"
|
#include "llvm/Object/WindowsMachineFlag.h"
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
@ -61,6 +62,24 @@ static void errorOrWarn(const Twine &s) {
|
|||||||
error(s);
|
error(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Causes the file associated with a lazy symbol to be linked in.
|
||||||
|
static void forceLazy(Symbol *s) {
|
||||||
|
s->pendingArchiveLoad = true;
|
||||||
|
switch (s->kind()) {
|
||||||
|
case Symbol::Kind::LazyArchiveKind: {
|
||||||
|
auto *l = cast<LazyArchive>(s);
|
||||||
|
l->file->addMember(l->sym);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Symbol::Kind::LazyObjectKind:
|
||||||
|
cast<LazyObject>(s)->file->fetch();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
llvm_unreachable(
|
||||||
|
"symbol passed to forceLazy is not a LazyArchive or LazyObject");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
|
// Returns the symbol in SC whose value is <= Addr that is closest to Addr.
|
||||||
// This is generally the global variable or function whose definition contains
|
// This is generally the global variable or function whose definition contains
|
||||||
// Addr.
|
// Addr.
|
||||||
@ -69,7 +88,8 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
|
|||||||
|
|
||||||
for (Symbol *s : sc->file->getSymbols()) {
|
for (Symbol *s : sc->file->getSymbols()) {
|
||||||
auto *d = dyn_cast_or_null<DefinedRegular>(s);
|
auto *d = dyn_cast_or_null<DefinedRegular>(s);
|
||||||
if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr ||
|
if (!d || !d->data || d->file != sc->file || d->getChunk() != sc ||
|
||||||
|
d->getValue() > addr ||
|
||||||
(candidate && d->getValue() < candidate->getValue()))
|
(candidate && d->getValue() < candidate->getValue()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -79,6 +99,38 @@ static Symbol *getSymbol(SectionChunk *sc, uint32_t addr) {
|
|||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> getSymbolLocations(BitcodeFile *file) {
|
||||||
|
std::string res("\n>>> referenced by ");
|
||||||
|
StringRef source = file->obj->getSourceFileName();
|
||||||
|
if (!source.empty())
|
||||||
|
res += source.str() + "\n>>> ";
|
||||||
|
res += toString(file);
|
||||||
|
return {res};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Optional<std::pair<StringRef, uint32_t>>
|
||||||
|
getFileLineDwarf(const SectionChunk *c, uint32_t addr) {
|
||||||
|
Optional<DILineInfo> optionalLineInfo =
|
||||||
|
c->file->getDILineInfo(addr, c->getSectionNumber() - 1);
|
||||||
|
if (!optionalLineInfo)
|
||||||
|
return None;
|
||||||
|
const DILineInfo &lineInfo = *optionalLineInfo;
|
||||||
|
if (lineInfo.FileName == DILineInfo::BadString)
|
||||||
|
return None;
|
||||||
|
return std::make_pair(saver.save(lineInfo.FileName), lineInfo.Line);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Optional<std::pair<StringRef, uint32_t>>
|
||||||
|
getFileLine(const SectionChunk *c, uint32_t addr) {
|
||||||
|
// MinGW can optionally use codeview, even if the default is dwarf.
|
||||||
|
Optional<std::pair<StringRef, uint32_t>> fileLine =
|
||||||
|
getFileLineCodeView(c, addr);
|
||||||
|
// If codeview didn't yield any result, check dwarf in MinGW mode.
|
||||||
|
if (!fileLine && config->mingw)
|
||||||
|
fileLine = getFileLineDwarf(c, addr);
|
||||||
|
return fileLine;
|
||||||
|
}
|
||||||
|
|
||||||
// Given a file and the index of a symbol in that file, returns a description
|
// Given a file and the index of a symbol in that file, returns a description
|
||||||
// of all references to that symbol from that file. If no debug information is
|
// of all references to that symbol from that file. If no debug information is
|
||||||
// available, returns just the name of the file, else one string per actual
|
// available, returns just the name of the file, else one string per actual
|
||||||
@ -97,11 +149,13 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
|
|||||||
for (const coff_relocation &r : sc->getRelocs()) {
|
for (const coff_relocation &r : sc->getRelocs()) {
|
||||||
if (r.SymbolTableIndex != symIndex)
|
if (r.SymbolTableIndex != symIndex)
|
||||||
continue;
|
continue;
|
||||||
std::pair<StringRef, uint32_t> fileLine =
|
Optional<std::pair<StringRef, uint32_t>> fileLine =
|
||||||
getFileLine(sc, r.VirtualAddress);
|
getFileLine(sc, r.VirtualAddress);
|
||||||
Symbol *sym = getSymbol(sc, r.VirtualAddress);
|
Symbol *sym = getSymbol(sc, r.VirtualAddress);
|
||||||
if (!fileLine.first.empty() || sym)
|
if (fileLine)
|
||||||
locations.push_back({sym, fileLine});
|
locations.push_back({sym, *fileLine});
|
||||||
|
else if (sym)
|
||||||
|
locations.push_back({sym, {"", 0}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,13 +177,23 @@ std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
|
|||||||
return symbolLocations;
|
return symbolLocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> getSymbolLocations(InputFile *file,
|
||||||
|
uint32_t symIndex) {
|
||||||
|
if (auto *o = dyn_cast<ObjFile>(file))
|
||||||
|
return getSymbolLocations(o, symIndex);
|
||||||
|
if (auto *b = dyn_cast<BitcodeFile>(file))
|
||||||
|
return getSymbolLocations(b);
|
||||||
|
llvm_unreachable("unsupported file type passed to getSymbolLocations");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// For an undefined symbol, stores all files referencing it and the index of
|
// For an undefined symbol, stores all files referencing it and the index of
|
||||||
// the undefined symbol in each file.
|
// the undefined symbol in each file.
|
||||||
struct UndefinedDiag {
|
struct UndefinedDiag {
|
||||||
Symbol *sym;
|
Symbol *sym;
|
||||||
struct File {
|
struct File {
|
||||||
ObjFile *oFile;
|
InputFile *file;
|
||||||
uint64_t symIndex;
|
uint32_t symIndex;
|
||||||
};
|
};
|
||||||
std::vector<File> files;
|
std::vector<File> files;
|
||||||
};
|
};
|
||||||
@ -143,7 +207,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
|
|||||||
size_t i = 0, numRefs = 0;
|
size_t i = 0, numRefs = 0;
|
||||||
for (const UndefinedDiag::File &ref : undefDiag.files) {
|
for (const UndefinedDiag::File &ref : undefDiag.files) {
|
||||||
std::vector<std::string> symbolLocations =
|
std::vector<std::string> symbolLocations =
|
||||||
getSymbolLocations(ref.oFile, ref.symIndex);
|
getSymbolLocations(ref.file, ref.symIndex);
|
||||||
numRefs += symbolLocations.size();
|
numRefs += symbolLocations.size();
|
||||||
for (const std::string &s : symbolLocations) {
|
for (const std::string &s : symbolLocations) {
|
||||||
if (i >= maxUndefReferences)
|
if (i >= maxUndefReferences)
|
||||||
@ -165,28 +229,33 @@ void SymbolTable::loadMinGWAutomaticImports() {
|
|||||||
continue;
|
continue;
|
||||||
if (!sym->isUsedInRegularObj)
|
if (!sym->isUsedInRegularObj)
|
||||||
continue;
|
continue;
|
||||||
|
if (undef->getWeakAlias())
|
||||||
|
continue;
|
||||||
|
|
||||||
StringRef name = undef->getName();
|
StringRef name = undef->getName();
|
||||||
|
|
||||||
if (name.startswith("__imp_"))
|
if (name.startswith("__imp_"))
|
||||||
continue;
|
continue;
|
||||||
// If we have an undefined symbol, but we have a Lazy representing a
|
// If we have an undefined symbol, but we have a lazy symbol we could
|
||||||
// symbol we could load from file, make sure to load that.
|
// load, load it.
|
||||||
Lazy *l = dyn_cast_or_null<Lazy>(find(("__imp_" + name).str()));
|
Symbol *l = find(("__imp_" + name).str());
|
||||||
if (!l || l->pendingArchiveLoad)
|
if (!l || l->pendingArchiveLoad || !l->isLazy())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
log("Loading lazy " + l->getName() + " from " + l->file->getName() +
|
log("Loading lazy " + l->getName() + " from " + l->getFile()->getName() +
|
||||||
" for automatic import");
|
" for automatic import");
|
||||||
l->pendingArchiveLoad = true;
|
forceLazy(l);
|
||||||
l->file->addMember(l->sym);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
Defined *SymbolTable::impSymbol(StringRef name) {
|
||||||
if (name.startswith("__imp_"))
|
if (name.startswith("__imp_"))
|
||||||
return false;
|
return nullptr;
|
||||||
Defined *imp = dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
|
return dyn_cast_or_null<Defined>(find(("__imp_" + name).str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
||||||
|
Defined *imp = impSymbol(name);
|
||||||
if (!imp)
|
if (!imp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -232,7 +301,97 @@ bool SymbolTable::handleMinGWAutomaticImport(Symbol *sym, StringRef name) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolTable::reportRemainingUndefines() {
|
/// Helper function for reportUnresolvable and resolveRemainingUndefines.
|
||||||
|
/// This function emits an "undefined symbol" diagnostic for each symbol in
|
||||||
|
/// undefs. If localImports is not nullptr, it also emits a "locally
|
||||||
|
/// defined symbol imported" diagnostic for symbols in localImports.
|
||||||
|
/// objFiles and bitcodeFiles (if not nullptr) are used to report where
|
||||||
|
/// undefined symbols are referenced.
|
||||||
|
static void
|
||||||
|
reportProblemSymbols(const SmallPtrSetImpl<Symbol *> &undefs,
|
||||||
|
const DenseMap<Symbol *, Symbol *> *localImports,
|
||||||
|
const std::vector<ObjFile *> objFiles,
|
||||||
|
const std::vector<BitcodeFile *> *bitcodeFiles) {
|
||||||
|
|
||||||
|
// Return early if there is nothing to report (which should be
|
||||||
|
// the common case).
|
||||||
|
if (undefs.empty() && (!localImports || localImports->empty()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (Symbol *b : config->gcroot) {
|
||||||
|
if (undefs.count(b))
|
||||||
|
errorOrWarn("<root>: undefined symbol: " + toString(*b));
|
||||||
|
if (localImports)
|
||||||
|
if (Symbol *imp = localImports->lookup(b))
|
||||||
|
warn("<root>: locally defined symbol imported: " + toString(*imp) +
|
||||||
|
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UndefinedDiag> undefDiags;
|
||||||
|
DenseMap<Symbol *, int> firstDiag;
|
||||||
|
|
||||||
|
auto processFile = [&](InputFile *file, ArrayRef<Symbol *> symbols) {
|
||||||
|
uint32_t symIndex = (uint32_t)-1;
|
||||||
|
for (Symbol *sym : symbols) {
|
||||||
|
++symIndex;
|
||||||
|
if (!sym)
|
||||||
|
continue;
|
||||||
|
if (undefs.count(sym)) {
|
||||||
|
auto it = firstDiag.find(sym);
|
||||||
|
if (it == firstDiag.end()) {
|
||||||
|
firstDiag[sym] = undefDiags.size();
|
||||||
|
undefDiags.push_back({sym, {{file, symIndex}}});
|
||||||
|
} else {
|
||||||
|
undefDiags[it->second].files.push_back({file, symIndex});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (localImports)
|
||||||
|
if (Symbol *imp = localImports->lookup(sym))
|
||||||
|
warn(toString(file) +
|
||||||
|
": locally defined symbol imported: " + toString(*imp) +
|
||||||
|
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (ObjFile *file : objFiles)
|
||||||
|
processFile(file, file->getSymbols());
|
||||||
|
|
||||||
|
if (bitcodeFiles)
|
||||||
|
for (BitcodeFile *file : *bitcodeFiles)
|
||||||
|
processFile(file, file->getSymbols());
|
||||||
|
|
||||||
|
for (const UndefinedDiag &undefDiag : undefDiags)
|
||||||
|
reportUndefinedSymbol(undefDiag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolTable::reportUnresolvable() {
|
||||||
|
SmallPtrSet<Symbol *, 8> undefs;
|
||||||
|
for (auto &i : symMap) {
|
||||||
|
Symbol *sym = i.second;
|
||||||
|
auto *undef = dyn_cast<Undefined>(sym);
|
||||||
|
if (!undef)
|
||||||
|
continue;
|
||||||
|
if (undef->getWeakAlias())
|
||||||
|
continue;
|
||||||
|
StringRef name = undef->getName();
|
||||||
|
if (name.startswith("__imp_")) {
|
||||||
|
Symbol *imp = find(name.substr(strlen("__imp_")));
|
||||||
|
if (imp && isa<Defined>(imp))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (name.contains("_PchSym_"))
|
||||||
|
continue;
|
||||||
|
if (config->mingw && impSymbol(name))
|
||||||
|
continue;
|
||||||
|
undefs.insert(sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
reportProblemSymbols(undefs,
|
||||||
|
/* localImports */ nullptr, ObjFile::instances,
|
||||||
|
&BitcodeFile::instances);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymbolTable::resolveRemainingUndefines() {
|
||||||
SmallPtrSet<Symbol *, 8> undefs;
|
SmallPtrSet<Symbol *, 8> undefs;
|
||||||
DenseMap<Symbol *, Symbol *> localImports;
|
DenseMap<Symbol *, Symbol *> localImports;
|
||||||
|
|
||||||
@ -290,46 +449,9 @@ void SymbolTable::reportRemainingUndefines() {
|
|||||||
undefs.insert(sym);
|
undefs.insert(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (undefs.empty() && localImports.empty())
|
reportProblemSymbols(
|
||||||
return;
|
undefs, config->warnLocallyDefinedImported ? &localImports : nullptr,
|
||||||
|
ObjFile::instances, /* bitcode files no longer needed */ nullptr);
|
||||||
for (Symbol *b : config->gcroot) {
|
|
||||||
if (undefs.count(b))
|
|
||||||
errorOrWarn("<root>: undefined symbol: " + toString(*b));
|
|
||||||
if (config->warnLocallyDefinedImported)
|
|
||||||
if (Symbol *imp = localImports.lookup(b))
|
|
||||||
warn("<root>: locally defined symbol imported: " + toString(*imp) +
|
|
||||||
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<UndefinedDiag> undefDiags;
|
|
||||||
DenseMap<Symbol *, int> firstDiag;
|
|
||||||
|
|
||||||
for (ObjFile *file : ObjFile::instances) {
|
|
||||||
size_t symIndex = (size_t)-1;
|
|
||||||
for (Symbol *sym : file->getSymbols()) {
|
|
||||||
++symIndex;
|
|
||||||
if (!sym)
|
|
||||||
continue;
|
|
||||||
if (undefs.count(sym)) {
|
|
||||||
auto it = firstDiag.find(sym);
|
|
||||||
if (it == firstDiag.end()) {
|
|
||||||
firstDiag[sym] = undefDiags.size();
|
|
||||||
undefDiags.push_back({sym, {{file, symIndex}}});
|
|
||||||
} else {
|
|
||||||
undefDiags[it->second].files.push_back({file, symIndex});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (config->warnLocallyDefinedImported)
|
|
||||||
if (Symbol *imp = localImports.lookup(sym))
|
|
||||||
warn(toString(file) +
|
|
||||||
": locally defined symbol imported: " + toString(*imp) +
|
|
||||||
" (defined in " + toString(imp->getFile()) + ") [LNK4217]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const UndefinedDiag& undefDiag : undefDiags)
|
|
||||||
reportUndefinedSymbol(undefDiag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
|
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
|
||||||
@ -356,26 +478,22 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
|
|||||||
Symbol *s;
|
Symbol *s;
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(name, f);
|
std::tie(s, wasInserted) = insert(name, f);
|
||||||
if (wasInserted || (isa<Lazy>(s) && isWeakAlias)) {
|
if (wasInserted || (s->isLazy() && isWeakAlias)) {
|
||||||
replaceSymbol<Undefined>(s, name);
|
replaceSymbol<Undefined>(s, name);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
if (auto *l = dyn_cast<Lazy>(s)) {
|
if (s->isLazy())
|
||||||
if (!s->pendingArchiveLoad) {
|
forceLazy(s);
|
||||||
s->pendingArchiveLoad = true;
|
|
||||||
l->file->addMember(l->sym);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) {
|
void SymbolTable::addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym) {
|
||||||
StringRef name = sym.getName();
|
StringRef name = sym.getName();
|
||||||
Symbol *s;
|
Symbol *s;
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(name);
|
std::tie(s, wasInserted) = insert(name);
|
||||||
if (wasInserted) {
|
if (wasInserted) {
|
||||||
replaceSymbol<Lazy>(s, f, sym);
|
replaceSymbol<LazyArchive>(s, f, sym);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto *u = dyn_cast<Undefined>(s);
|
auto *u = dyn_cast<Undefined>(s);
|
||||||
@ -385,15 +503,84 @@ void SymbolTable::addLazy(ArchiveFile *f, const Archive::Symbol &sym) {
|
|||||||
f->addMember(sym);
|
f->addMember(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile) {
|
void SymbolTable::addLazyObject(LazyObjFile *f, StringRef n) {
|
||||||
std::string msg = "duplicate symbol: " + toString(*existing) + " in " +
|
Symbol *s;
|
||||||
toString(existing->getFile()) + " and in " +
|
bool wasInserted;
|
||||||
toString(newFile);
|
std::tie(s, wasInserted) = insert(n, f);
|
||||||
|
if (wasInserted) {
|
||||||
|
replaceSymbol<LazyObject>(s, f, n);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto *u = dyn_cast<Undefined>(s);
|
||||||
|
if (!u || u->weakAlias || s->pendingArchiveLoad)
|
||||||
|
return;
|
||||||
|
s->pendingArchiveLoad = true;
|
||||||
|
f->fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getSourceLocationBitcode(BitcodeFile *file) {
|
||||||
|
std::string res("\n>>> defined at ");
|
||||||
|
StringRef source = file->obj->getSourceFileName();
|
||||||
|
if (!source.empty())
|
||||||
|
res += source.str() + "\n>>> ";
|
||||||
|
res += toString(file);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getSourceLocationObj(ObjFile *file, SectionChunk *sc,
|
||||||
|
uint32_t offset, StringRef name) {
|
||||||
|
Optional<std::pair<StringRef, uint32_t>> fileLine;
|
||||||
|
if (sc)
|
||||||
|
fileLine = getFileLine(sc, offset);
|
||||||
|
if (!fileLine)
|
||||||
|
fileLine = file->getVariableLocation(name);
|
||||||
|
|
||||||
|
std::string res;
|
||||||
|
llvm::raw_string_ostream os(res);
|
||||||
|
os << "\n>>> defined at ";
|
||||||
|
if (fileLine)
|
||||||
|
os << fileLine->first << ":" << fileLine->second << "\n>>> ";
|
||||||
|
os << toString(file);
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string getSourceLocation(InputFile *file, SectionChunk *sc,
|
||||||
|
uint32_t offset, StringRef name) {
|
||||||
|
if (auto *o = dyn_cast<ObjFile>(file))
|
||||||
|
return getSourceLocationObj(o, sc, offset, name);
|
||||||
|
if (auto *b = dyn_cast<BitcodeFile>(file))
|
||||||
|
return getSourceLocationBitcode(b);
|
||||||
|
return "\n>>> defined at " + toString(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct and print an error message in the form of:
|
||||||
|
//
|
||||||
|
// lld-link: error: duplicate symbol: foo
|
||||||
|
// >>> defined at bar.c:30
|
||||||
|
// >>> bar.o
|
||||||
|
// >>> defined at baz.c:563
|
||||||
|
// >>> baz.o
|
||||||
|
void SymbolTable::reportDuplicate(Symbol *existing, InputFile *newFile,
|
||||||
|
SectionChunk *newSc,
|
||||||
|
uint32_t newSectionOffset) {
|
||||||
|
std::string msg;
|
||||||
|
llvm::raw_string_ostream os(msg);
|
||||||
|
os << "duplicate symbol: " << toString(*existing);
|
||||||
|
|
||||||
|
DefinedRegular *d = cast<DefinedRegular>(existing);
|
||||||
|
if (d && isa<ObjFile>(d->getFile())) {
|
||||||
|
os << getSourceLocation(d->getFile(), d->getChunk(), d->getValue(),
|
||||||
|
existing->getName());
|
||||||
|
} else {
|
||||||
|
os << getSourceLocation(existing->getFile(), nullptr, 0, "");
|
||||||
|
}
|
||||||
|
os << getSourceLocation(newFile, newSc, newSectionOffset,
|
||||||
|
existing->getName());
|
||||||
|
|
||||||
if (config->forceMultiple)
|
if (config->forceMultiple)
|
||||||
warn(msg);
|
warn(os.str());
|
||||||
else
|
else
|
||||||
error(msg);
|
error(os.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
|
Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
|
||||||
@ -401,7 +588,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, COFFSymbolRef sym) {
|
|||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||||
replaceSymbol<DefinedAbsolute>(s, n, sym);
|
replaceSymbol<DefinedAbsolute>(s, n, sym);
|
||||||
else if (!isa<DefinedCOFF>(s))
|
else if (!isa<DefinedCOFF>(s))
|
||||||
reportDuplicate(s, nullptr);
|
reportDuplicate(s, nullptr);
|
||||||
@ -413,7 +600,7 @@ Symbol *SymbolTable::addAbsolute(StringRef n, uint64_t va) {
|
|||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||||
replaceSymbol<DefinedAbsolute>(s, n, va);
|
replaceSymbol<DefinedAbsolute>(s, n, va);
|
||||||
else if (!isa<DefinedCOFF>(s))
|
else if (!isa<DefinedCOFF>(s))
|
||||||
reportDuplicate(s, nullptr);
|
reportDuplicate(s, nullptr);
|
||||||
@ -425,7 +612,7 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
|
|||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s))
|
if (wasInserted || isa<Undefined>(s) || s->isLazy())
|
||||||
replaceSymbol<DefinedSynthetic>(s, n, c);
|
replaceSymbol<DefinedSynthetic>(s, n, c);
|
||||||
else if (!isa<DefinedCOFF>(s))
|
else if (!isa<DefinedCOFF>(s))
|
||||||
reportDuplicate(s, nullptr);
|
reportDuplicate(s, nullptr);
|
||||||
@ -433,8 +620,8 @@ Symbol *SymbolTable::addSynthetic(StringRef n, Chunk *c) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
|
Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
|
||||||
const coff_symbol_generic *sym,
|
const coff_symbol_generic *sym, SectionChunk *c,
|
||||||
SectionChunk *c) {
|
uint32_t sectionOffset) {
|
||||||
Symbol *s;
|
Symbol *s;
|
||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, f);
|
std::tie(s, wasInserted) = insert(n, f);
|
||||||
@ -442,7 +629,7 @@ Symbol *SymbolTable::addRegular(InputFile *f, StringRef n,
|
|||||||
replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
|
replaceSymbol<DefinedRegular>(s, f, n, /*IsCOMDAT*/ false,
|
||||||
/*IsExternal*/ true, sym, c);
|
/*IsExternal*/ true, sym, c);
|
||||||
else
|
else
|
||||||
reportDuplicate(s, f);
|
reportDuplicate(s, f, c, sectionOffset);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,7 +668,7 @@ Symbol *SymbolTable::addImportData(StringRef n, ImportFile *f) {
|
|||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(n, nullptr);
|
std::tie(s, wasInserted) = insert(n, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
|
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
|
||||||
replaceSymbol<DefinedImportData>(s, n, f);
|
replaceSymbol<DefinedImportData>(s, n, f);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@ -496,7 +683,7 @@ Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
|
|||||||
bool wasInserted;
|
bool wasInserted;
|
||||||
std::tie(s, wasInserted) = insert(name, nullptr);
|
std::tie(s, wasInserted) = insert(name, nullptr);
|
||||||
s->isUsedInRegularObj = true;
|
s->isUsedInRegularObj = true;
|
||||||
if (wasInserted || isa<Undefined>(s) || isa<Lazy>(s)) {
|
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
|
||||||
replaceSymbol<DefinedImportThunk>(s, name, id, machine);
|
replaceSymbol<DefinedImportThunk>(s, name, id, machine);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@ -510,9 +697,12 @@ void SymbolTable::addLibcall(StringRef name) {
|
|||||||
if (!sym)
|
if (!sym)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Lazy *l = dyn_cast<Lazy>(sym)) {
|
if (auto *l = dyn_cast<LazyArchive>(sym)) {
|
||||||
MemoryBufferRef mb = l->getMemberBuffer();
|
MemoryBufferRef mb = l->getMemberBuffer();
|
||||||
if (identify_magic(mb.getBuffer()) == llvm::file_magic::bitcode)
|
if (isBitcode(mb))
|
||||||
|
addUndefined(sym->getName());
|
||||||
|
} else if (LazyObject *o = dyn_cast<LazyObject>(sym)) {
|
||||||
|
if (isBitcode(o->file->mb))
|
||||||
addUndefined(sym->getName());
|
addUndefined(sym->getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class Defined;
|
|||||||
class DefinedAbsolute;
|
class DefinedAbsolute;
|
||||||
class DefinedRegular;
|
class DefinedRegular;
|
||||||
class DefinedRelative;
|
class DefinedRelative;
|
||||||
class Lazy;
|
class LazyArchive;
|
||||||
class SectionChunk;
|
class SectionChunk;
|
||||||
class Symbol;
|
class Symbol;
|
||||||
|
|
||||||
@ -49,10 +49,13 @@ class SymbolTable {
|
|||||||
public:
|
public:
|
||||||
void addFile(InputFile *file);
|
void addFile(InputFile *file);
|
||||||
|
|
||||||
|
// Emit errors for symbols that cannot be resolved.
|
||||||
|
void reportUnresolvable();
|
||||||
|
|
||||||
// Try to resolve any undefined symbols and update the symbol table
|
// Try to resolve any undefined symbols and update the symbol table
|
||||||
// accordingly, then print an error message for any remaining undefined
|
// accordingly, then print an error message for any remaining undefined
|
||||||
// symbols.
|
// symbols and warn about imported local symbols.
|
||||||
void reportRemainingUndefines();
|
void resolveRemainingUndefines();
|
||||||
|
|
||||||
void loadMinGWAutomaticImports();
|
void loadMinGWAutomaticImports();
|
||||||
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
|
bool handleMinGWAutomaticImport(Symbol *sym, StringRef name);
|
||||||
@ -83,11 +86,12 @@ class SymbolTable {
|
|||||||
Symbol *addAbsolute(StringRef n, uint64_t va);
|
Symbol *addAbsolute(StringRef n, uint64_t va);
|
||||||
|
|
||||||
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
|
Symbol *addUndefined(StringRef name, InputFile *f, bool isWeakAlias);
|
||||||
void addLazy(ArchiveFile *f, const Archive::Symbol &sym);
|
void addLazyArchive(ArchiveFile *f, const Archive::Symbol &sym);
|
||||||
|
void addLazyObject(LazyObjFile *f, StringRef n);
|
||||||
Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
|
Symbol *addAbsolute(StringRef n, COFFSymbolRef s);
|
||||||
Symbol *addRegular(InputFile *f, StringRef n,
|
Symbol *addRegular(InputFile *f, StringRef n,
|
||||||
const llvm::object::coff_symbol_generic *s = nullptr,
|
const llvm::object::coff_symbol_generic *s = nullptr,
|
||||||
SectionChunk *c = nullptr);
|
SectionChunk *c = nullptr, uint32_t sectionOffset = 0);
|
||||||
std::pair<DefinedRegular *, bool>
|
std::pair<DefinedRegular *, bool>
|
||||||
addComdat(InputFile *f, StringRef n,
|
addComdat(InputFile *f, StringRef n,
|
||||||
const llvm::object::coff_symbol_generic *s = nullptr);
|
const llvm::object::coff_symbol_generic *s = nullptr);
|
||||||
@ -99,7 +103,9 @@ class SymbolTable {
|
|||||||
uint16_t machine);
|
uint16_t machine);
|
||||||
void addLibcall(StringRef name);
|
void addLibcall(StringRef name);
|
||||||
|
|
||||||
void reportDuplicate(Symbol *existing, InputFile *newFile);
|
void reportDuplicate(Symbol *existing, InputFile *newFile,
|
||||||
|
SectionChunk *newSc = nullptr,
|
||||||
|
uint32_t newSectionOffset = 0);
|
||||||
|
|
||||||
// A list of chunks which to be added to .rdata.
|
// A list of chunks which to be added to .rdata.
|
||||||
std::vector<Chunk *> localImportChunks;
|
std::vector<Chunk *> localImportChunks;
|
||||||
@ -111,6 +117,9 @@ class SymbolTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// Given a name without "__imp_" prefix, returns a defined symbol
|
||||||
|
/// with the "__imp_" prefix, if it exists.
|
||||||
|
Defined *impSymbol(StringRef name);
|
||||||
/// Inserts symbol if not already present.
|
/// Inserts symbol if not already present.
|
||||||
std::pair<Symbol *, bool> insert(StringRef name);
|
std::pair<Symbol *, bool> insert(StringRef name);
|
||||||
/// Same as insert(Name), but also sets isUsedInRegularObj.
|
/// Same as insert(Name), but also sets isUsedInRegularObj.
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "lld/Common/Memory.h"
|
#include "lld/Common/Memory.h"
|
||||||
#include "lld/Common/Strings.h"
|
#include "lld/Common/Strings.h"
|
||||||
#include "llvm/ADT/STLExtras.h"
|
#include "llvm/ADT/STLExtras.h"
|
||||||
|
#include "llvm/Demangle/Demangle.h"
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
@ -26,15 +27,27 @@ static_assert(sizeof(SymbolUnion) <= 48,
|
|||||||
"symbols should be optimized for memory usage");
|
"symbols should be optimized for memory usage");
|
||||||
|
|
||||||
// Returns a symbol name for an error message.
|
// Returns a symbol name for an error message.
|
||||||
static std::string demangle(StringRef symName) {
|
static std::string maybeDemangleSymbol(StringRef symName) {
|
||||||
if (config->demangle)
|
if (config->demangle) {
|
||||||
if (Optional<std::string> s = demangleMSVC(symName))
|
std::string prefix;
|
||||||
return *s;
|
StringRef prefixless = symName;
|
||||||
|
if (prefixless.consume_front("__imp_"))
|
||||||
|
prefix = "__declspec(dllimport) ";
|
||||||
|
StringRef demangleInput = prefixless;
|
||||||
|
if (config->machine == I386)
|
||||||
|
demangleInput.consume_front("_");
|
||||||
|
std::string demangled = demangle(demangleInput);
|
||||||
|
if (demangled != demangleInput)
|
||||||
|
return prefix + demangle(demangleInput);
|
||||||
|
return (prefix + prefixless).str();
|
||||||
|
}
|
||||||
return symName;
|
return symName;
|
||||||
}
|
}
|
||||||
std::string toString(coff::Symbol &b) { return demangle(b.getName()); }
|
std::string toString(coff::Symbol &b) {
|
||||||
|
return maybeDemangleSymbol(b.getName());
|
||||||
|
}
|
||||||
std::string toCOFFString(const Archive::Symbol &b) {
|
std::string toCOFFString(const Archive::Symbol &b) {
|
||||||
return demangle(b.getName());
|
return maybeDemangleSymbol(b.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace coff {
|
namespace coff {
|
||||||
@ -61,7 +74,9 @@ StringRef Symbol::getName() {
|
|||||||
InputFile *Symbol::getFile() {
|
InputFile *Symbol::getFile() {
|
||||||
if (auto *sym = dyn_cast<DefinedCOFF>(this))
|
if (auto *sym = dyn_cast<DefinedCOFF>(this))
|
||||||
return sym->file;
|
return sym->file;
|
||||||
if (auto *sym = dyn_cast<Lazy>(this))
|
if (auto *sym = dyn_cast<LazyArchive>(this))
|
||||||
|
return sym->file;
|
||||||
|
if (auto *sym = dyn_cast<LazyObject>(this))
|
||||||
return sym->file;
|
return sym->file;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -119,7 +134,7 @@ Defined *Undefined::getWeakAlias() {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryBufferRef Lazy::getMemberBuffer() {
|
MemoryBufferRef LazyArchive::getMemberBuffer() {
|
||||||
Archive::Child c =
|
Archive::Child c =
|
||||||
CHECK(sym.getMember(),
|
CHECK(sym.getMember(),
|
||||||
"could not get the member for symbol " + toCOFFString(sym));
|
"could not get the member for symbol " + toCOFFString(sym));
|
||||||
|
@ -59,7 +59,8 @@ class Symbol {
|
|||||||
DefinedSyntheticKind,
|
DefinedSyntheticKind,
|
||||||
|
|
||||||
UndefinedKind,
|
UndefinedKind,
|
||||||
LazyKind,
|
LazyArchiveKind,
|
||||||
|
LazyObjectKind,
|
||||||
|
|
||||||
LastDefinedCOFFKind = DefinedCommonKind,
|
LastDefinedCOFFKind = DefinedCommonKind,
|
||||||
LastDefinedKind = DefinedSyntheticKind,
|
LastDefinedKind = DefinedSyntheticKind,
|
||||||
@ -79,6 +80,10 @@ class Symbol {
|
|||||||
// after calling markLive.
|
// after calling markLive.
|
||||||
bool isLive() const;
|
bool isLive() const;
|
||||||
|
|
||||||
|
bool isLazy() const {
|
||||||
|
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend SymbolTable;
|
friend SymbolTable;
|
||||||
explicit Symbol(Kind k, StringRef n = "")
|
explicit Symbol(Kind k, StringRef n = "")
|
||||||
@ -256,26 +261,29 @@ class DefinedSynthetic : public Defined {
|
|||||||
// This class represents a symbol defined in an archive file. It is
|
// This class represents a symbol defined in an archive file. It is
|
||||||
// created from an archive file header, and it knows how to load an
|
// created from an archive file header, and it knows how to load an
|
||||||
// object file from an archive to replace itself with a defined
|
// object file from an archive to replace itself with a defined
|
||||||
// symbol. If the resolver finds both Undefined and Lazy for
|
// symbol. If the resolver finds both Undefined and LazyArchive for
|
||||||
// the same name, it will ask the Lazy to load a file.
|
// the same name, it will ask the LazyArchive to load a file.
|
||||||
class Lazy : public Symbol {
|
class LazyArchive : public Symbol {
|
||||||
public:
|
public:
|
||||||
Lazy(ArchiveFile *f, const Archive::Symbol s)
|
LazyArchive(ArchiveFile *f, const Archive::Symbol s)
|
||||||
: Symbol(LazyKind, s.getName()), file(f), sym(s) {}
|
: Symbol(LazyArchiveKind, s.getName()), file(f), sym(s) {}
|
||||||
|
|
||||||
static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
|
static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
|
||||||
|
|
||||||
MemoryBufferRef getMemberBuffer();
|
MemoryBufferRef getMemberBuffer();
|
||||||
|
|
||||||
ArchiveFile *file;
|
ArchiveFile *file;
|
||||||
|
|
||||||
private:
|
|
||||||
friend SymbolTable;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const Archive::Symbol sym;
|
const Archive::Symbol sym;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LazyObject : public Symbol {
|
||||||
|
public:
|
||||||
|
LazyObject(LazyObjFile *f, StringRef n)
|
||||||
|
: Symbol(LazyObjectKind, n), file(f) {}
|
||||||
|
static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
|
||||||
|
LazyObjFile *file;
|
||||||
|
};
|
||||||
|
|
||||||
// Undefined symbols.
|
// Undefined symbols.
|
||||||
class Undefined : public Symbol {
|
class Undefined : public Symbol {
|
||||||
public:
|
public:
|
||||||
@ -381,7 +389,8 @@ inline uint64_t Defined::getRVA() {
|
|||||||
return cast<DefinedCommon>(this)->getRVA();
|
return cast<DefinedCommon>(this)->getRVA();
|
||||||
case DefinedRegularKind:
|
case DefinedRegularKind:
|
||||||
return cast<DefinedRegular>(this)->getRVA();
|
return cast<DefinedRegular>(this)->getRVA();
|
||||||
case LazyKind:
|
case LazyArchiveKind:
|
||||||
|
case LazyObjectKind:
|
||||||
case UndefinedKind:
|
case UndefinedKind:
|
||||||
llvm_unreachable("Cannot get the address for an undefined symbol.");
|
llvm_unreachable("Cannot get the address for an undefined symbol.");
|
||||||
}
|
}
|
||||||
@ -404,7 +413,8 @@ inline Chunk *Defined::getChunk() {
|
|||||||
return cast<DefinedLocalImport>(this)->getChunk();
|
return cast<DefinedLocalImport>(this)->getChunk();
|
||||||
case DefinedCommonKind:
|
case DefinedCommonKind:
|
||||||
return cast<DefinedCommon>(this)->getChunk();
|
return cast<DefinedCommon>(this)->getChunk();
|
||||||
case LazyKind:
|
case LazyArchiveKind:
|
||||||
|
case LazyObjectKind:
|
||||||
case UndefinedKind:
|
case UndefinedKind:
|
||||||
llvm_unreachable("Cannot get the chunk of an undefined symbol.");
|
llvm_unreachable("Cannot get the chunk of an undefined symbol.");
|
||||||
}
|
}
|
||||||
@ -419,11 +429,12 @@ union SymbolUnion {
|
|||||||
alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
|
alignas(DefinedCommon) char b[sizeof(DefinedCommon)];
|
||||||
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
|
alignas(DefinedAbsolute) char c[sizeof(DefinedAbsolute)];
|
||||||
alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)];
|
alignas(DefinedSynthetic) char d[sizeof(DefinedSynthetic)];
|
||||||
alignas(Lazy) char e[sizeof(Lazy)];
|
alignas(LazyArchive) char e[sizeof(LazyArchive)];
|
||||||
alignas(Undefined) char f[sizeof(Undefined)];
|
alignas(Undefined) char f[sizeof(Undefined)];
|
||||||
alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
|
alignas(DefinedImportData) char g[sizeof(DefinedImportData)];
|
||||||
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
|
alignas(DefinedImportThunk) char h[sizeof(DefinedImportThunk)];
|
||||||
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
|
alignas(DefinedLocalImport) char i[sizeof(DefinedLocalImport)];
|
||||||
|
alignas(LazyObject) char j[sizeof(LazyObject)];
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename... ArgT>
|
template <typename T, typename... ArgT>
|
||||||
|
@ -40,8 +40,9 @@ using namespace llvm::COFF;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support;
|
using namespace llvm::support;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::coff;
|
namespace lld {
|
||||||
|
namespace coff {
|
||||||
|
|
||||||
/* To re-generate DOSProgram:
|
/* To re-generate DOSProgram:
|
||||||
$ cat > /tmp/DOSProgram.asm
|
$ cat > /tmp/DOSProgram.asm
|
||||||
@ -240,6 +241,8 @@ class Writer {
|
|||||||
IdataContents idata;
|
IdataContents idata;
|
||||||
Chunk *importTableStart = nullptr;
|
Chunk *importTableStart = nullptr;
|
||||||
uint64_t importTableSize = 0;
|
uint64_t importTableSize = 0;
|
||||||
|
Chunk *edataStart = nullptr;
|
||||||
|
Chunk *edataEnd = nullptr;
|
||||||
Chunk *iatStart = nullptr;
|
Chunk *iatStart = nullptr;
|
||||||
uint64_t iatSize = 0;
|
uint64_t iatSize = 0;
|
||||||
DelayLoadContents delayIdata;
|
DelayLoadContents delayIdata;
|
||||||
@ -283,9 +286,6 @@ class Writer {
|
|||||||
};
|
};
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace lld {
|
|
||||||
namespace coff {
|
|
||||||
|
|
||||||
static Timer codeLayoutTimer("Code Layout", Timer::root());
|
static Timer codeLayoutTimer("Code Layout", Timer::root());
|
||||||
static Timer diskCommitTimer("Commit Output File", Timer::root());
|
static Timer diskCommitTimer("Commit Output File", Timer::root());
|
||||||
|
|
||||||
@ -331,9 +331,6 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
|
|||||||
contribSections.push_back(sec);
|
contribSections.push_back(sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace coff
|
|
||||||
} // namespace lld
|
|
||||||
|
|
||||||
// Check whether the target address S is in range from a relocation
|
// Check whether the target address S is in range from a relocation
|
||||||
// of type relType at address P.
|
// of type relType at address P.
|
||||||
static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
|
static bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
|
||||||
@ -741,7 +738,8 @@ void Writer::addSyntheticIdata() {
|
|||||||
add(".idata$2", idata.dirs);
|
add(".idata$2", idata.dirs);
|
||||||
add(".idata$4", idata.lookups);
|
add(".idata$4", idata.lookups);
|
||||||
add(".idata$5", idata.addresses);
|
add(".idata$5", idata.addresses);
|
||||||
add(".idata$6", idata.hints);
|
if (!idata.hints.empty())
|
||||||
|
add(".idata$6", idata.hints);
|
||||||
add(".idata$7", idata.dllNames);
|
add(".idata$7", idata.dllNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -840,6 +838,7 @@ void Writer::createSections() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fixPartialSectionChars(".rsrc", data | r);
|
fixPartialSectionChars(".rsrc", data | r);
|
||||||
|
fixPartialSectionChars(".edata", data | r);
|
||||||
// Even in non MinGW cases, we might need to link against GNU import
|
// Even in non MinGW cases, we might need to link against GNU import
|
||||||
// libraries.
|
// libraries.
|
||||||
bool hasIdata = fixGnuImportChunks();
|
bool hasIdata = fixGnuImportChunks();
|
||||||
@ -1014,10 +1013,19 @@ void Writer::appendImportThunks() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Writer::createExportTable() {
|
void Writer::createExportTable() {
|
||||||
if (config->exports.empty())
|
if (!edataSec->chunks.empty()) {
|
||||||
return;
|
// Allow using a custom built export table from input object files, instead
|
||||||
for (Chunk *c : edata.chunks)
|
// of having the linker synthesize the tables.
|
||||||
edataSec->addChunk(c);
|
if (config->hadExplicitExports)
|
||||||
|
warn("literal .edata sections override exports");
|
||||||
|
} else if (!config->exports.empty()) {
|
||||||
|
for (Chunk *c : edata.chunks)
|
||||||
|
edataSec->addChunk(c);
|
||||||
|
}
|
||||||
|
if (!edataSec->chunks.empty()) {
|
||||||
|
edataStart = edataSec->chunks.front();
|
||||||
|
edataEnd = edataSec->chunks.back();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Writer::removeUnusedSections() {
|
void Writer::removeUnusedSections() {
|
||||||
@ -1366,9 +1374,10 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
|||||||
// Write data directory
|
// Write data directory
|
||||||
auto *dir = reinterpret_cast<data_directory *>(buf);
|
auto *dir = reinterpret_cast<data_directory *>(buf);
|
||||||
buf += sizeof(*dir) * numberOfDataDirectory;
|
buf += sizeof(*dir) * numberOfDataDirectory;
|
||||||
if (!config->exports.empty()) {
|
if (edataStart) {
|
||||||
dir[EXPORT_TABLE].RelativeVirtualAddress = edata.getRVA();
|
dir[EXPORT_TABLE].RelativeVirtualAddress = edataStart->getRVA();
|
||||||
dir[EXPORT_TABLE].Size = edata.getSize();
|
dir[EXPORT_TABLE].Size =
|
||||||
|
edataEnd->getRVA() + edataEnd->getSize() - edataStart->getRVA();
|
||||||
}
|
}
|
||||||
if (importTableStart) {
|
if (importTableStart) {
|
||||||
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
|
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
|
||||||
@ -1506,7 +1515,8 @@ static void maybeAddAddressTakenFunction(SymbolRVASet &addressTakenSyms,
|
|||||||
// Absolute is never code, synthetic generally isn't and usually isn't
|
// Absolute is never code, synthetic generally isn't and usually isn't
|
||||||
// determinable.
|
// determinable.
|
||||||
break;
|
break;
|
||||||
case Symbol::LazyKind:
|
case Symbol::LazyArchiveKind:
|
||||||
|
case Symbol::LazyObjectKind:
|
||||||
case Symbol::UndefinedKind:
|
case Symbol::UndefinedKind:
|
||||||
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy
|
// Undefined symbols resolve to zero, so they don't have an RVA. Lazy
|
||||||
// symbols shouldn't have relocations.
|
// symbols shouldn't have relocations.
|
||||||
@ -1930,3 +1940,6 @@ PartialSection *Writer::findPartialSection(StringRef name, uint32_t outChars) {
|
|||||||
return it->second;
|
return it->second;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace coff
|
||||||
|
} // namespace lld
|
||||||
|
@ -29,6 +29,7 @@ set_property(SOURCE Version.cpp APPEND PROPERTY
|
|||||||
|
|
||||||
add_lld_library(lldCommon
|
add_lld_library(lldCommon
|
||||||
Args.cpp
|
Args.cpp
|
||||||
|
DWARF.cpp
|
||||||
ErrorHandler.cpp
|
ErrorHandler.cpp
|
||||||
Filesystem.cpp
|
Filesystem.cpp
|
||||||
Memory.cpp
|
Memory.cpp
|
||||||
@ -46,6 +47,7 @@ add_lld_library(lldCommon
|
|||||||
LINK_COMPONENTS
|
LINK_COMPONENTS
|
||||||
Codegen
|
Codegen
|
||||||
Core
|
Core
|
||||||
|
DebugInfoDWARF
|
||||||
Demangle
|
Demangle
|
||||||
MC
|
MC
|
||||||
Option
|
Option
|
||||||
|
103
contrib/llvm-project/lld/Common/DWARF.cpp
Normal file
103
contrib/llvm-project/lld/Common/DWARF.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
//===- DWARF.cpp ----------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "lld/Common/DWARF.h"
|
||||||
|
#include "lld/Common/ErrorHandler.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
|
||||||
|
DWARFCache::DWARFCache(std::unique_ptr<llvm::DWARFContext> d)
|
||||||
|
: dwarf(std::move(d)) {
|
||||||
|
for (std::unique_ptr<DWARFUnit> &cu : dwarf->compile_units()) {
|
||||||
|
auto report = [](Error err) {
|
||||||
|
handleAllErrors(std::move(err),
|
||||||
|
[](ErrorInfoBase &info) { warn(info.message()); });
|
||||||
|
};
|
||||||
|
Expected<const DWARFDebugLine::LineTable *> expectedLT =
|
||||||
|
dwarf->getLineTableForUnit(cu.get(), report);
|
||||||
|
const DWARFDebugLine::LineTable *lt = nullptr;
|
||||||
|
if (expectedLT)
|
||||||
|
lt = *expectedLT;
|
||||||
|
else
|
||||||
|
report(expectedLT.takeError());
|
||||||
|
if (!lt)
|
||||||
|
continue;
|
||||||
|
lineTables.push_back(lt);
|
||||||
|
|
||||||
|
// Loop over variable records and insert them to variableLoc.
|
||||||
|
for (const auto &entry : cu->dies()) {
|
||||||
|
DWARFDie die(cu.get(), &entry);
|
||||||
|
// Skip all tags that are not variables.
|
||||||
|
if (die.getTag() != dwarf::DW_TAG_variable)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Skip if a local variable because we don't need them for generating
|
||||||
|
// error messages. In general, only non-local symbols can fail to be
|
||||||
|
// linked.
|
||||||
|
if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get the source filename index for the variable.
|
||||||
|
unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0);
|
||||||
|
if (!lt->hasFileAtIndex(file))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get the line number on which the variable is declared.
|
||||||
|
unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0);
|
||||||
|
|
||||||
|
// Here we want to take the variable name to add it into variableLoc.
|
||||||
|
// Variable can have regular and linkage name associated. At first, we try
|
||||||
|
// to get linkage name as it can be different, for example when we have
|
||||||
|
// two variables in different namespaces of the same object. Use common
|
||||||
|
// name otherwise, but handle the case when it also absent in case if the
|
||||||
|
// input object file lacks some debug info.
|
||||||
|
StringRef name =
|
||||||
|
dwarf::toString(die.find(dwarf::DW_AT_linkage_name),
|
||||||
|
dwarf::toString(die.find(dwarf::DW_AT_name), ""));
|
||||||
|
if (!name.empty())
|
||||||
|
variableLoc.insert({name, {lt, file, line}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the pair of file name and line number describing location of data
|
||||||
|
// object (variable, array, etc) definition.
|
||||||
|
Optional<std::pair<std::string, unsigned>>
|
||||||
|
DWARFCache::getVariableLoc(StringRef name) {
|
||||||
|
// Return if we have no debug information about data object.
|
||||||
|
auto it = variableLoc.find(name);
|
||||||
|
if (it == variableLoc.end())
|
||||||
|
return None;
|
||||||
|
|
||||||
|
// Take file name string from line table.
|
||||||
|
std::string fileName;
|
||||||
|
if (!it->second.lt->getFileNameByIndex(
|
||||||
|
it->second.file, {},
|
||||||
|
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName))
|
||||||
|
return None;
|
||||||
|
|
||||||
|
return std::make_pair(fileName, it->second.line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns source line information for a given offset
|
||||||
|
// using DWARF debug info.
|
||||||
|
Optional<DILineInfo> DWARFCache::getDILineInfo(uint64_t offset,
|
||||||
|
uint64_t sectionIndex) {
|
||||||
|
DILineInfo info;
|
||||||
|
for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) {
|
||||||
|
if (lt->getFileLineInfoForAddress(
|
||||||
|
{offset, sectionIndex}, nullptr,
|
||||||
|
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info))
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace lld
|
@ -29,16 +29,14 @@ using namespace lld;
|
|||||||
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
||||||
static std::mutex mu;
|
static std::mutex mu;
|
||||||
|
|
||||||
// Prints "\n" or does nothing, depending on Msg contents of
|
// We want to separate multi-line messages with a newline. `sep` is "\n"
|
||||||
// the previous call of this function.
|
// if the last messages was multi-line. Otherwise "".
|
||||||
static void newline(raw_ostream *errorOS, const Twine &msg) {
|
static StringRef sep;
|
||||||
// True if the previous error message contained "\n".
|
|
||||||
// We want to separate multi-line error messages with a newline.
|
|
||||||
static bool flag;
|
|
||||||
|
|
||||||
if (flag)
|
static StringRef getSeparator(const Twine &msg) {
|
||||||
*errorOS << "\n";
|
if (StringRef(msg.str()).contains('\n'))
|
||||||
flag = StringRef(msg.str()).contains('\n');
|
return "\n";
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorHandler &lld::errorHandler() {
|
ErrorHandler &lld::errorHandler() {
|
||||||
@ -46,6 +44,10 @@ ErrorHandler &lld::errorHandler() {
|
|||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lld::enableColors(bool enable) {
|
||||||
|
errorHandler().errorOS->enable_colors(enable);
|
||||||
|
}
|
||||||
|
|
||||||
void lld::exitLld(int val) {
|
void lld::exitLld(int val) {
|
||||||
// Delete any temporary file, while keeping the memory mapping open.
|
// Delete any temporary file, while keeping the memory mapping open.
|
||||||
if (errorHandler().outputBuffer)
|
if (errorHandler().outputBuffer)
|
||||||
@ -85,56 +87,69 @@ void lld::checkError(Error e) {
|
|||||||
[&](ErrorInfoBase &eib) { error(eib.message()); });
|
[&](ErrorInfoBase &eib) { error(eib.message()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string getLocation(std::string msg, std::string defaultMsg) {
|
// This is for --vs-diagnostics.
|
||||||
static std::vector<std::regex> Regexes{
|
//
|
||||||
std::regex(R"(^undefined symbol:.*\n>>> referenced by (\S+):(\d+)\n.*)"),
|
// Normally, lld's error message starts with argv[0]. Therefore, it usually
|
||||||
|
// looks like this:
|
||||||
|
//
|
||||||
|
// ld.lld: error: ...
|
||||||
|
//
|
||||||
|
// This error message style is unfortunately unfriendly to Visual Studio
|
||||||
|
// IDE. VS interprets the first word of the first line as an error location
|
||||||
|
// and make it clickable, thus "ld.lld" in the above message would become a
|
||||||
|
// clickable text. When you click it, VS opens "ld.lld" executable file with
|
||||||
|
// a binary editor.
|
||||||
|
//
|
||||||
|
// As a workaround, we print out an error location instead of "ld.lld" if
|
||||||
|
// lld is running in VS diagnostics mode. As a result, error message will
|
||||||
|
// look like this:
|
||||||
|
//
|
||||||
|
// src/foo.c(35): error: ...
|
||||||
|
//
|
||||||
|
// This function returns an error location string. An error location is
|
||||||
|
// extracted from an error message using regexps.
|
||||||
|
std::string ErrorHandler::getLocation(const Twine &msg) {
|
||||||
|
if (!vsDiagnostics)
|
||||||
|
return logName;
|
||||||
|
|
||||||
|
static std::regex regexes[] = {
|
||||||
|
std::regex(
|
||||||
|
R"(^undefined (?:\S+ )?symbol:.*\n)"
|
||||||
|
R"(>>> referenced by .+\((\S+):(\d+)\))"),
|
||||||
|
std::regex(
|
||||||
|
R"(^undefined (?:\S+ )?symbol:.*\n>>> referenced by (\S+):(\d+))"),
|
||||||
std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"),
|
std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"),
|
||||||
std::regex(
|
std::regex(
|
||||||
R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"),
|
R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"),
|
||||||
std::regex(
|
std::regex(
|
||||||
R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+).*)"),
|
R"(^duplicate symbol: .*\n>>> defined at .+\((\S+):(\d+)\))"),
|
||||||
|
std::regex(R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+))"),
|
||||||
std::regex(
|
std::regex(
|
||||||
R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),
|
R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))"),
|
||||||
std::regex(
|
std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),
|
||||||
R"(^undefined (internal|hidden|protected) symbol: .*\n>>> referenced by (\S+):(\d+)\n.*)"),
|
|
||||||
std::regex(R"((\S+):(\d+): unclosed quote)"),
|
std::regex(R"((\S+):(\d+): unclosed quote)"),
|
||||||
};
|
};
|
||||||
|
|
||||||
std::smatch Match;
|
std::string str = msg.str();
|
||||||
for (std::regex &Re : Regexes) {
|
for (std::regex &re : regexes) {
|
||||||
if (std::regex_search(msg, Match, Re)) {
|
std::smatch m;
|
||||||
return Match.size() > 2 ? Match.str(1) + "(" + Match.str(2) + ")"
|
if (!std::regex_search(str, m, re))
|
||||||
: Match.str(1);
|
continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
return defaultMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ErrorHandler::printHeader(StringRef s, raw_ostream::Colors c,
|
assert(m.size() == 2 || m.size() == 3);
|
||||||
const Twine &msg) {
|
if (m.size() == 2)
|
||||||
|
return m.str(1);
|
||||||
if (vsDiagnostics) {
|
return m.str(1) + "(" + m.str(2) + ")";
|
||||||
// A Visual Studio-style error message starts with an error location.
|
|
||||||
// If a location cannot be extracted then we default to LogName.
|
|
||||||
*errorOS << getLocation(msg.str(), logName) << ": ";
|
|
||||||
} else {
|
|
||||||
*errorOS << logName << ": ";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (colorDiagnostics) {
|
return logName;
|
||||||
errorOS->changeColor(c, true);
|
|
||||||
*errorOS << s;
|
|
||||||
errorOS->resetColor();
|
|
||||||
} else {
|
|
||||||
*errorOS << s;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorHandler::log(const Twine &msg) {
|
void ErrorHandler::log(const Twine &msg) {
|
||||||
if (verbose) {
|
if (!verbose)
|
||||||
std::lock_guard<std::mutex> lock(mu);
|
return;
|
||||||
*errorOS << logName << ": " << msg << "\n";
|
std::lock_guard<std::mutex> lock(mu);
|
||||||
}
|
*errorOS << logName << ": " << msg << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorHandler::message(const Twine &msg) {
|
void ErrorHandler::message(const Twine &msg) {
|
||||||
@ -150,25 +165,41 @@ void ErrorHandler::warn(const Twine &msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(mu);
|
std::lock_guard<std::mutex> lock(mu);
|
||||||
newline(errorOS, msg);
|
*errorOS << sep << getLocation(msg) << ": " << Colors::MAGENTA
|
||||||
printHeader("warning: ", raw_ostream::MAGENTA, msg);
|
<< "warning: " << Colors::RESET << msg << "\n";
|
||||||
*errorOS << msg << "\n";
|
sep = getSeparator(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ErrorHandler::error(const Twine &msg) {
|
void ErrorHandler::error(const Twine &msg) {
|
||||||
|
// If Visual Studio-style error message mode is enabled,
|
||||||
|
// this particular error is printed out as two errors.
|
||||||
|
if (vsDiagnostics) {
|
||||||
|
static std::regex re(R"(^(duplicate symbol: .*))"
|
||||||
|
R"((\n>>> defined at \S+:\d+.*\n>>>.*))"
|
||||||
|
R"((\n>>> defined at \S+:\d+.*\n>>>.*))");
|
||||||
|
std::string str = msg.str();
|
||||||
|
std::smatch m;
|
||||||
|
|
||||||
|
if (std::regex_match(str, m, re)) {
|
||||||
|
error(m.str(1) + m.str(2));
|
||||||
|
error(m.str(1) + m.str(3));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(mu);
|
std::lock_guard<std::mutex> lock(mu);
|
||||||
newline(errorOS, msg);
|
|
||||||
|
|
||||||
if (errorLimit == 0 || errorCount < errorLimit) {
|
if (errorLimit == 0 || errorCount < errorLimit) {
|
||||||
printHeader("error: ", raw_ostream::RED, msg);
|
*errorOS << sep << getLocation(msg) << ": " << Colors::RED
|
||||||
*errorOS << msg << "\n";
|
<< "error: " << Colors::RESET << msg << "\n";
|
||||||
} else if (errorCount == errorLimit) {
|
} else if (errorCount == errorLimit) {
|
||||||
printHeader("error: ", raw_ostream::RED, msg);
|
*errorOS << sep << getLocation(msg) << ": " << Colors::RED
|
||||||
*errorOS << errorLimitExceededMsg << "\n";
|
<< "error: " << Colors::RESET << errorLimitExceededMsg << "\n";
|
||||||
if (exitEarly)
|
if (exitEarly)
|
||||||
exitLld(1);
|
exitLld(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sep = getSeparator(msg);
|
||||||
++errorCount;
|
++errorCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,39 +18,17 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace lld;
|
using namespace lld;
|
||||||
|
|
||||||
// Returns the demangled C++ symbol name for Name.
|
// Returns the demangled C++ symbol name for name.
|
||||||
Optional<std::string> lld::demangleItanium(StringRef name) {
|
std::string lld::demangleItanium(StringRef name) {
|
||||||
// itaniumDemangle can be used to demangle strings other than symbol
|
// itaniumDemangle can be used to demangle strings other than symbol
|
||||||
// names which do not necessarily start with "_Z". Name can be
|
// names which do not necessarily start with "_Z". Name can be
|
||||||
// either a C or C++ symbol. Don't call itaniumDemangle if the name
|
// either a C or C++ symbol. Don't call demangle if the name
|
||||||
// does not look like a C++ symbol name to avoid getting unexpected
|
// does not look like a C++ symbol name to avoid getting unexpected
|
||||||
// result for a C symbol that happens to match a mangled type name.
|
// result for a C symbol that happens to match a mangled type name.
|
||||||
if (!name.startswith("_Z"))
|
if (!name.startswith("_Z"))
|
||||||
return None;
|
return name;
|
||||||
|
|
||||||
char *buf = itaniumDemangle(name.str().c_str(), nullptr, nullptr, nullptr);
|
return demangle(name);
|
||||||
if (!buf)
|
|
||||||
return None;
|
|
||||||
std::string s(buf);
|
|
||||||
free(buf);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<std::string> lld::demangleMSVC(StringRef name) {
|
|
||||||
std::string prefix;
|
|
||||||
if (name.consume_front("__imp_"))
|
|
||||||
prefix = "__declspec(dllimport) ";
|
|
||||||
|
|
||||||
// Demangle only C++ names.
|
|
||||||
if (!name.startswith("?"))
|
|
||||||
return None;
|
|
||||||
|
|
||||||
char *buf = microsoftDemangle(name.str().c_str(), nullptr, nullptr, nullptr);
|
|
||||||
if (!buf)
|
|
||||||
return None;
|
|
||||||
std::string s(buf);
|
|
||||||
free(buf);
|
|
||||||
return prefix + s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StringMatcher::StringMatcher(ArrayRef<StringRef> pat) {
|
StringMatcher::StringMatcher(ArrayRef<StringRef> pat) {
|
||||||
@ -96,7 +74,7 @@ bool lld::isValidCIdentifier(StringRef s) {
|
|||||||
// Write the contents of the a buffer to a file
|
// Write the contents of the a buffer to a file
|
||||||
void lld::saveBuffer(StringRef buffer, const Twine &path) {
|
void lld::saveBuffer(StringRef buffer, const Twine &path) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::F_None);
|
raw_fd_ostream os(path.str(), ec, sys::fs::OpenFlags::OF_None);
|
||||||
if (ec)
|
if (ec)
|
||||||
error("cannot create " + path + ": " + ec.message());
|
error("cannot create " + path + ": " + ec.message());
|
||||||
os << buffer;
|
os << buffer;
|
||||||
|
@ -26,6 +26,10 @@ llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
|
|||||||
return ::InitTargetOptionsFromCodeGenFlags();
|
return ::InitTargetOptionsFromCodeGenFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Optional<llvm::Reloc::Model> lld::getRelocModelFromCMModel() {
|
||||||
|
return getRelocModel();
|
||||||
|
}
|
||||||
|
|
||||||
llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
|
llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
|
||||||
return getCodeModel();
|
return getCodeModel();
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,10 @@
|
|||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// This file implements Section Patching for the purpose of working around
|
// This file implements Section Patching for the purpose of working around
|
||||||
// errata in CPUs. The general principle is that an erratum sequence of one or
|
// the AArch64 Cortex-53 errata 843419 that affects r0p0, r0p1, r0p2 and r0p4
|
||||||
|
// versions of the core.
|
||||||
|
//
|
||||||
|
// The general principle is that an erratum sequence of one or
|
||||||
// more instructions is detected in the instruction stream, one of the
|
// more instructions is detected in the instruction stream, one of the
|
||||||
// instructions in the sequence is replaced with a branch to a patch sequence
|
// instructions in the sequence is replaced with a branch to a patch sequence
|
||||||
// of replacement instructions. At the end of the replacement sequence the
|
// of replacement instructions. At the end of the replacement sequence the
|
||||||
@ -20,12 +23,6 @@
|
|||||||
// - We can overwrite an instruction in the erratum sequence with a branch to
|
// - We can overwrite an instruction in the erratum sequence with a branch to
|
||||||
// the replacement sequence.
|
// the replacement sequence.
|
||||||
// - We can place the replacement sequence within range of the branch.
|
// - We can place the replacement sequence within range of the branch.
|
||||||
|
|
||||||
// FIXME:
|
|
||||||
// - The implementation here only supports one patch, the AArch64 Cortex-53
|
|
||||||
// errata 843419 that affects r0p0, r0p1, r0p2 and r0p4 versions of the core.
|
|
||||||
// To keep the initial version simple there is no support for multiple
|
|
||||||
// architectures or selection of different patches.
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "AArch64ErrataFix.h"
|
#include "AArch64ErrataFix.h"
|
||||||
@ -48,8 +45,8 @@ using namespace llvm::object;
|
|||||||
using namespace llvm::support;
|
using namespace llvm::support;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
// Helper functions to identify instructions and conditions needed to trigger
|
// Helper functions to identify instructions and conditions needed to trigger
|
||||||
// the Cortex-A53-843419 erratum.
|
// the Cortex-A53-843419 erratum.
|
||||||
@ -333,16 +330,16 @@ static bool is843419ErratumSequence(uint32_t instr1, uint32_t instr2,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scan the instruction sequence starting at Offset Off from the base of
|
// Scan the instruction sequence starting at Offset Off from the base of
|
||||||
// InputSection IS. We update Off in this function rather than in the caller as
|
// InputSection isec. We update Off in this function rather than in the caller
|
||||||
// we can skip ahead much further into the section when we know how many
|
// as we can skip ahead much further into the section when we know how many
|
||||||
// instructions we've scanned.
|
// instructions we've scanned.
|
||||||
// Return the offset of the load or store instruction in IS that we want to
|
// Return the offset of the load or store instruction in isec that we want to
|
||||||
// patch or 0 if no patch required.
|
// patch or 0 if no patch required.
|
||||||
static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
|
static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
|
||||||
uint64_t limit) {
|
uint64_t limit) {
|
||||||
uint64_t isecAddr = isec->getVA(0);
|
uint64_t isecAddr = isec->getVA(0);
|
||||||
|
|
||||||
// Advance Off so that (ISAddr + Off) modulo 0x1000 is at least 0xff8.
|
// Advance Off so that (isecAddr + Off) modulo 0x1000 is at least 0xff8.
|
||||||
uint64_t initialPageOff = (isecAddr + off) & 0xfff;
|
uint64_t initialPageOff = (isecAddr + off) & 0xfff;
|
||||||
if (initialPageOff < 0xff8)
|
if (initialPageOff < 0xff8)
|
||||||
off += 0xff8 - initialPageOff;
|
off += 0xff8 - initialPageOff;
|
||||||
@ -374,7 +371,7 @@ static uint64_t scanCortexA53Errata843419(InputSection *isec, uint64_t &off,
|
|||||||
return patchOff;
|
return patchOff;
|
||||||
}
|
}
|
||||||
|
|
||||||
class lld::elf::Patch843419Section : public SyntheticSection {
|
class Patch843419Section : public SyntheticSection {
|
||||||
public:
|
public:
|
||||||
Patch843419Section(InputSection *p, uint64_t off);
|
Patch843419Section(InputSection *p, uint64_t off);
|
||||||
|
|
||||||
@ -386,13 +383,13 @@ class lld::elf::Patch843419Section : public SyntheticSection {
|
|||||||
|
|
||||||
// The Section we are patching.
|
// The Section we are patching.
|
||||||
const InputSection *patchee;
|
const InputSection *patchee;
|
||||||
// The offset of the instruction in the Patchee section we are patching.
|
// The offset of the instruction in the patchee section we are patching.
|
||||||
uint64_t patcheeOffset;
|
uint64_t patcheeOffset;
|
||||||
// A label for the start of the Patch that we can use as a relocation target.
|
// A label for the start of the Patch that we can use as a relocation target.
|
||||||
Symbol *patchSym;
|
Symbol *patchSym;
|
||||||
};
|
};
|
||||||
|
|
||||||
lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
|
Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
|
||||||
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
|
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
|
||||||
".text.patch"),
|
".text.patch"),
|
||||||
patchee(p), patcheeOffset(off) {
|
patchee(p), patcheeOffset(off) {
|
||||||
@ -403,16 +400,16 @@ lld::elf::Patch843419Section::Patch843419Section(InputSection *p, uint64_t off)
|
|||||||
addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this);
|
addSyntheticLocal(saver.save("$x"), STT_NOTYPE, 0, 0, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t lld::elf::Patch843419Section::getLDSTAddr() const {
|
uint64_t Patch843419Section::getLDSTAddr() const {
|
||||||
return patchee->getVA(patcheeOffset);
|
return patchee->getVA(patcheeOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lld::elf::Patch843419Section::writeTo(uint8_t *buf) {
|
void Patch843419Section::writeTo(uint8_t *buf) {
|
||||||
// Copy the instruction that we will be replacing with a branch in the
|
// Copy the instruction that we will be replacing with a branch in the
|
||||||
// Patchee Section.
|
// patchee Section.
|
||||||
write32le(buf, read32le(patchee->data().begin() + patcheeOffset));
|
write32le(buf, read32le(patchee->data().begin() + patcheeOffset));
|
||||||
|
|
||||||
// Apply any relocation transferred from the original PatcheeSection.
|
// Apply any relocation transferred from the original patchee section.
|
||||||
// For a SyntheticSection Buf already has outSecOff added, but relocateAlloc
|
// For a SyntheticSection Buf already has outSecOff added, but relocateAlloc
|
||||||
// also adds outSecOff so we need to subtract to avoid double counting.
|
// also adds outSecOff so we need to subtract to avoid double counting.
|
||||||
this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
|
this->relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
|
||||||
@ -461,18 +458,18 @@ void AArch64Err843419Patcher::init() {
|
|||||||
// $d.0 $d.1 $x.1.
|
// $d.0 $d.1 $x.1.
|
||||||
for (auto &kv : sectionMap) {
|
for (auto &kv : sectionMap) {
|
||||||
std::vector<const Defined *> &mapSyms = kv.second;
|
std::vector<const Defined *> &mapSyms = kv.second;
|
||||||
if (mapSyms.size() <= 1)
|
|
||||||
continue;
|
|
||||||
llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) {
|
llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) {
|
||||||
return a->value < b->value;
|
return a->value < b->value;
|
||||||
});
|
});
|
||||||
mapSyms.erase(
|
mapSyms.erase(
|
||||||
std::unique(mapSyms.begin(), mapSyms.end(),
|
std::unique(mapSyms.begin(), mapSyms.end(),
|
||||||
[=](const Defined *a, const Defined *b) {
|
[=](const Defined *a, const Defined *b) {
|
||||||
return (isCodeMapSymbol(a) && isCodeMapSymbol(b)) ||
|
return isCodeMapSymbol(a) == isCodeMapSymbol(b);
|
||||||
(isDataMapSymbol(a) && isDataMapSymbol(b));
|
|
||||||
}),
|
}),
|
||||||
mapSyms.end());
|
mapSyms.end());
|
||||||
|
// Always start with a Code Mapping Symbol.
|
||||||
|
if (!mapSyms.empty() && !isCodeMapSymbol(mapSyms.front()))
|
||||||
|
mapSyms.erase(mapSyms.begin());
|
||||||
}
|
}
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
@ -511,19 +508,16 @@ void AArch64Err843419Patcher::insertPatches(
|
|||||||
(*patchIt)->outSecOff = isecLimit;
|
(*patchIt)->outSecOff = isecLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge all patch sections. We use the outSecOff assigned above to
|
// Merge all patch sections. We use the outSecOff assigned above to
|
||||||
// determine the insertion point. This is ok as we only merge into an
|
// determine the insertion point. This is ok as we only merge into an
|
||||||
// InputSectionDescription once per pass, and at the end of the pass
|
// InputSectionDescription once per pass, and at the end of the pass
|
||||||
// assignAddresses() will recalculate all the outSecOff values.
|
// assignAddresses() will recalculate all the outSecOff values.
|
||||||
std::vector<InputSection *> tmp;
|
std::vector<InputSection *> tmp;
|
||||||
tmp.reserve(isd.sections.size() + patches.size());
|
tmp.reserve(isd.sections.size() + patches.size());
|
||||||
auto mergeCmp = [](const InputSection *a, const InputSection *b) {
|
auto mergeCmp = [](const InputSection *a, const InputSection *b) {
|
||||||
if (a->outSecOff < b->outSecOff)
|
if (a->outSecOff != b->outSecOff)
|
||||||
return true;
|
return a->outSecOff < b->outSecOff;
|
||||||
if (a->outSecOff == b->outSecOff && isa<Patch843419Section>(a) &&
|
return isa<Patch843419Section>(a) && !isa<Patch843419Section>(b);
|
||||||
!isa<Patch843419Section>(b))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
|
std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
|
||||||
patches.end(), std::back_inserter(tmp), mergeCmp);
|
patches.end(), std::back_inserter(tmp), mergeCmp);
|
||||||
@ -532,7 +526,7 @@ void AArch64Err843419Patcher::insertPatches(
|
|||||||
|
|
||||||
// Given an erratum sequence that starts at address adrpAddr, with an
|
// Given an erratum sequence that starts at address adrpAddr, with an
|
||||||
// instruction that we need to patch at patcheeOffset from the start of
|
// instruction that we need to patch at patcheeOffset from the start of
|
||||||
// InputSection IS, create a Patch843419 Section and add it to the
|
// InputSection isec, create a Patch843419 Section and add it to the
|
||||||
// Patches that we need to insert.
|
// Patches that we need to insert.
|
||||||
static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
|
static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
|
||||||
InputSection *isec,
|
InputSection *isec,
|
||||||
@ -578,7 +572,7 @@ static void implementPatch(uint64_t adrpAddr, uint64_t patcheeOffset,
|
|||||||
|
|
||||||
// Scan all the instructions in InputSectionDescription, for each instance of
|
// Scan all the instructions in InputSectionDescription, for each instance of
|
||||||
// the erratum sequence create a Patch843419Section. We return the list of
|
// the erratum sequence create a Patch843419Section. We return the list of
|
||||||
// Patch843419Sections that need to be applied to ISD.
|
// Patch843419Sections that need to be applied to the InputSectionDescription.
|
||||||
std::vector<Patch843419Section *>
|
std::vector<Patch843419Section *>
|
||||||
AArch64Err843419Patcher::patchInputSectionDescription(
|
AArch64Err843419Patcher::patchInputSectionDescription(
|
||||||
InputSectionDescription &isd) {
|
InputSectionDescription &isd) {
|
||||||
@ -594,10 +588,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
|
|||||||
// section size).
|
// section size).
|
||||||
std::vector<const Defined *> &mapSyms = sectionMap[isec];
|
std::vector<const Defined *> &mapSyms = sectionMap[isec];
|
||||||
|
|
||||||
auto codeSym = llvm::find_if(mapSyms, [&](const Defined *ms) {
|
auto codeSym = mapSyms.begin();
|
||||||
return ms->getName().startswith("$x");
|
|
||||||
});
|
|
||||||
|
|
||||||
while (codeSym != mapSyms.end()) {
|
while (codeSym != mapSyms.end()) {
|
||||||
auto dataSym = std::next(codeSym);
|
auto dataSym = std::next(codeSym);
|
||||||
uint64_t off = (*codeSym)->value;
|
uint64_t off = (*codeSym)->value;
|
||||||
@ -606,7 +597,8 @@ AArch64Err843419Patcher::patchInputSectionDescription(
|
|||||||
|
|
||||||
while (off < limit) {
|
while (off < limit) {
|
||||||
uint64_t startAddr = isec->getVA(off);
|
uint64_t startAddr = isec->getVA(off);
|
||||||
if (uint64_t patcheeOffset = scanCortexA53Errata843419(isec, off, limit))
|
if (uint64_t patcheeOffset =
|
||||||
|
scanCortexA53Errata843419(isec, off, limit))
|
||||||
implementPatch(startAddr, patcheeOffset, isec, patches);
|
implementPatch(startAddr, patcheeOffset, isec, patches);
|
||||||
}
|
}
|
||||||
if (dataSym == mapSyms.end())
|
if (dataSym == mapSyms.end())
|
||||||
@ -630,7 +622,7 @@ AArch64Err843419Patcher::patchInputSectionDescription(
|
|||||||
// Ouptut and Input Sections may have been changed.
|
// Ouptut and Input Sections may have been changed.
|
||||||
// Returns false if no patches were required and no changes were made.
|
// Returns false if no patches were required and no changes were made.
|
||||||
bool AArch64Err843419Patcher::createFixes() {
|
bool AArch64Err843419Patcher::createFixes() {
|
||||||
if (initialized == false)
|
if (!initialized)
|
||||||
init();
|
init();
|
||||||
|
|
||||||
bool addressesChanged = false;
|
bool addressesChanged = false;
|
||||||
@ -649,3 +641,5 @@ bool AArch64Err843419Patcher::createFixes() {
|
|||||||
}
|
}
|
||||||
return addressesChanged;
|
return addressesChanged;
|
||||||
}
|
}
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
528
contrib/llvm-project/lld/ELF/ARMErrataFix.cpp
Normal file
528
contrib/llvm-project/lld/ELF/ARMErrataFix.cpp
Normal file
@ -0,0 +1,528 @@
|
|||||||
|
//===- ARMErrataFix.cpp ---------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// This file implements Section Patching for the purpose of working around the
|
||||||
|
// Cortex-a8 erratum 657417 "A 32bit branch instruction that spans 2 4K regions
|
||||||
|
// can result in an incorrect instruction fetch or processor deadlock." The
|
||||||
|
// erratum affects all but r1p7, r2p5, r2p6, r3p1 and r3p2 revisions of the
|
||||||
|
// Cortex-A8. A high level description of the patching technique is given in
|
||||||
|
// the opening comment of AArch64ErrataFix.cpp.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "ARMErrataFix.h"
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
|
#include "LinkerScript.h"
|
||||||
|
#include "OutputSections.h"
|
||||||
|
#include "Relocations.h"
|
||||||
|
#include "Symbols.h"
|
||||||
|
#include "SyntheticSections.h"
|
||||||
|
#include "Target.h"
|
||||||
|
#include "lld/Common/Memory.h"
|
||||||
|
#include "lld/Common/Strings.h"
|
||||||
|
#include "llvm/Support/Endian.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
using namespace llvm::ELF;
|
||||||
|
using namespace llvm::object;
|
||||||
|
using namespace llvm::support;
|
||||||
|
using namespace llvm::support::endian;
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
|
// The documented title for Erratum 657417 is:
|
||||||
|
// "A 32bit branch instruction that spans two 4K regions can result in an
|
||||||
|
// incorrect instruction fetch or processor deadlock". Graphically using a
|
||||||
|
// 32-bit B.w instruction encoded as a pair of halfwords 0xf7fe 0xbfff
|
||||||
|
// xxxxxx000 // Memory region 1 start
|
||||||
|
// target:
|
||||||
|
// ...
|
||||||
|
// xxxxxxffe f7fe // First halfword of branch to target:
|
||||||
|
// xxxxxx000 // Memory region 2 start
|
||||||
|
// xxxxxx002 bfff // Second halfword of branch to target:
|
||||||
|
//
|
||||||
|
// The specific trigger conditions that can be detected at link time are:
|
||||||
|
// - There is a 32-bit Thumb-2 branch instruction with an address of the form
|
||||||
|
// xxxxxxFFE. The first 2 bytes of the instruction are in 4KiB region 1, the
|
||||||
|
// second 2 bytes are in region 2.
|
||||||
|
// - The branch instruction is one of BLX, BL, B.w BCC.w
|
||||||
|
// - The instruction preceding the branch is a 32-bit non-branch instruction.
|
||||||
|
// - The target of the branch is in region 1.
|
||||||
|
//
|
||||||
|
// The linker mitigation for the fix is to redirect any branch that meets the
|
||||||
|
// erratum conditions to a patch section containing a branch to the target.
|
||||||
|
//
|
||||||
|
// As adding patch sections may move branches onto region boundaries the patch
|
||||||
|
// must iterate until no more patches are added.
|
||||||
|
//
|
||||||
|
// Example, before:
|
||||||
|
// 00000FFA func: NOP.w // 32-bit Thumb function
|
||||||
|
// 00000FFE B.W func // 32-bit branch spanning 2 regions, dest in 1st.
|
||||||
|
// Example, after:
|
||||||
|
// 00000FFA func: NOP.w // 32-bit Thumb function
|
||||||
|
// 00000FFE B.w __CortexA8657417_00000FFE
|
||||||
|
// 00001002 2 - bytes padding
|
||||||
|
// 00001004 __CortexA8657417_00000FFE: B.w func
|
||||||
|
|
||||||
|
class Patch657417Section : public SyntheticSection {
|
||||||
|
public:
|
||||||
|
Patch657417Section(InputSection *p, uint64_t off, uint32_t instr, bool isARM);
|
||||||
|
|
||||||
|
void writeTo(uint8_t *buf) override;
|
||||||
|
|
||||||
|
size_t getSize() const override { return 4; }
|
||||||
|
|
||||||
|
// Get the virtual address of the branch instruction at patcheeOffset.
|
||||||
|
uint64_t getBranchAddr() const;
|
||||||
|
|
||||||
|
// The Section we are patching.
|
||||||
|
const InputSection *patchee;
|
||||||
|
// The offset of the instruction in the Patchee section we are patching.
|
||||||
|
uint64_t patcheeOffset;
|
||||||
|
// A label for the start of the Patch that we can use as a relocation target.
|
||||||
|
Symbol *patchSym;
|
||||||
|
// A decoding of the branch instruction at patcheeOffset.
|
||||||
|
uint32_t instr;
|
||||||
|
// True If the patch is to be written in ARM state, otherwise the patch will
|
||||||
|
// be written in Thumb state.
|
||||||
|
bool isARM;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Return true if the half-word, when taken as the first of a pair of halfwords
|
||||||
|
// is the first half of a 32-bit instruction.
|
||||||
|
// Reference from ARM Architecure Reference Manual ARMv7-A and ARMv7-R edition
|
||||||
|
// section A6.3: 32-bit Thumb instruction encoding
|
||||||
|
// | HW1 | HW2 |
|
||||||
|
// | 1 1 1 | op1 (2) | op2 (7) | x (4) |op| x (15) |
|
||||||
|
// With op1 == 0b00, a 16-bit instruction is encoded.
|
||||||
|
//
|
||||||
|
// We test only the first halfword, looking for op != 0b00.
|
||||||
|
static bool is32bitInstruction(uint16_t hw) {
|
||||||
|
return (hw & 0xe000) == 0xe000 && (hw & 0x1800) != 0x0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference from ARM Architecure Reference Manual ARMv7-A and ARMv7-R edition
|
||||||
|
// section A6.3.4 Branches and miscellaneous control.
|
||||||
|
// | HW1 | HW2 |
|
||||||
|
// | 1 1 1 | 1 0 | op (7) | x (4) | 1 | op1 (3) | op2 (4) | imm8 (8) |
|
||||||
|
// op1 == 0x0 op != x111xxx | Conditional branch (Bcc.W)
|
||||||
|
// op1 == 0x1 | Branch (B.W)
|
||||||
|
// op1 == 1x0 | Branch with Link and Exchange (BLX.w)
|
||||||
|
// op1 == 1x1 | Branch with Link (BL.W)
|
||||||
|
|
||||||
|
static bool isBcc(uint32_t instr) {
|
||||||
|
return (instr & 0xf800d000) == 0xf0008000 &&
|
||||||
|
(instr & 0x03800000) != 0x03800000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isB(uint32_t instr) { return (instr & 0xf800d000) == 0xf0009000; }
|
||||||
|
|
||||||
|
static bool isBLX(uint32_t instr) { return (instr & 0xf800d000) == 0xf000c000; }
|
||||||
|
|
||||||
|
static bool isBL(uint32_t instr) { return (instr & 0xf800d000) == 0xf000d000; }
|
||||||
|
|
||||||
|
static bool is32bitBranch(uint32_t instr) {
|
||||||
|
return isBcc(instr) || isB(instr) || isBL(instr) || isBLX(instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Patch657417Section::Patch657417Section(InputSection *p, uint64_t off,
|
||||||
|
uint32_t instr, bool isARM)
|
||||||
|
: SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 4,
|
||||||
|
".text.patch"),
|
||||||
|
patchee(p), patcheeOffset(off), instr(instr), isARM(isARM) {
|
||||||
|
parent = p->getParent();
|
||||||
|
patchSym = addSyntheticLocal(
|
||||||
|
saver.save("__CortexA8657417_" + utohexstr(getBranchAddr())), STT_FUNC,
|
||||||
|
isARM ? 0 : 1, getSize(), *this);
|
||||||
|
addSyntheticLocal(saver.save(isARM ? "$a" : "$t"), STT_NOTYPE, 0, 0, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Patch657417Section::getBranchAddr() const {
|
||||||
|
return patchee->getVA(patcheeOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a branch instruction instr at sourceAddr work out its destination
|
||||||
|
// address. This is only used when the branch instruction has no relocation.
|
||||||
|
static uint64_t getThumbDestAddr(uint64_t sourceAddr, uint32_t instr) {
|
||||||
|
uint8_t buf[4];
|
||||||
|
write16le(buf, instr >> 16);
|
||||||
|
write16le(buf + 2, instr & 0x0000ffff);
|
||||||
|
int64_t offset;
|
||||||
|
if (isBcc(instr))
|
||||||
|
offset = target->getImplicitAddend(buf, R_ARM_THM_JUMP19);
|
||||||
|
else if (isB(instr))
|
||||||
|
offset = target->getImplicitAddend(buf, R_ARM_THM_JUMP24);
|
||||||
|
else
|
||||||
|
offset = target->getImplicitAddend(buf, R_ARM_THM_CALL);
|
||||||
|
return sourceAddr + offset + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patch657417Section::writeTo(uint8_t *buf) {
|
||||||
|
// The base instruction of the patch is always a 32-bit unconditional branch.
|
||||||
|
if (isARM)
|
||||||
|
write32le(buf, 0xea000000);
|
||||||
|
else
|
||||||
|
write32le(buf, 0x9000f000);
|
||||||
|
// If we have a relocation then apply it. For a SyntheticSection buf already
|
||||||
|
// has outSecOff added, but relocateAlloc also adds outSecOff so we need to
|
||||||
|
// subtract to avoid double counting.
|
||||||
|
if (!relocations.empty()) {
|
||||||
|
relocateAlloc(buf - outSecOff, buf - outSecOff + getSize());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have a relocation then we must calculate and write the offset
|
||||||
|
// ourselves.
|
||||||
|
// Get the destination offset from the addend in the branch instruction.
|
||||||
|
// We cannot use the instruction in the patchee section as this will have
|
||||||
|
// been altered to point to us!
|
||||||
|
uint64_t s = getThumbDestAddr(getBranchAddr(), instr);
|
||||||
|
uint64_t p = getVA(4);
|
||||||
|
target->relocateOne(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a branch instruction spanning two 4KiB regions, at offset off from the
|
||||||
|
// start of isec, return true if the destination of the branch is within the
|
||||||
|
// first of the two 4Kib regions.
|
||||||
|
static bool branchDestInFirstRegion(const InputSection *isec, uint64_t off,
|
||||||
|
uint32_t instr, const Relocation *r) {
|
||||||
|
uint64_t sourceAddr = isec->getVA(0) + off;
|
||||||
|
assert((sourceAddr & 0xfff) == 0xffe);
|
||||||
|
uint64_t destAddr = sourceAddr;
|
||||||
|
// If there is a branch relocation at the same offset we must use this to
|
||||||
|
// find the destination address as the branch could be indirected via a thunk
|
||||||
|
// or the PLT.
|
||||||
|
if (r) {
|
||||||
|
uint64_t dst = (r->expr == R_PLT_PC) ? r->sym->getPltVA() : r->sym->getVA();
|
||||||
|
// Account for Thumb PC bias, usually cancelled to 0 by addend of -4.
|
||||||
|
destAddr = dst + r->addend + 4;
|
||||||
|
} else {
|
||||||
|
// If there is no relocation, we must have an intra-section branch
|
||||||
|
// We must extract the offset from the addend manually.
|
||||||
|
destAddr = getThumbDestAddr(sourceAddr, instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (destAddr & 0xfffff000) == (sourceAddr & 0xfffff000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if a branch can reach a patch section placed after isec.
|
||||||
|
// The Bcc.w instruction has a range of 1 MiB, all others have 16 MiB.
|
||||||
|
static bool patchInRange(const InputSection *isec, uint64_t off,
|
||||||
|
uint32_t instr) {
|
||||||
|
|
||||||
|
// We need the branch at source to reach a patch section placed immediately
|
||||||
|
// after isec. As there can be more than one patch in the patch section we
|
||||||
|
// add 0x100 as contingency to account for worst case of 1 branch every 4KiB
|
||||||
|
// for a 1 MiB range.
|
||||||
|
return target->inBranchRange(
|
||||||
|
isBcc(instr) ? R_ARM_THM_JUMP19 : R_ARM_THM_JUMP24, isec->getVA(off),
|
||||||
|
isec->getVA() + isec->getSize() + 0x100);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ScanResult {
|
||||||
|
// Offset of branch within its InputSection.
|
||||||
|
uint64_t off;
|
||||||
|
// Cached decoding of the branch instruction.
|
||||||
|
uint32_t instr;
|
||||||
|
// Branch relocation at off. Will be nullptr if no relocation exists.
|
||||||
|
Relocation *rel;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Detect the erratum sequence, returning the offset of the branch instruction
|
||||||
|
// and a decoding of the branch. If the erratum sequence is not found then
|
||||||
|
// return an offset of 0 for the branch. 0 is a safe value to use for no patch
|
||||||
|
// as there must be at least one 32-bit non-branch instruction before the
|
||||||
|
// branch so the minimum offset for a patch is 4.
|
||||||
|
static ScanResult scanCortexA8Errata657417(InputSection *isec, uint64_t &off,
|
||||||
|
uint64_t limit) {
|
||||||
|
uint64_t isecAddr = isec->getVA(0);
|
||||||
|
// Advance Off so that (isecAddr + off) modulo 0x1000 is at least 0xffa. We
|
||||||
|
// need to check for a 32-bit instruction immediately before a 32-bit branch
|
||||||
|
// at 0xffe modulo 0x1000.
|
||||||
|
off = alignTo(isecAddr + off, 0x1000, 0xffa) - isecAddr;
|
||||||
|
if (off >= limit || limit - off < 8) {
|
||||||
|
// Need at least 2 4-byte sized instructions to trigger erratum.
|
||||||
|
off = limit;
|
||||||
|
return {0, 0, nullptr};
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanResult scanRes = {0, 0, nullptr};
|
||||||
|
const uint8_t *buf = isec->data().begin();
|
||||||
|
// ARMv7-A Thumb 32-bit instructions are encoded 2 consecutive
|
||||||
|
// little-endian halfwords.
|
||||||
|
const ulittle16_t *instBuf = reinterpret_cast<const ulittle16_t *>(buf + off);
|
||||||
|
uint16_t hw11 = *instBuf++;
|
||||||
|
uint16_t hw12 = *instBuf++;
|
||||||
|
uint16_t hw21 = *instBuf++;
|
||||||
|
uint16_t hw22 = *instBuf++;
|
||||||
|
if (is32bitInstruction(hw11) && is32bitInstruction(hw21)) {
|
||||||
|
uint32_t instr1 = (hw11 << 16) | hw12;
|
||||||
|
uint32_t instr2 = (hw21 << 16) | hw22;
|
||||||
|
if (!is32bitBranch(instr1) && is32bitBranch(instr2)) {
|
||||||
|
// Find a relocation for the branch if it exists. This will be used
|
||||||
|
// to determine the target.
|
||||||
|
uint64_t branchOff = off + 4;
|
||||||
|
auto relIt = llvm::find_if(isec->relocations, [=](const Relocation &r) {
|
||||||
|
return r.offset == branchOff &&
|
||||||
|
(r.type == R_ARM_THM_JUMP19 || r.type == R_ARM_THM_JUMP24 ||
|
||||||
|
r.type == R_ARM_THM_CALL);
|
||||||
|
});
|
||||||
|
if (relIt != isec->relocations.end())
|
||||||
|
scanRes.rel = &(*relIt);
|
||||||
|
if (branchDestInFirstRegion(isec, branchOff, instr2, scanRes.rel)) {
|
||||||
|
if (patchInRange(isec, branchOff, instr2)) {
|
||||||
|
scanRes.off = branchOff;
|
||||||
|
scanRes.instr = instr2;
|
||||||
|
} else {
|
||||||
|
warn(toString(isec->file) +
|
||||||
|
": skipping cortex-a8 657417 erratum sequence, section " +
|
||||||
|
isec->name + " is too large to patch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
off += 0x1000;
|
||||||
|
return scanRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMErr657417Patcher::init() {
|
||||||
|
// The Arm ABI permits a mix of ARM, Thumb and Data in the same
|
||||||
|
// InputSection. We must only scan Thumb instructions to avoid false
|
||||||
|
// matches. We use the mapping symbols in the InputObjects to identify this
|
||||||
|
// data, caching the results in sectionMap so we don't have to recalculate
|
||||||
|
// it each pass.
|
||||||
|
|
||||||
|
// The ABI Section 4.5.5 Mapping symbols; defines local symbols that describe
|
||||||
|
// half open intervals [Symbol Value, Next Symbol Value) of code and data
|
||||||
|
// within sections. If there is no next symbol then the half open interval is
|
||||||
|
// [Symbol Value, End of section). The type, code or data, is determined by
|
||||||
|
// the mapping symbol name, $a for Arm code, $t for Thumb code, $d for data.
|
||||||
|
auto isArmMapSymbol = [](const Symbol *s) {
|
||||||
|
return s->getName() == "$a" || s->getName().startswith("$a.");
|
||||||
|
};
|
||||||
|
auto isThumbMapSymbol = [](const Symbol *s) {
|
||||||
|
return s->getName() == "$t" || s->getName().startswith("$t.");
|
||||||
|
};
|
||||||
|
auto isDataMapSymbol = [](const Symbol *s) {
|
||||||
|
return s->getName() == "$d" || s->getName().startswith("$d.");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Collect mapping symbols for every executable InputSection.
|
||||||
|
for (InputFile *file : objectFiles) {
|
||||||
|
auto *f = cast<ObjFile<ELF32LE>>(file);
|
||||||
|
for (Symbol *s : f->getLocalSymbols()) {
|
||||||
|
auto *def = dyn_cast<Defined>(s);
|
||||||
|
if (!def)
|
||||||
|
continue;
|
||||||
|
if (!isArmMapSymbol(def) && !isThumbMapSymbol(def) &&
|
||||||
|
!isDataMapSymbol(def))
|
||||||
|
continue;
|
||||||
|
if (auto *sec = dyn_cast_or_null<InputSection>(def->section))
|
||||||
|
if (sec->flags & SHF_EXECINSTR)
|
||||||
|
sectionMap[sec].push_back(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// For each InputSection make sure the mapping symbols are in sorted in
|
||||||
|
// ascending order and are in alternating Thumb, non-Thumb order.
|
||||||
|
for (auto &kv : sectionMap) {
|
||||||
|
std::vector<const Defined *> &mapSyms = kv.second;
|
||||||
|
llvm::stable_sort(mapSyms, [](const Defined *a, const Defined *b) {
|
||||||
|
return a->value < b->value;
|
||||||
|
});
|
||||||
|
mapSyms.erase(std::unique(mapSyms.begin(), mapSyms.end(),
|
||||||
|
[=](const Defined *a, const Defined *b) {
|
||||||
|
return (isThumbMapSymbol(a) ==
|
||||||
|
isThumbMapSymbol(b));
|
||||||
|
}),
|
||||||
|
mapSyms.end());
|
||||||
|
// Always start with a Thumb Mapping Symbol
|
||||||
|
if (!mapSyms.empty() && !isThumbMapSymbol(mapSyms.front()))
|
||||||
|
mapSyms.erase(mapSyms.begin());
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMErr657417Patcher::insertPatches(
|
||||||
|
InputSectionDescription &isd, std::vector<Patch657417Section *> &patches) {
|
||||||
|
uint64_t spacing = 0x100000 - 0x7500;
|
||||||
|
uint64_t isecLimit;
|
||||||
|
uint64_t prevIsecLimit = isd.sections.front()->outSecOff;
|
||||||
|
uint64_t patchUpperBound = prevIsecLimit + spacing;
|
||||||
|
uint64_t outSecAddr = isd.sections.front()->getParent()->addr;
|
||||||
|
|
||||||
|
// Set the outSecOff of patches to the place where we want to insert them.
|
||||||
|
// We use a similar strategy to initial thunk placement, using 1 MiB as the
|
||||||
|
// range of the Thumb-2 conditional branch with a contingency accounting for
|
||||||
|
// thunk generation.
|
||||||
|
auto patchIt = patches.begin();
|
||||||
|
auto patchEnd = patches.end();
|
||||||
|
for (const InputSection *isec : isd.sections) {
|
||||||
|
isecLimit = isec->outSecOff + isec->getSize();
|
||||||
|
if (isecLimit > patchUpperBound) {
|
||||||
|
for (; patchIt != patchEnd; ++patchIt) {
|
||||||
|
if ((*patchIt)->getBranchAddr() - outSecAddr >= prevIsecLimit)
|
||||||
|
break;
|
||||||
|
(*patchIt)->outSecOff = prevIsecLimit;
|
||||||
|
}
|
||||||
|
patchUpperBound = prevIsecLimit + spacing;
|
||||||
|
}
|
||||||
|
prevIsecLimit = isecLimit;
|
||||||
|
}
|
||||||
|
for (; patchIt != patchEnd; ++patchIt)
|
||||||
|
(*patchIt)->outSecOff = isecLimit;
|
||||||
|
|
||||||
|
// Merge all patch sections. We use the outSecOff assigned above to
|
||||||
|
// determine the insertion point. This is ok as we only merge into an
|
||||||
|
// InputSectionDescription once per pass, and at the end of the pass
|
||||||
|
// assignAddresses() will recalculate all the outSecOff values.
|
||||||
|
std::vector<InputSection *> tmp;
|
||||||
|
tmp.reserve(isd.sections.size() + patches.size());
|
||||||
|
auto mergeCmp = [](const InputSection *a, const InputSection *b) {
|
||||||
|
if (a->outSecOff != b->outSecOff)
|
||||||
|
return a->outSecOff < b->outSecOff;
|
||||||
|
return isa<Patch657417Section>(a) && !isa<Patch657417Section>(b);
|
||||||
|
};
|
||||||
|
std::merge(isd.sections.begin(), isd.sections.end(), patches.begin(),
|
||||||
|
patches.end(), std::back_inserter(tmp), mergeCmp);
|
||||||
|
isd.sections = std::move(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a branch instruction described by ScanRes redirect it to a patch
|
||||||
|
// section containing an unconditional branch instruction to the target.
|
||||||
|
// Ensure that this patch section is 4-byte aligned so that the branch cannot
|
||||||
|
// span two 4 KiB regions. Place the patch section so that it is always after
|
||||||
|
// isec so the branch we are patching always goes forwards.
|
||||||
|
static void implementPatch(ScanResult sr, InputSection *isec,
|
||||||
|
std::vector<Patch657417Section *> &patches) {
|
||||||
|
|
||||||
|
log("detected cortex-a8-657419 erratum sequence starting at " +
|
||||||
|
utohexstr(isec->getVA(sr.off)) + " in unpatched output.");
|
||||||
|
Patch657417Section *psec;
|
||||||
|
// We have two cases to deal with.
|
||||||
|
// Case 1. There is a relocation at patcheeOffset to a symbol. The
|
||||||
|
// unconditional branch in the patch must have a relocation so that any
|
||||||
|
// further redirection via the PLT or a Thunk happens as normal. At
|
||||||
|
// patcheeOffset we redirect the existing relocation to a Symbol defined at
|
||||||
|
// the start of the patch section.
|
||||||
|
//
|
||||||
|
// Case 2. There is no relocation at patcheeOffset. We are unlikely to have
|
||||||
|
// a symbol that we can use as a target for a relocation in the patch section.
|
||||||
|
// Luckily we know that the destination cannot be indirected via the PLT or
|
||||||
|
// a Thunk so we can just write the destination directly.
|
||||||
|
if (sr.rel) {
|
||||||
|
// Case 1. We have an existing relocation to redirect to patch and a
|
||||||
|
// Symbol target.
|
||||||
|
|
||||||
|
// Create a branch relocation for the unconditional branch in the patch.
|
||||||
|
// This can be redirected via the PLT or Thunks.
|
||||||
|
RelType patchRelType = R_ARM_THM_JUMP24;
|
||||||
|
int64_t patchRelAddend = sr.rel->addend;
|
||||||
|
bool destIsARM = false;
|
||||||
|
if (isBL(sr.instr) || isBLX(sr.instr)) {
|
||||||
|
// The final target of the branch may be ARM or Thumb, if the target
|
||||||
|
// is ARM then we write the patch in ARM state to avoid a state change
|
||||||
|
// Thunk from the patch to the target.
|
||||||
|
uint64_t dstSymAddr = (sr.rel->expr == R_PLT_PC) ? sr.rel->sym->getPltVA()
|
||||||
|
: sr.rel->sym->getVA();
|
||||||
|
destIsARM = (dstSymAddr & 1) == 0;
|
||||||
|
}
|
||||||
|
psec = make<Patch657417Section>(isec, sr.off, sr.instr, destIsARM);
|
||||||
|
if (destIsARM) {
|
||||||
|
// The patch will be in ARM state. Use an ARM relocation and account for
|
||||||
|
// the larger ARM PC-bias of 8 rather than Thumb's 4.
|
||||||
|
patchRelType = R_ARM_JUMP24;
|
||||||
|
patchRelAddend -= 4;
|
||||||
|
}
|
||||||
|
psec->relocations.push_back(
|
||||||
|
Relocation{sr.rel->expr, patchRelType, 0, patchRelAddend, sr.rel->sym});
|
||||||
|
// Redirect the existing branch relocation to the patch.
|
||||||
|
sr.rel->expr = R_PC;
|
||||||
|
sr.rel->addend = -4;
|
||||||
|
sr.rel->sym = psec->patchSym;
|
||||||
|
} else {
|
||||||
|
// Case 2. We do not have a relocation to the patch. Add a relocation of the
|
||||||
|
// appropriate type to the patch at patcheeOffset.
|
||||||
|
|
||||||
|
// The destination is ARM if we have a BLX.
|
||||||
|
psec = make<Patch657417Section>(isec, sr.off, sr.instr, isBLX(sr.instr));
|
||||||
|
RelType type;
|
||||||
|
if (isBcc(sr.instr))
|
||||||
|
type = R_ARM_THM_JUMP19;
|
||||||
|
else if (isB(sr.instr))
|
||||||
|
type = R_ARM_THM_JUMP24;
|
||||||
|
else
|
||||||
|
type = R_ARM_THM_CALL;
|
||||||
|
isec->relocations.push_back(
|
||||||
|
Relocation{R_PC, type, sr.off, -4, psec->patchSym});
|
||||||
|
}
|
||||||
|
patches.push_back(psec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan all the instructions in InputSectionDescription, for each instance of
|
||||||
|
// the erratum sequence create a Patch657417Section. We return the list of
|
||||||
|
// Patch657417Sections that need to be applied to the InputSectionDescription.
|
||||||
|
std::vector<Patch657417Section *>
|
||||||
|
ARMErr657417Patcher::patchInputSectionDescription(
|
||||||
|
InputSectionDescription &isd) {
|
||||||
|
std::vector<Patch657417Section *> patches;
|
||||||
|
for (InputSection *isec : isd.sections) {
|
||||||
|
// LLD doesn't use the erratum sequence in SyntheticSections.
|
||||||
|
if (isa<SyntheticSection>(isec))
|
||||||
|
continue;
|
||||||
|
// Use sectionMap to make sure we only scan Thumb code and not Arm or inline
|
||||||
|
// data. We have already sorted mapSyms in ascending order and removed
|
||||||
|
// consecutive mapping symbols of the same type. Our range of executable
|
||||||
|
// instructions to scan is therefore [thumbSym->value, nonThumbSym->value)
|
||||||
|
// or [thumbSym->value, section size).
|
||||||
|
std::vector<const Defined *> &mapSyms = sectionMap[isec];
|
||||||
|
|
||||||
|
auto thumbSym = mapSyms.begin();
|
||||||
|
while (thumbSym != mapSyms.end()) {
|
||||||
|
auto nonThumbSym = std::next(thumbSym);
|
||||||
|
uint64_t off = (*thumbSym)->value;
|
||||||
|
uint64_t limit = (nonThumbSym == mapSyms.end()) ? isec->data().size()
|
||||||
|
: (*nonThumbSym)->value;
|
||||||
|
|
||||||
|
while (off < limit) {
|
||||||
|
ScanResult sr = scanCortexA8Errata657417(isec, off, limit);
|
||||||
|
if (sr.off)
|
||||||
|
implementPatch(sr, isec, patches);
|
||||||
|
}
|
||||||
|
if (nonThumbSym == mapSyms.end())
|
||||||
|
break;
|
||||||
|
thumbSym = std::next(nonThumbSym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return patches;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ARMErr657417Patcher::createFixes() {
|
||||||
|
if (!initialized)
|
||||||
|
init();
|
||||||
|
|
||||||
|
bool addressesChanged = false;
|
||||||
|
for (OutputSection *os : outputSections) {
|
||||||
|
if (!(os->flags & SHF_ALLOC) || !(os->flags & SHF_EXECINSTR))
|
||||||
|
continue;
|
||||||
|
for (BaseCommand *bc : os->sectionCommands)
|
||||||
|
if (auto *isd = dyn_cast<InputSectionDescription>(bc)) {
|
||||||
|
std::vector<Patch657417Section *> patches =
|
||||||
|
patchInputSectionDescription(*isd);
|
||||||
|
if (!patches.empty()) {
|
||||||
|
insertPatches(*isd, patches);
|
||||||
|
addressesChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return addressesChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
51
contrib/llvm-project/lld/ELF/ARMErrataFix.h
Normal file
51
contrib/llvm-project/lld/ELF/ARMErrataFix.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
//===- ARMErrataFix.h -------------------------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLD_ELF_ARMA8ERRATAFIX_H
|
||||||
|
#define LLD_ELF_ARMA8ERRATAFIX_H
|
||||||
|
|
||||||
|
#include "lld/Common/LLVM.h"
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
|
class Defined;
|
||||||
|
class InputSection;
|
||||||
|
struct InputSectionDescription;
|
||||||
|
class OutputSection;
|
||||||
|
class Patch657417Section;
|
||||||
|
|
||||||
|
class ARMErr657417Patcher {
|
||||||
|
public:
|
||||||
|
// Return true if Patches have been added to the OutputSections.
|
||||||
|
bool createFixes();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Patch657417Section *>
|
||||||
|
patchInputSectionDescription(InputSectionDescription &isd);
|
||||||
|
|
||||||
|
void insertPatches(InputSectionDescription &isd,
|
||||||
|
std::vector<Patch657417Section *> &patches);
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
// A cache of the mapping symbols defined by the InputSection sorted in order
|
||||||
|
// of ascending value with redundant symbols removed. These describe
|
||||||
|
// the ranges of code and data in an executable InputSection.
|
||||||
|
llvm::DenseMap<InputSection *, std::vector<const Defined *>> sectionMap;
|
||||||
|
|
||||||
|
bool initialized = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
|
||||||
|
#endif
|
@ -17,13 +17,14 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
// Page(Expr) is the page address of the expression Expr, defined
|
// Page(Expr) is the page address of the expression Expr, defined
|
||||||
// as (Expr & ~0xFFF). (This applies even if the machine page size
|
// as (Expr & ~0xFFF). (This applies even if the machine page size
|
||||||
// supported by the platform has a different value.)
|
// supported by the platform has a different value.)
|
||||||
uint64_t elf::getAArch64Page(uint64_t expr) {
|
uint64_t getAArch64Page(uint64_t expr) {
|
||||||
return expr & ~static_cast<uint64_t>(0xFFF);
|
return expr & ~static_cast<uint64_t>(0xFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,6 +77,26 @@ AArch64::AArch64() {
|
|||||||
RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
||||||
const uint8_t *loc) const {
|
const uint8_t *loc) const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case R_AARCH64_ABS16:
|
||||||
|
case R_AARCH64_ABS32:
|
||||||
|
case R_AARCH64_ABS64:
|
||||||
|
case R_AARCH64_ADD_ABS_LO12_NC:
|
||||||
|
case R_AARCH64_LDST128_ABS_LO12_NC:
|
||||||
|
case R_AARCH64_LDST16_ABS_LO12_NC:
|
||||||
|
case R_AARCH64_LDST32_ABS_LO12_NC:
|
||||||
|
case R_AARCH64_LDST64_ABS_LO12_NC:
|
||||||
|
case R_AARCH64_LDST8_ABS_LO12_NC:
|
||||||
|
case R_AARCH64_MOVW_SABS_G0:
|
||||||
|
case R_AARCH64_MOVW_SABS_G1:
|
||||||
|
case R_AARCH64_MOVW_SABS_G2:
|
||||||
|
case R_AARCH64_MOVW_UABS_G0:
|
||||||
|
case R_AARCH64_MOVW_UABS_G0_NC:
|
||||||
|
case R_AARCH64_MOVW_UABS_G1:
|
||||||
|
case R_AARCH64_MOVW_UABS_G1_NC:
|
||||||
|
case R_AARCH64_MOVW_UABS_G2:
|
||||||
|
case R_AARCH64_MOVW_UABS_G2_NC:
|
||||||
|
case R_AARCH64_MOVW_UABS_G3:
|
||||||
|
return R_ABS;
|
||||||
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
case R_AARCH64_TLSDESC_ADR_PAGE21:
|
||||||
return R_AARCH64_TLSDESC_PAGE;
|
return R_AARCH64_TLSDESC_PAGE;
|
||||||
case R_AARCH64_TLSDESC_LD64_LO12:
|
case R_AARCH64_TLSDESC_LD64_LO12:
|
||||||
@ -90,6 +111,11 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
|||||||
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
|
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
|
||||||
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
|
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
|
||||||
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
|
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
|
||||||
return R_TLS;
|
return R_TLS;
|
||||||
case R_AARCH64_CALL26:
|
case R_AARCH64_CALL26:
|
||||||
case R_AARCH64_CONDBR19:
|
case R_AARCH64_CONDBR19:
|
||||||
@ -101,6 +127,13 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
|||||||
case R_AARCH64_PREL64:
|
case R_AARCH64_PREL64:
|
||||||
case R_AARCH64_ADR_PREL_LO21:
|
case R_AARCH64_ADR_PREL_LO21:
|
||||||
case R_AARCH64_LD_PREL_LO19:
|
case R_AARCH64_LD_PREL_LO19:
|
||||||
|
case R_AARCH64_MOVW_PREL_G0:
|
||||||
|
case R_AARCH64_MOVW_PREL_G0_NC:
|
||||||
|
case R_AARCH64_MOVW_PREL_G1:
|
||||||
|
case R_AARCH64_MOVW_PREL_G1_NC:
|
||||||
|
case R_AARCH64_MOVW_PREL_G2:
|
||||||
|
case R_AARCH64_MOVW_PREL_G2_NC:
|
||||||
|
case R_AARCH64_MOVW_PREL_G3:
|
||||||
return R_PC;
|
return R_PC;
|
||||||
case R_AARCH64_ADR_PREL_PG_HI21:
|
case R_AARCH64_ADR_PREL_PG_HI21:
|
||||||
case R_AARCH64_ADR_PREL_PG_HI21_NC:
|
case R_AARCH64_ADR_PREL_PG_HI21_NC:
|
||||||
@ -114,7 +147,9 @@ RelExpr AArch64::getRelExpr(RelType type, const Symbol &s,
|
|||||||
case R_AARCH64_NONE:
|
case R_AARCH64_NONE:
|
||||||
return R_NONE;
|
return R_NONE;
|
||||||
default:
|
default:
|
||||||
return R_ABS;
|
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
|
||||||
|
") against symbol " + toString(s));
|
||||||
|
return R_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +282,26 @@ static void or32AArch64Imm(uint8_t *l, uint64_t imm) {
|
|||||||
or32le(l, (imm & 0xFFF) << 10);
|
or32le(l, (imm & 0xFFF) << 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the immediate field in an AArch64 movk, movn or movz instruction
|
||||||
|
// for a signed relocation, and update the opcode of a movn or movz instruction
|
||||||
|
// to match the sign of the operand.
|
||||||
|
static void writeSMovWImm(uint8_t *loc, uint32_t imm) {
|
||||||
|
uint32_t inst = read32le(loc);
|
||||||
|
// Opcode field is bits 30, 29, with 10 = movz, 00 = movn and 11 = movk.
|
||||||
|
if (!(inst & (1 << 29))) {
|
||||||
|
// movn or movz.
|
||||||
|
if (imm & 0x10000) {
|
||||||
|
// Change opcode to movn, which takes an inverted operand.
|
||||||
|
imm ^= 0xFFFF;
|
||||||
|
inst &= ~(1 << 30);
|
||||||
|
} else {
|
||||||
|
// Change opcode to movz.
|
||||||
|
inst |= 1 << 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write32le(loc, inst | ((imm & 0xFFFF) << 5));
|
||||||
|
}
|
||||||
|
|
||||||
void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case R_AARCH64_ABS16:
|
case R_AARCH64_ABS16:
|
||||||
@ -326,18 +381,56 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
checkAlignment(loc, val, 16, type);
|
checkAlignment(loc, val, 16, type);
|
||||||
or32AArch64Imm(loc, getBits(val, 4, 11));
|
or32AArch64Imm(loc, getBits(val, 4, 11));
|
||||||
break;
|
break;
|
||||||
|
case R_AARCH64_MOVW_UABS_G0:
|
||||||
|
checkUInt(loc, val, 16, type);
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
case R_AARCH64_MOVW_UABS_G0_NC:
|
case R_AARCH64_MOVW_UABS_G0_NC:
|
||||||
or32le(loc, (val & 0xFFFF) << 5);
|
or32le(loc, (val & 0xFFFF) << 5);
|
||||||
break;
|
break;
|
||||||
|
case R_AARCH64_MOVW_UABS_G1:
|
||||||
|
checkUInt(loc, val, 32, type);
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
case R_AARCH64_MOVW_UABS_G1_NC:
|
case R_AARCH64_MOVW_UABS_G1_NC:
|
||||||
or32le(loc, (val & 0xFFFF0000) >> 11);
|
or32le(loc, (val & 0xFFFF0000) >> 11);
|
||||||
break;
|
break;
|
||||||
|
case R_AARCH64_MOVW_UABS_G2:
|
||||||
|
checkUInt(loc, val, 48, type);
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
case R_AARCH64_MOVW_UABS_G2_NC:
|
case R_AARCH64_MOVW_UABS_G2_NC:
|
||||||
or32le(loc, (val & 0xFFFF00000000) >> 27);
|
or32le(loc, (val & 0xFFFF00000000) >> 27);
|
||||||
break;
|
break;
|
||||||
case R_AARCH64_MOVW_UABS_G3:
|
case R_AARCH64_MOVW_UABS_G3:
|
||||||
or32le(loc, (val & 0xFFFF000000000000) >> 43);
|
or32le(loc, (val & 0xFFFF000000000000) >> 43);
|
||||||
break;
|
break;
|
||||||
|
case R_AARCH64_MOVW_PREL_G0:
|
||||||
|
case R_AARCH64_MOVW_SABS_G0:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
|
||||||
|
checkInt(loc, val, 17, type);
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
|
case R_AARCH64_MOVW_PREL_G0_NC:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
|
||||||
|
writeSMovWImm(loc, val);
|
||||||
|
break;
|
||||||
|
case R_AARCH64_MOVW_PREL_G1:
|
||||||
|
case R_AARCH64_MOVW_SABS_G1:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
|
||||||
|
checkInt(loc, val, 33, type);
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
|
case R_AARCH64_MOVW_PREL_G1_NC:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
|
||||||
|
writeSMovWImm(loc, val >> 16);
|
||||||
|
break;
|
||||||
|
case R_AARCH64_MOVW_PREL_G2:
|
||||||
|
case R_AARCH64_MOVW_SABS_G2:
|
||||||
|
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
|
||||||
|
checkInt(loc, val, 49, type);
|
||||||
|
LLVM_FALLTHROUGH;
|
||||||
|
case R_AARCH64_MOVW_PREL_G2_NC:
|
||||||
|
writeSMovWImm(loc, val >> 32);
|
||||||
|
break;
|
||||||
|
case R_AARCH64_MOVW_PREL_G3:
|
||||||
|
writeSMovWImm(loc, val >> 48);
|
||||||
|
break;
|
||||||
case R_AARCH64_TSTBR14:
|
case R_AARCH64_TSTBR14:
|
||||||
checkInt(loc, val, 16, type);
|
checkInt(loc, val, 16, type);
|
||||||
or32le(loc, (val & 0xFFFC) << 3);
|
or32le(loc, (val & 0xFFFC) << 3);
|
||||||
@ -351,7 +444,7 @@ void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
or32AArch64Imm(loc, val);
|
or32AArch64Imm(loc, val);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
llvm_unreachable("unknown relocation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,4 +680,7 @@ static TargetInfo *getTargetInfo() {
|
|||||||
return &t;
|
return &t;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
|
TargetInfo *getAArch64TargetInfo() { return getTargetInfo(); }
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -17,8 +17,9 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class AMDGPU final : public TargetInfo {
|
class AMDGPU final : public TargetInfo {
|
||||||
@ -107,7 +108,10 @@ RelType AMDGPU::getDynRel(RelType type) const {
|
|||||||
return R_AMDGPU_NONE;
|
return R_AMDGPU_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getAMDGPUTargetInfo() {
|
TargetInfo *getAMDGPUTargetInfo() {
|
||||||
static AMDGPU target;
|
static AMDGPU target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -18,8 +18,9 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class ARM final : public TargetInfo {
|
class ARM final : public TargetInfo {
|
||||||
@ -600,7 +601,10 @@ int64_t ARM::getImplicitAddend(const uint8_t *buf, RelType type) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getARMTargetInfo() {
|
TargetInfo *getARMTargetInfo() {
|
||||||
static ARM target;
|
static ARM target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -36,8 +36,9 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class AVR final : public TargetInfo {
|
class AVR final : public TargetInfo {
|
||||||
@ -70,7 +71,10 @@ void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getAVRTargetInfo() {
|
TargetInfo *getAVRTargetInfo() {
|
||||||
static AVR target;
|
static AVR target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -19,8 +19,9 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class Hexagon final : public TargetInfo {
|
class Hexagon final : public TargetInfo {
|
||||||
@ -29,6 +30,7 @@ class Hexagon final : public TargetInfo {
|
|||||||
uint32_t calcEFlags() const override;
|
uint32_t calcEFlags() const override;
|
||||||
RelExpr getRelExpr(RelType type, const Symbol &s,
|
RelExpr getRelExpr(RelType type, const Symbol &s,
|
||||||
const uint8_t *loc) const override;
|
const uint8_t *loc) const override;
|
||||||
|
RelType getDynRel(RelType type) const override;
|
||||||
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
|
||||||
void writePltHeader(uint8_t *buf) const override;
|
void writePltHeader(uint8_t *buf) const override;
|
||||||
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr,
|
||||||
@ -86,25 +88,47 @@ static uint32_t applyMask(uint32_t mask, uint32_t data) {
|
|||||||
RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
|
RelExpr Hexagon::getRelExpr(RelType type, const Symbol &s,
|
||||||
const uint8_t *loc) const {
|
const uint8_t *loc) const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case R_HEX_NONE:
|
||||||
|
return R_NONE;
|
||||||
|
case R_HEX_6_X:
|
||||||
|
case R_HEX_8_X:
|
||||||
|
case R_HEX_9_X:
|
||||||
|
case R_HEX_10_X:
|
||||||
|
case R_HEX_11_X:
|
||||||
|
case R_HEX_12_X:
|
||||||
|
case R_HEX_16_X:
|
||||||
|
case R_HEX_32:
|
||||||
|
case R_HEX_32_6_X:
|
||||||
|
case R_HEX_HI16:
|
||||||
|
case R_HEX_LO16:
|
||||||
|
return R_ABS;
|
||||||
case R_HEX_B9_PCREL:
|
case R_HEX_B9_PCREL:
|
||||||
case R_HEX_B9_PCREL_X:
|
|
||||||
case R_HEX_B13_PCREL:
|
case R_HEX_B13_PCREL:
|
||||||
case R_HEX_B15_PCREL:
|
case R_HEX_B15_PCREL:
|
||||||
case R_HEX_B15_PCREL_X:
|
|
||||||
case R_HEX_6_PCREL_X:
|
case R_HEX_6_PCREL_X:
|
||||||
case R_HEX_32_PCREL:
|
case R_HEX_32_PCREL:
|
||||||
return R_PC;
|
return R_PC;
|
||||||
|
case R_HEX_B9_PCREL_X:
|
||||||
|
case R_HEX_B15_PCREL_X:
|
||||||
case R_HEX_B22_PCREL:
|
case R_HEX_B22_PCREL:
|
||||||
case R_HEX_PLT_B22_PCREL:
|
case R_HEX_PLT_B22_PCREL:
|
||||||
case R_HEX_B22_PCREL_X:
|
case R_HEX_B22_PCREL_X:
|
||||||
case R_HEX_B32_PCREL_X:
|
case R_HEX_B32_PCREL_X:
|
||||||
return R_PLT_PC;
|
return R_PLT_PC;
|
||||||
|
case R_HEX_GOTREL_11_X:
|
||||||
|
case R_HEX_GOTREL_16_X:
|
||||||
|
case R_HEX_GOTREL_32_6_X:
|
||||||
|
case R_HEX_GOTREL_HI16:
|
||||||
|
case R_HEX_GOTREL_LO16:
|
||||||
|
return R_GOTPLTREL;
|
||||||
case R_HEX_GOT_11_X:
|
case R_HEX_GOT_11_X:
|
||||||
case R_HEX_GOT_16_X:
|
case R_HEX_GOT_16_X:
|
||||||
case R_HEX_GOT_32_6_X:
|
case R_HEX_GOT_32_6_X:
|
||||||
return R_HEXAGON_GOT;
|
return R_GOTPLT;
|
||||||
default:
|
default:
|
||||||
return R_ABS;
|
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
|
||||||
|
") against symbol " + toString(s));
|
||||||
|
return R_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +221,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
break;
|
break;
|
||||||
case R_HEX_11_X:
|
case R_HEX_11_X:
|
||||||
case R_HEX_GOT_11_X:
|
case R_HEX_GOT_11_X:
|
||||||
|
case R_HEX_GOTREL_11_X:
|
||||||
or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f));
|
or32le(loc, applyMask(findMaskR11(read32le(loc)), val & 0x3f));
|
||||||
break;
|
break;
|
||||||
case R_HEX_12_X:
|
case R_HEX_12_X:
|
||||||
@ -204,6 +229,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
break;
|
break;
|
||||||
case R_HEX_16_X: // These relocs only have 6 effective bits.
|
case R_HEX_16_X: // These relocs only have 6 effective bits.
|
||||||
case R_HEX_GOT_16_X:
|
case R_HEX_GOT_16_X:
|
||||||
|
case R_HEX_GOTREL_16_X:
|
||||||
or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f));
|
or32le(loc, applyMask(findMaskR16(read32le(loc)), val & 0x3f));
|
||||||
break;
|
break;
|
||||||
case R_HEX_32:
|
case R_HEX_32:
|
||||||
@ -212,18 +238,22 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
break;
|
break;
|
||||||
case R_HEX_32_6_X:
|
case R_HEX_32_6_X:
|
||||||
case R_HEX_GOT_32_6_X:
|
case R_HEX_GOT_32_6_X:
|
||||||
|
case R_HEX_GOTREL_32_6_X:
|
||||||
or32le(loc, applyMask(0x0fff3fff, val >> 6));
|
or32le(loc, applyMask(0x0fff3fff, val >> 6));
|
||||||
break;
|
break;
|
||||||
case R_HEX_B9_PCREL:
|
case R_HEX_B9_PCREL:
|
||||||
|
checkInt(loc, val, 11, type);
|
||||||
or32le(loc, applyMask(0x003000fe, val >> 2));
|
or32le(loc, applyMask(0x003000fe, val >> 2));
|
||||||
break;
|
break;
|
||||||
case R_HEX_B9_PCREL_X:
|
case R_HEX_B9_PCREL_X:
|
||||||
or32le(loc, applyMask(0x003000fe, val & 0x3f));
|
or32le(loc, applyMask(0x003000fe, val & 0x3f));
|
||||||
break;
|
break;
|
||||||
case R_HEX_B13_PCREL:
|
case R_HEX_B13_PCREL:
|
||||||
|
checkInt(loc, val, 15, type);
|
||||||
or32le(loc, applyMask(0x00202ffe, val >> 2));
|
or32le(loc, applyMask(0x00202ffe, val >> 2));
|
||||||
break;
|
break;
|
||||||
case R_HEX_B15_PCREL:
|
case R_HEX_B15_PCREL:
|
||||||
|
checkInt(loc, val, 17, type);
|
||||||
or32le(loc, applyMask(0x00df20fe, val >> 2));
|
or32le(loc, applyMask(0x00df20fe, val >> 2));
|
||||||
break;
|
break;
|
||||||
case R_HEX_B15_PCREL_X:
|
case R_HEX_B15_PCREL_X:
|
||||||
@ -231,6 +261,7 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
break;
|
break;
|
||||||
case R_HEX_B22_PCREL:
|
case R_HEX_B22_PCREL:
|
||||||
case R_HEX_PLT_B22_PCREL:
|
case R_HEX_PLT_B22_PCREL:
|
||||||
|
checkInt(loc, val, 22, type);
|
||||||
or32le(loc, applyMask(0x1ff3ffe, val >> 2));
|
or32le(loc, applyMask(0x1ff3ffe, val >> 2));
|
||||||
break;
|
break;
|
||||||
case R_HEX_B22_PCREL_X:
|
case R_HEX_B22_PCREL_X:
|
||||||
@ -239,15 +270,16 @@ void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
case R_HEX_B32_PCREL_X:
|
case R_HEX_B32_PCREL_X:
|
||||||
or32le(loc, applyMask(0x0fff3fff, val >> 6));
|
or32le(loc, applyMask(0x0fff3fff, val >> 6));
|
||||||
break;
|
break;
|
||||||
|
case R_HEX_GOTREL_HI16:
|
||||||
case R_HEX_HI16:
|
case R_HEX_HI16:
|
||||||
or32le(loc, applyMask(0x00c03fff, val >> 16));
|
or32le(loc, applyMask(0x00c03fff, val >> 16));
|
||||||
break;
|
break;
|
||||||
|
case R_HEX_GOTREL_LO16:
|
||||||
case R_HEX_LO16:
|
case R_HEX_LO16:
|
||||||
or32le(loc, applyMask(0x00c03fff, val));
|
or32le(loc, applyMask(0x00c03fff, val));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
|
llvm_unreachable("unknown relocation");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,7 +317,16 @@ void Hexagon::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
|||||||
relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
|
relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getHexagonTargetInfo() {
|
RelType Hexagon::getDynRel(RelType type) const {
|
||||||
|
if (type == R_HEX_32)
|
||||||
|
return type;
|
||||||
|
return R_HEX_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
TargetInfo *getHexagonTargetInfo() {
|
||||||
static Hexagon target;
|
static Hexagon target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -26,8 +26,9 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class MSP430 final : public TargetInfo {
|
class MSP430 final : public TargetInfo {
|
||||||
@ -87,7 +88,10 @@ void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getMSP430TargetInfo() {
|
TargetInfo *getMSP430TargetInfo() {
|
||||||
static MSP430 target;
|
static MSP430 target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -14,15 +14,13 @@
|
|||||||
#include "Thunks.h"
|
#include "Thunks.h"
|
||||||
#include "lld/Common/ErrorHandler.h"
|
#include "lld/Common/ErrorHandler.h"
|
||||||
#include "llvm/Object/ELF.h"
|
#include "llvm/Object/ELF.h"
|
||||||
#include "llvm/Support/Endian.h"
|
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
namespace {
|
namespace {
|
||||||
template <class ELFT> class MIPS final : public TargetInfo {
|
template <class ELFT> class MIPS final : public TargetInfo {
|
||||||
public:
|
public:
|
||||||
@ -85,8 +83,14 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case R_MIPS_JALR:
|
case R_MIPS_JALR:
|
||||||
|
// If the target symbol is not preemptible and is not microMIPS,
|
||||||
|
// it might be possible to replace jalr/jr instruction by bal/b.
|
||||||
|
// It depends on the target symbol's offset.
|
||||||
|
if (!s.isPreemptible && !(s.getVA() & 0x1))
|
||||||
|
return R_PC;
|
||||||
|
return R_NONE;
|
||||||
case R_MICROMIPS_JALR:
|
case R_MICROMIPS_JALR:
|
||||||
return R_HINT;
|
return R_NONE;
|
||||||
case R_MIPS_GPREL16:
|
case R_MIPS_GPREL16:
|
||||||
case R_MIPS_GPREL32:
|
case R_MIPS_GPREL32:
|
||||||
case R_MICROMIPS_GPREL16:
|
case R_MICROMIPS_GPREL16:
|
||||||
@ -120,15 +124,16 @@ RelExpr MIPS<ELFT>::getRelExpr(RelType type, const Symbol &s,
|
|||||||
case R_MIPS_TLS_DTPREL_LO16:
|
case R_MIPS_TLS_DTPREL_LO16:
|
||||||
case R_MIPS_TLS_DTPREL32:
|
case R_MIPS_TLS_DTPREL32:
|
||||||
case R_MIPS_TLS_DTPREL64:
|
case R_MIPS_TLS_DTPREL64:
|
||||||
|
case R_MICROMIPS_TLS_DTPREL_HI16:
|
||||||
|
case R_MICROMIPS_TLS_DTPREL_LO16:
|
||||||
|
return R_ABS;
|
||||||
case R_MIPS_TLS_TPREL_HI16:
|
case R_MIPS_TLS_TPREL_HI16:
|
||||||
case R_MIPS_TLS_TPREL_LO16:
|
case R_MIPS_TLS_TPREL_LO16:
|
||||||
case R_MIPS_TLS_TPREL32:
|
case R_MIPS_TLS_TPREL32:
|
||||||
case R_MIPS_TLS_TPREL64:
|
case R_MIPS_TLS_TPREL64:
|
||||||
case R_MICROMIPS_TLS_DTPREL_HI16:
|
|
||||||
case R_MICROMIPS_TLS_DTPREL_LO16:
|
|
||||||
case R_MICROMIPS_TLS_TPREL_HI16:
|
case R_MICROMIPS_TLS_TPREL_HI16:
|
||||||
case R_MICROMIPS_TLS_TPREL_LO16:
|
case R_MICROMIPS_TLS_TPREL_LO16:
|
||||||
return R_ABS;
|
return R_TLS;
|
||||||
case R_MIPS_PC32:
|
case R_MIPS_PC32:
|
||||||
case R_MIPS_PC16:
|
case R_MIPS_PC16:
|
||||||
case R_MIPS_PC19_S2:
|
case R_MIPS_PC19_S2:
|
||||||
@ -192,7 +197,7 @@ void MIPS<ELFT>::writeGotPlt(uint8_t *buf, const Symbol &) const {
|
|||||||
uint64_t va = in.plt->getVA();
|
uint64_t va = in.plt->getVA();
|
||||||
if (isMicroMips())
|
if (isMicroMips())
|
||||||
va |= 1;
|
va |= 1;
|
||||||
write32<ELFT::TargetEndianness>(buf, va);
|
write32(buf, va);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <endianness E> static uint32_t readShuffle(const uint8_t *loc) {
|
template <endianness E> static uint32_t readShuffle(const uint8_t *loc) {
|
||||||
@ -202,19 +207,18 @@ template <endianness E> static uint32_t readShuffle(const uint8_t *loc) {
|
|||||||
// as early as possible. To do so, little-endian binaries keep 16-bit
|
// as early as possible. To do so, little-endian binaries keep 16-bit
|
||||||
// words in a big-endian order. That is why we have to swap these
|
// words in a big-endian order. That is why we have to swap these
|
||||||
// words to get a correct value.
|
// words to get a correct value.
|
||||||
uint32_t v = read32<E>(loc);
|
uint32_t v = read32(loc);
|
||||||
if (E == support::little)
|
if (E == support::little)
|
||||||
return (v << 16) | (v >> 16);
|
return (v << 16) | (v >> 16);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <endianness E>
|
|
||||||
static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
static void writeValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
||||||
uint8_t shift) {
|
uint8_t shift) {
|
||||||
uint32_t instr = read32<E>(loc);
|
uint32_t instr = read32(loc);
|
||||||
uint32_t mask = 0xffffffff >> (32 - bitsSize);
|
uint32_t mask = 0xffffffff >> (32 - bitsSize);
|
||||||
uint32_t data = (instr & ~mask) | ((v >> shift) & mask);
|
uint32_t data = (instr & ~mask) | ((v >> shift) & mask);
|
||||||
write32<E>(loc, data);
|
write32(loc, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <endianness E>
|
template <endianness E>
|
||||||
@ -225,7 +229,7 @@ static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
|||||||
if (E == support::little)
|
if (E == support::little)
|
||||||
std::swap(words[0], words[1]);
|
std::swap(words[0], words[1]);
|
||||||
|
|
||||||
writeValue<E>(loc, v, bitsSize, shift);
|
writeValue(loc, v, bitsSize, shift);
|
||||||
|
|
||||||
if (E == support::little)
|
if (E == support::little)
|
||||||
std::swap(words[0], words[1]);
|
std::swap(words[0], words[1]);
|
||||||
@ -234,94 +238,92 @@ static void writeShuffleValue(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
|||||||
template <endianness E>
|
template <endianness E>
|
||||||
static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
static void writeMicroRelocation16(uint8_t *loc, uint64_t v, uint8_t bitsSize,
|
||||||
uint8_t shift) {
|
uint8_t shift) {
|
||||||
uint16_t instr = read16<E>(loc);
|
uint16_t instr = read16(loc);
|
||||||
uint16_t mask = 0xffff >> (16 - bitsSize);
|
uint16_t mask = 0xffff >> (16 - bitsSize);
|
||||||
uint16_t data = (instr & ~mask) | ((v >> shift) & mask);
|
uint16_t data = (instr & ~mask) | ((v >> shift) & mask);
|
||||||
write16<E>(loc, data);
|
write16(loc, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const {
|
template <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *buf) const {
|
||||||
const endianness e = ELFT::TargetEndianness;
|
|
||||||
if (isMicroMips()) {
|
if (isMicroMips()) {
|
||||||
uint64_t gotPlt = in.gotPlt->getVA();
|
uint64_t gotPlt = in.gotPlt->getVA();
|
||||||
uint64_t plt = in.plt->getVA();
|
uint64_t plt = in.plt->getVA();
|
||||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||||
memset(buf, 0, pltHeaderSize);
|
memset(buf, 0, pltHeaderSize);
|
||||||
|
|
||||||
write16<e>(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
|
write16(buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
|
||||||
write16<e>(buf + 4, 0xff23); // lw $25, 0($3)
|
write16(buf + 4, 0xff23); // lw $25, 0($3)
|
||||||
write16<e>(buf + 8, 0x0535); // subu16 $2, $2, $3
|
write16(buf + 8, 0x0535); // subu16 $2, $2, $3
|
||||||
write16<e>(buf + 10, 0x2525); // srl16 $2, $2, 2
|
write16(buf + 10, 0x2525); // srl16 $2, $2, 2
|
||||||
write16<e>(buf + 12, 0x3302); // addiu $24, $2, -2
|
write16(buf + 12, 0x3302); // addiu $24, $2, -2
|
||||||
write16<e>(buf + 14, 0xfffe);
|
write16(buf + 14, 0xfffe);
|
||||||
write16<e>(buf + 16, 0x0dff); // move $15, $31
|
write16(buf + 16, 0x0dff); // move $15, $31
|
||||||
if (isMipsR6()) {
|
if (isMipsR6()) {
|
||||||
write16<e>(buf + 18, 0x0f83); // move $28, $3
|
write16(buf + 18, 0x0f83); // move $28, $3
|
||||||
write16<e>(buf + 20, 0x472b); // jalrc $25
|
write16(buf + 20, 0x472b); // jalrc $25
|
||||||
write16<e>(buf + 22, 0x0c00); // nop
|
write16(buf + 22, 0x0c00); // nop
|
||||||
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
|
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
|
||||||
} else {
|
} else {
|
||||||
write16<e>(buf + 18, 0x45f9); // jalrc $25
|
write16(buf + 18, 0x45f9); // jalrc $25
|
||||||
write16<e>(buf + 20, 0x0f83); // move $28, $3
|
write16(buf + 20, 0x0f83); // move $28, $3
|
||||||
write16<e>(buf + 22, 0x0c00); // nop
|
write16(buf + 22, 0x0c00); // nop
|
||||||
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
|
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->mipsN32Abi) {
|
if (config->mipsN32Abi) {
|
||||||
write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||||
write32<e>(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
|
write32(buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
|
||||||
write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||||
write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
|
write32(buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
write32(buf + 16, 0x03e07825); // move $15, $31
|
||||||
write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
|
write32(buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||||
} else if (ELFT::Is64Bits) {
|
} else if (ELFT::Is64Bits) {
|
||||||
write32<e>(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
write32(buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||||
write32<e>(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
|
write32(buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14)
|
||||||
write32<e>(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
write32(buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||||
write32<e>(buf + 12, 0x030ec023); // subu $24, $24, $14
|
write32(buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
write32(buf + 16, 0x03e07825); // move $15, $31
|
||||||
write32<e>(buf + 20, 0x0018c0c2); // srl $24, $24, 3
|
write32(buf + 20, 0x0018c0c2); // srl $24, $24, 3
|
||||||
} else {
|
} else {
|
||||||
write32<e>(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
|
write32(buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
|
||||||
write32<e>(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
|
write32(buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
|
||||||
write32<e>(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
write32(buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
||||||
write32<e>(buf + 12, 0x031cc023); // subu $24, $24, $28
|
write32(buf + 12, 0x031cc023); // subu $24, $24, $28
|
||||||
write32<e>(buf + 16, 0x03e07825); // move $15, $31
|
write32(buf + 16, 0x03e07825); // move $15, $31
|
||||||
write32<e>(buf + 20, 0x0018c082); // srl $24, $24, 2
|
write32(buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809;
|
uint32_t jalrInst = config->zHazardplt ? 0x0320fc09 : 0x0320f809;
|
||||||
write32<e>(buf + 24, jalrInst); // jalr.hb $25 or jalr $25
|
write32(buf + 24, jalrInst); // jalr.hb $25 or jalr $25
|
||||||
write32<e>(buf + 28, 0x2718fffe); // subu $24, $24, 2
|
write32(buf + 28, 0x2718fffe); // subu $24, $24, 2
|
||||||
|
|
||||||
uint64_t gotPlt = in.gotPlt->getVA();
|
uint64_t gotPlt = in.gotPlt->getVA();
|
||||||
writeValue<e>(buf, gotPlt + 0x8000, 16, 16);
|
writeValue(buf, gotPlt + 0x8000, 16, 16);
|
||||||
writeValue<e>(buf + 4, gotPlt, 16, 0);
|
writeValue(buf + 4, gotPlt, 16, 0);
|
||||||
writeValue<e>(buf + 8, gotPlt, 16, 0);
|
writeValue(buf + 8, gotPlt, 16, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT>
|
template <class ELFT>
|
||||||
void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
||||||
uint64_t pltEntryAddr, int32_t index,
|
uint64_t pltEntryAddr, int32_t index,
|
||||||
unsigned relOff) const {
|
unsigned relOff) const {
|
||||||
const endianness e = ELFT::TargetEndianness;
|
|
||||||
if (isMicroMips()) {
|
if (isMicroMips()) {
|
||||||
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
// Overwrite trap instructions written by Writer::writeTrapInstr.
|
||||||
memset(buf, 0, pltEntrySize);
|
memset(buf, 0, pltEntrySize);
|
||||||
|
|
||||||
if (isMipsR6()) {
|
if (isMipsR6()) {
|
||||||
write16<e>(buf, 0x7840); // addiupc $2, (GOTPLT) - .
|
write16(buf, 0x7840); // addiupc $2, (GOTPLT) - .
|
||||||
write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
|
write16(buf + 4, 0xff22); // lw $25, 0($2)
|
||||||
write16<e>(buf + 8, 0x0f02); // move $24, $2
|
write16(buf + 8, 0x0f02); // move $24, $2
|
||||||
write16<e>(buf + 10, 0x4723); // jrc $25 / jr16 $25
|
write16(buf + 10, 0x4723); // jrc $25 / jr16 $25
|
||||||
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
|
relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
|
||||||
} else {
|
} else {
|
||||||
write16<e>(buf, 0x7900); // addiupc $2, (GOTPLT) - .
|
write16(buf, 0x7900); // addiupc $2, (GOTPLT) - .
|
||||||
write16<e>(buf + 4, 0xff22); // lw $25, 0($2)
|
write16(buf + 4, 0xff22); // lw $25, 0($2)
|
||||||
write16<e>(buf + 8, 0x4599); // jrc $25 / jr16 $25
|
write16(buf + 8, 0x4599); // jrc $25 / jr16 $25
|
||||||
write16<e>(buf + 10, 0x0f02); // move $24, $2
|
write16(buf + 10, 0x0f02); // move $24, $2
|
||||||
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
|
relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -332,13 +334,13 @@ void MIPS<ELFT>::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
|||||||
: (config->zHazardplt ? 0x03200408 : 0x03200008);
|
: (config->zHazardplt ? 0x03200408 : 0x03200008);
|
||||||
uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000;
|
uint32_t addInst = ELFT::Is64Bits ? 0x65f80000 : 0x25f80000;
|
||||||
|
|
||||||
write32<e>(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
|
write32(buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
|
||||||
write32<e>(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15)
|
write32(buf + 4, loadInst); // l[wd] $25, %lo(.got.plt entry)($15)
|
||||||
write32<e>(buf + 8, jrInst); // jr $25 / jr.hb $25
|
write32(buf + 8, jrInst); // jr $25 / jr.hb $25
|
||||||
write32<e>(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry)
|
write32(buf + 12, addInst); // [d]addiu $24, $15, %lo(.got.plt entry)
|
||||||
writeValue<e>(buf, gotPltEntryAddr + 0x8000, 16, 16);
|
writeValue(buf, gotPltEntryAddr + 0x8000, 16, 16);
|
||||||
writeValue<e>(buf + 4, gotPltEntryAddr, 16, 0);
|
writeValue(buf + 4, gotPltEntryAddr, 16, 0);
|
||||||
writeValue<e>(buf + 12, gotPltEntryAddr, 16, 0);
|
writeValue(buf + 12, gotPltEntryAddr, 16, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT>
|
template <class ELFT>
|
||||||
@ -372,16 +374,16 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
|
|||||||
case R_MIPS_GPREL32:
|
case R_MIPS_GPREL32:
|
||||||
case R_MIPS_TLS_DTPREL32:
|
case R_MIPS_TLS_DTPREL32:
|
||||||
case R_MIPS_TLS_TPREL32:
|
case R_MIPS_TLS_TPREL32:
|
||||||
return SignExtend64<32>(read32<e>(buf));
|
return SignExtend64<32>(read32(buf));
|
||||||
case R_MIPS_26:
|
case R_MIPS_26:
|
||||||
// FIXME (simon): If the relocation target symbol is not a PLT entry
|
// FIXME (simon): If the relocation target symbol is not a PLT entry
|
||||||
// we should use another expression for calculation:
|
// we should use another expression for calculation:
|
||||||
// ((A << 2) | (P & 0xf0000000)) >> 2
|
// ((A << 2) | (P & 0xf0000000)) >> 2
|
||||||
return SignExtend64<28>(read32<e>(buf) << 2);
|
return SignExtend64<28>(read32(buf) << 2);
|
||||||
case R_MIPS_GOT16:
|
case R_MIPS_GOT16:
|
||||||
case R_MIPS_HI16:
|
case R_MIPS_HI16:
|
||||||
case R_MIPS_PCHI16:
|
case R_MIPS_PCHI16:
|
||||||
return SignExtend64<16>(read32<e>(buf)) << 16;
|
return SignExtend64<16>(read32(buf)) << 16;
|
||||||
case R_MIPS_GPREL16:
|
case R_MIPS_GPREL16:
|
||||||
case R_MIPS_LO16:
|
case R_MIPS_LO16:
|
||||||
case R_MIPS_PCLO16:
|
case R_MIPS_PCLO16:
|
||||||
@ -389,7 +391,7 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
|
|||||||
case R_MIPS_TLS_DTPREL_LO16:
|
case R_MIPS_TLS_DTPREL_LO16:
|
||||||
case R_MIPS_TLS_TPREL_HI16:
|
case R_MIPS_TLS_TPREL_HI16:
|
||||||
case R_MIPS_TLS_TPREL_LO16:
|
case R_MIPS_TLS_TPREL_LO16:
|
||||||
return SignExtend64<16>(read32<e>(buf));
|
return SignExtend64<16>(read32(buf));
|
||||||
case R_MICROMIPS_GOT16:
|
case R_MICROMIPS_GOT16:
|
||||||
case R_MICROMIPS_HI16:
|
case R_MICROMIPS_HI16:
|
||||||
return SignExtend64<16>(readShuffle<e>(buf)) << 16;
|
return SignExtend64<16>(readShuffle<e>(buf)) << 16;
|
||||||
@ -403,21 +405,21 @@ int64_t MIPS<ELFT>::getImplicitAddend(const uint8_t *buf, RelType type) const {
|
|||||||
case R_MICROMIPS_GPREL7_S2:
|
case R_MICROMIPS_GPREL7_S2:
|
||||||
return SignExtend64<9>(readShuffle<e>(buf) << 2);
|
return SignExtend64<9>(readShuffle<e>(buf) << 2);
|
||||||
case R_MIPS_PC16:
|
case R_MIPS_PC16:
|
||||||
return SignExtend64<18>(read32<e>(buf) << 2);
|
return SignExtend64<18>(read32(buf) << 2);
|
||||||
case R_MIPS_PC19_S2:
|
case R_MIPS_PC19_S2:
|
||||||
return SignExtend64<21>(read32<e>(buf) << 2);
|
return SignExtend64<21>(read32(buf) << 2);
|
||||||
case R_MIPS_PC21_S2:
|
case R_MIPS_PC21_S2:
|
||||||
return SignExtend64<23>(read32<e>(buf) << 2);
|
return SignExtend64<23>(read32(buf) << 2);
|
||||||
case R_MIPS_PC26_S2:
|
case R_MIPS_PC26_S2:
|
||||||
return SignExtend64<28>(read32<e>(buf) << 2);
|
return SignExtend64<28>(read32(buf) << 2);
|
||||||
case R_MIPS_PC32:
|
case R_MIPS_PC32:
|
||||||
return SignExtend64<32>(read32<e>(buf));
|
return SignExtend64<32>(read32(buf));
|
||||||
case R_MICROMIPS_26_S1:
|
case R_MICROMIPS_26_S1:
|
||||||
return SignExtend64<27>(readShuffle<e>(buf) << 1);
|
return SignExtend64<27>(readShuffle<e>(buf) << 1);
|
||||||
case R_MICROMIPS_PC7_S1:
|
case R_MICROMIPS_PC7_S1:
|
||||||
return SignExtend64<8>(read16<e>(buf) << 1);
|
return SignExtend64<8>(read16(buf) << 1);
|
||||||
case R_MICROMIPS_PC10_S1:
|
case R_MICROMIPS_PC10_S1:
|
||||||
return SignExtend64<11>(read16<e>(buf) << 1);
|
return SignExtend64<11>(read16(buf) << 1);
|
||||||
case R_MICROMIPS_PC16_S1:
|
case R_MICROMIPS_PC16_S1:
|
||||||
return SignExtend64<17>(readShuffle<e>(buf) << 1);
|
return SignExtend64<17>(readShuffle<e>(buf) << 1);
|
||||||
case R_MICROMIPS_PC18_S3:
|
case R_MICROMIPS_PC18_S3:
|
||||||
@ -487,9 +489,9 @@ static uint64_t fixupCrossModeJump(uint8_t *loc, RelType type, uint64_t val) {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case R_MIPS_26: {
|
case R_MIPS_26: {
|
||||||
uint32_t inst = read32<e>(loc) >> 26;
|
uint32_t inst = read32(loc) >> 26;
|
||||||
if (inst == 0x3 || inst == 0x1d) { // JAL or JALX
|
if (inst == 0x3 || inst == 0x1d) { // JAL or JALX
|
||||||
writeValue<e>(loc, 0x1d << 26, 32, 0);
|
writeValue(loc, 0x1d << 26, 32, 0);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -538,11 +540,6 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
type == R_MICROMIPS_TLS_DTPREL_HI16 ||
|
type == R_MICROMIPS_TLS_DTPREL_HI16 ||
|
||||||
type == R_MICROMIPS_TLS_DTPREL_LO16) {
|
type == R_MICROMIPS_TLS_DTPREL_LO16) {
|
||||||
val -= 0x8000;
|
val -= 0x8000;
|
||||||
} else if (type == R_MIPS_TLS_TPREL_HI16 || type == R_MIPS_TLS_TPREL_LO16 ||
|
|
||||||
type == R_MIPS_TLS_TPREL32 || type == R_MIPS_TLS_TPREL64 ||
|
|
||||||
type == R_MICROMIPS_TLS_TPREL_HI16 ||
|
|
||||||
type == R_MICROMIPS_TLS_TPREL_LO16) {
|
|
||||||
val -= 0x7000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -550,25 +547,25 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
case R_MIPS_GPREL32:
|
case R_MIPS_GPREL32:
|
||||||
case R_MIPS_TLS_DTPREL32:
|
case R_MIPS_TLS_DTPREL32:
|
||||||
case R_MIPS_TLS_TPREL32:
|
case R_MIPS_TLS_TPREL32:
|
||||||
write32<e>(loc, val);
|
write32(loc, val);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_64:
|
case R_MIPS_64:
|
||||||
case R_MIPS_TLS_DTPREL64:
|
case R_MIPS_TLS_DTPREL64:
|
||||||
case R_MIPS_TLS_TPREL64:
|
case R_MIPS_TLS_TPREL64:
|
||||||
write64<e>(loc, val);
|
write64(loc, val);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_26:
|
case R_MIPS_26:
|
||||||
writeValue<e>(loc, val, 26, 2);
|
writeValue(loc, val, 26, 2);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_GOT16:
|
case R_MIPS_GOT16:
|
||||||
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
|
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
|
||||||
// is updated addend (not a GOT index). In that case write high 16 bits
|
// is updated addend (not a GOT index). In that case write high 16 bits
|
||||||
// to store a correct addend value.
|
// to store a correct addend value.
|
||||||
if (config->relocatable) {
|
if (config->relocatable) {
|
||||||
writeValue<e>(loc, val + 0x8000, 16, 16);
|
writeValue(loc, val + 0x8000, 16, 16);
|
||||||
} else {
|
} else {
|
||||||
checkInt(loc, val, 16, type);
|
checkInt(loc, val, 16, type);
|
||||||
writeValue<e>(loc, val, 16, 0);
|
writeValue(loc, val, 16, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case R_MICROMIPS_GOT16:
|
case R_MICROMIPS_GOT16:
|
||||||
@ -595,7 +592,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
case R_MIPS_PCLO16:
|
case R_MIPS_PCLO16:
|
||||||
case R_MIPS_TLS_DTPREL_LO16:
|
case R_MIPS_TLS_DTPREL_LO16:
|
||||||
case R_MIPS_TLS_TPREL_LO16:
|
case R_MIPS_TLS_TPREL_LO16:
|
||||||
writeValue<e>(loc, val, 16, 0);
|
writeValue(loc, val, 16, 0);
|
||||||
break;
|
break;
|
||||||
case R_MICROMIPS_GPREL16:
|
case R_MICROMIPS_GPREL16:
|
||||||
case R_MICROMIPS_TLS_GD:
|
case R_MICROMIPS_TLS_GD:
|
||||||
@ -621,7 +618,7 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
case R_MIPS_PCHI16:
|
case R_MIPS_PCHI16:
|
||||||
case R_MIPS_TLS_DTPREL_HI16:
|
case R_MIPS_TLS_DTPREL_HI16:
|
||||||
case R_MIPS_TLS_TPREL_HI16:
|
case R_MIPS_TLS_TPREL_HI16:
|
||||||
writeValue<e>(loc, val + 0x8000, 16, 16);
|
writeValue(loc, val + 0x8000, 16, 16);
|
||||||
break;
|
break;
|
||||||
case R_MICROMIPS_CALL_HI16:
|
case R_MICROMIPS_CALL_HI16:
|
||||||
case R_MICROMIPS_GOT_HI16:
|
case R_MICROMIPS_GOT_HI16:
|
||||||
@ -631,37 +628,51 @@ void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
|
writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_HIGHER:
|
case R_MIPS_HIGHER:
|
||||||
writeValue<e>(loc, val + 0x80008000, 16, 32);
|
writeValue(loc, val + 0x80008000, 16, 32);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_HIGHEST:
|
case R_MIPS_HIGHEST:
|
||||||
writeValue<e>(loc, val + 0x800080008000, 16, 48);
|
writeValue(loc, val + 0x800080008000, 16, 48);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_JALR:
|
case R_MIPS_JALR:
|
||||||
|
val -= 4;
|
||||||
|
// Replace jalr/jr instructions by bal/b if the target
|
||||||
|
// offset fits into the 18-bit range.
|
||||||
|
if (isInt<18>(val)) {
|
||||||
|
switch (read32(loc)) {
|
||||||
|
case 0x0320f809: // jalr $25 => bal sym
|
||||||
|
write32(loc, 0x04110000 | ((val >> 2) & 0xffff));
|
||||||
|
break;
|
||||||
|
case 0x03200008: // jr $25 => b sym
|
||||||
|
write32(loc, 0x10000000 | ((val >> 2) & 0xffff));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case R_MICROMIPS_JALR:
|
case R_MICROMIPS_JALR:
|
||||||
// Ignore this optimization relocation for now
|
// Ignore this optimization relocation for now
|
||||||
break;
|
break;
|
||||||
case R_MIPS_PC16:
|
case R_MIPS_PC16:
|
||||||
checkAlignment(loc, val, 4, type);
|
checkAlignment(loc, val, 4, type);
|
||||||
checkInt(loc, val, 18, type);
|
checkInt(loc, val, 18, type);
|
||||||
writeValue<e>(loc, val, 16, 2);
|
writeValue(loc, val, 16, 2);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_PC19_S2:
|
case R_MIPS_PC19_S2:
|
||||||
checkAlignment(loc, val, 4, type);
|
checkAlignment(loc, val, 4, type);
|
||||||
checkInt(loc, val, 21, type);
|
checkInt(loc, val, 21, type);
|
||||||
writeValue<e>(loc, val, 19, 2);
|
writeValue(loc, val, 19, 2);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_PC21_S2:
|
case R_MIPS_PC21_S2:
|
||||||
checkAlignment(loc, val, 4, type);
|
checkAlignment(loc, val, 4, type);
|
||||||
checkInt(loc, val, 23, type);
|
checkInt(loc, val, 23, type);
|
||||||
writeValue<e>(loc, val, 21, 2);
|
writeValue(loc, val, 21, 2);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_PC26_S2:
|
case R_MIPS_PC26_S2:
|
||||||
checkAlignment(loc, val, 4, type);
|
checkAlignment(loc, val, 4, type);
|
||||||
checkInt(loc, val, 28, type);
|
checkInt(loc, val, 28, type);
|
||||||
writeValue<e>(loc, val, 26, 2);
|
writeValue(loc, val, 26, 2);
|
||||||
break;
|
break;
|
||||||
case R_MIPS_PC32:
|
case R_MIPS_PC32:
|
||||||
writeValue<e>(loc, val, 32, 0);
|
writeValue(loc, val, 32, 0);
|
||||||
break;
|
break;
|
||||||
case R_MICROMIPS_26_S1:
|
case R_MICROMIPS_26_S1:
|
||||||
case R_MICROMIPS_PC26_S1:
|
case R_MICROMIPS_PC26_S1:
|
||||||
@ -707,7 +718,7 @@ template <class ELFT> bool MIPS<ELFT>::usesOnlyLowPageBits(RelType type) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return true if the symbol is a PIC function.
|
// Return true if the symbol is a PIC function.
|
||||||
template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
|
template <class ELFT> bool isMipsPIC(const Defined *sym) {
|
||||||
if (!sym->isFunc())
|
if (!sym->isFunc())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -725,17 +736,20 @@ template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
|
|||||||
return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
|
return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
|
template <class ELFT> TargetInfo *getMipsTargetInfo() {
|
||||||
static MIPS<ELFT> target;
|
static MIPS<ELFT> target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
|
template TargetInfo *getMipsTargetInfo<ELF32LE>();
|
||||||
template TargetInfo *elf::getMipsTargetInfo<ELF32BE>();
|
template TargetInfo *getMipsTargetInfo<ELF32BE>();
|
||||||
template TargetInfo *elf::getMipsTargetInfo<ELF64LE>();
|
template TargetInfo *getMipsTargetInfo<ELF64LE>();
|
||||||
template TargetInfo *elf::getMipsTargetInfo<ELF64BE>();
|
template TargetInfo *getMipsTargetInfo<ELF64BE>();
|
||||||
|
|
||||||
template bool elf::isMipsPIC<ELF32LE>(const Defined *);
|
template bool isMipsPIC<ELF32LE>(const Defined *);
|
||||||
template bool elf::isMipsPIC<ELF32BE>(const Defined *);
|
template bool isMipsPIC<ELF32BE>(const Defined *);
|
||||||
template bool elf::isMipsPIC<ELF64LE>(const Defined *);
|
template bool isMipsPIC<ELF64LE>(const Defined *);
|
||||||
template bool elf::isMipsPIC<ELF64BE>(const Defined *);
|
template bool isMipsPIC<ELF64BE>(const Defined *);
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -23,8 +23,8 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct ArchTreeEdge {
|
struct ArchTreeEdge {
|
||||||
@ -166,17 +166,17 @@ static ArchTreeEdge archTree[] = {
|
|||||||
{EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
|
{EF_MIPS_ARCH_2, EF_MIPS_ARCH_1},
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool isArchMatched(uint32_t New, uint32_t res) {
|
static bool isArchMatched(uint32_t newFlags, uint32_t res) {
|
||||||
if (New == res)
|
if (newFlags == res)
|
||||||
return true;
|
return true;
|
||||||
if (New == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res))
|
if (newFlags == EF_MIPS_ARCH_32 && isArchMatched(EF_MIPS_ARCH_64, res))
|
||||||
return true;
|
return true;
|
||||||
if (New == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res))
|
if (newFlags == EF_MIPS_ARCH_32R2 && isArchMatched(EF_MIPS_ARCH_64R2, res))
|
||||||
return true;
|
return true;
|
||||||
for (const auto &edge : archTree) {
|
for (const auto &edge : archTree) {
|
||||||
if (res == edge.child) {
|
if (res == edge.child) {
|
||||||
res = edge.parent;
|
res = edge.parent;
|
||||||
if (res == New)
|
if (res == newFlags)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -278,46 +278,34 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> files) {
|
|||||||
uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
|
uint32_t ret = files[0].flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
|
||||||
|
|
||||||
for (const FileFlags &f : files.slice(1)) {
|
for (const FileFlags &f : files.slice(1)) {
|
||||||
uint32_t New = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
|
uint32_t newFlags = f.flags & (EF_MIPS_ARCH | EF_MIPS_MACH);
|
||||||
|
|
||||||
// Check ISA compatibility.
|
// Check ISA compatibility.
|
||||||
if (isArchMatched(New, ret))
|
if (isArchMatched(newFlags, ret))
|
||||||
continue;
|
continue;
|
||||||
if (!isArchMatched(ret, New)) {
|
if (!isArchMatched(ret, newFlags)) {
|
||||||
error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
|
error("incompatible target ISA:\n>>> " + toString(files[0].file) + ": " +
|
||||||
getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
|
getFullArchName(ret) + "\n>>> " + toString(f.file) + ": " +
|
||||||
getFullArchName(New));
|
getFullArchName(newFlags));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ret = New;
|
ret = newFlags;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't have any input files, we'll have to rely on the information we
|
template <class ELFT> uint32_t calcMipsEFlags() {
|
||||||
// can derive from emulation information, since this at least gets us ABI.
|
|
||||||
static uint32_t getFlagsFromEmulation() {
|
|
||||||
uint32_t ret = 0;
|
|
||||||
|
|
||||||
if (config->emulation.empty())
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (config->ekind == ELF32BEKind || config->ekind == ELF32LEKind) {
|
|
||||||
if (config->mipsN32Abi)
|
|
||||||
ret |= EF_MIPS_ABI2;
|
|
||||||
else
|
|
||||||
ret |= EF_MIPS_ABI_O32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class ELFT> uint32_t elf::calcMipsEFlags() {
|
|
||||||
std::vector<FileFlags> v;
|
std::vector<FileFlags> v;
|
||||||
for (InputFile *f : objectFiles)
|
for (InputFile *f : objectFiles)
|
||||||
v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
|
v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
|
||||||
if (v.empty())
|
if (v.empty()) {
|
||||||
return getFlagsFromEmulation();
|
// If we don't have any input files, we'll have to rely on the information
|
||||||
|
// we can derive from emulation information, since this at least gets us
|
||||||
|
// ABI.
|
||||||
|
if (config->emulation.empty() || config->is64)
|
||||||
|
return 0;
|
||||||
|
return config->mipsN32Abi ? EF_MIPS_ABI2 : EF_MIPS_ABI_O32;
|
||||||
|
}
|
||||||
checkFlags(v);
|
checkFlags(v);
|
||||||
return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v);
|
return getMiscFlags(v) | getPicFlags(v) | getArchFlags(v);
|
||||||
}
|
}
|
||||||
@ -362,8 +350,7 @@ static StringRef getMipsFpAbiName(uint8_t fpAbi) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
|
uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, StringRef fileName) {
|
||||||
StringRef fileName) {
|
|
||||||
if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
|
if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
|
||||||
return newFlag;
|
return newFlag;
|
||||||
if (compareMipsFpAbi(oldFlag, newFlag) < 0)
|
if (compareMipsFpAbi(oldFlag, newFlag) < 0)
|
||||||
@ -379,7 +366,7 @@ template <class ELFT> static bool isN32Abi(const InputFile *f) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool elf::isMipsN32Abi(const InputFile *f) {
|
bool isMipsN32Abi(const InputFile *f) {
|
||||||
switch (config->ekind) {
|
switch (config->ekind) {
|
||||||
case ELF32LEKind:
|
case ELF32LEKind:
|
||||||
return isN32Abi<ELF32LE>(f);
|
return isN32Abi<ELF32LE>(f);
|
||||||
@ -394,14 +381,17 @@ bool elf::isMipsN32Abi(const InputFile *f) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
|
bool isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
|
||||||
|
|
||||||
bool elf::isMipsR6() {
|
bool isMipsR6() {
|
||||||
uint32_t arch = config->eflags & EF_MIPS_ARCH;
|
uint32_t arch = config->eflags & EF_MIPS_ARCH;
|
||||||
return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
|
return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
|
||||||
}
|
}
|
||||||
|
|
||||||
template uint32_t elf::calcMipsEFlags<ELF32LE>();
|
template uint32_t calcMipsEFlags<ELF32LE>();
|
||||||
template uint32_t elf::calcMipsEFlags<ELF32BE>();
|
template uint32_t calcMipsEFlags<ELF32BE>();
|
||||||
template uint32_t elf::calcMipsEFlags<ELF64LE>();
|
template uint32_t calcMipsEFlags<ELF64LE>();
|
||||||
template uint32_t elf::calcMipsEFlags<ELF64BE>();
|
template uint32_t calcMipsEFlags<ELF64BE>();
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class PPC final : public TargetInfo {
|
class PPC final : public TargetInfo {
|
||||||
@ -61,7 +62,7 @@ static void writeFromHalf16(uint8_t *loc, uint32_t insn) {
|
|||||||
write32(config->isLE ? loc : loc - 2, insn);
|
write32(config->isLE ? loc : loc - 2, insn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void elf::writePPC32GlinkSection(uint8_t *buf, size_t numEntries) {
|
void writePPC32GlinkSection(uint8_t *buf, size_t numEntries) {
|
||||||
// On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an
|
// On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an
|
||||||
// absolute address from a specific .plt slot (usually called .got.plt on
|
// absolute address from a specific .plt slot (usually called .got.plt on
|
||||||
// other targets) and jumps there.
|
// other targets) and jumps there.
|
||||||
@ -435,7 +436,10 @@ void PPC::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getPPCTargetInfo() {
|
TargetInfo *getPPCTargetInfo() {
|
||||||
static PPC target;
|
static PPC target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -16,8 +16,9 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
static uint64_t ppc64TocOffset = 0x8000;
|
static uint64_t ppc64TocOffset = 0x8000;
|
||||||
static uint64_t dynamicThreadPointerOffset = 0x8000;
|
static uint64_t dynamicThreadPointerOffset = 0x8000;
|
||||||
@ -59,7 +60,7 @@ enum DFormOpcd {
|
|||||||
ADDI = 14
|
ADDI = 14
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t elf::getPPC64TocBase() {
|
uint64_t getPPC64TocBase() {
|
||||||
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
|
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
|
||||||
// TOC starts where the first of these sections starts. We always create a
|
// TOC starts where the first of these sections starts. We always create a
|
||||||
// .got when we see a relocation that uses it, so for us the start is always
|
// .got when we see a relocation that uses it, so for us the start is always
|
||||||
@ -73,7 +74,7 @@ uint64_t elf::getPPC64TocBase() {
|
|||||||
return tocVA + ppc64TocOffset;
|
return tocVA + ppc64TocOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
|
unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
|
||||||
// The offset is encoded into the 3 most significant bits of the st_other
|
// The offset is encoded into the 3 most significant bits of the st_other
|
||||||
// field, with some special values described in section 3.4.1 of the ABI:
|
// field, with some special values described in section 3.4.1 of the ABI:
|
||||||
// 0 --> Zero offset between the GEP and LEP, and the function does NOT use
|
// 0 --> Zero offset between the GEP and LEP, and the function does NOT use
|
||||||
@ -98,7 +99,7 @@ unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool elf::isPPC64SmallCodeModelTocReloc(RelType type) {
|
bool isPPC64SmallCodeModelTocReloc(RelType type) {
|
||||||
// The only small code model relocations that access the .toc section.
|
// The only small code model relocations that access the .toc section.
|
||||||
return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
|
return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
|
||||||
}
|
}
|
||||||
@ -153,8 +154,8 @@ getRelaTocSymAndAddend(InputSectionBase *tocSec, uint64_t offset) {
|
|||||||
// ld/lwa 3, 0(3) # load the value from the address
|
// ld/lwa 3, 0(3) # load the value from the address
|
||||||
//
|
//
|
||||||
// Returns true if the relaxation is performed.
|
// Returns true if the relaxation is performed.
|
||||||
bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
|
bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
|
||||||
uint8_t *bufLoc) {
|
uint8_t *bufLoc) {
|
||||||
assert(config->tocOptimize);
|
assert(config->tocOptimize);
|
||||||
if (rel.addend < 0)
|
if (rel.addend < 0)
|
||||||
return false;
|
return false;
|
||||||
@ -175,6 +176,10 @@ bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
|
|||||||
if (!d || d->isPreemptible)
|
if (!d || d->isPreemptible)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// R_PPC64_ADDR64 should have created a canonical PLT for the non-preemptable
|
||||||
|
// ifunc and changed its type to STT_FUNC.
|
||||||
|
assert(!d->isGnuIFunc());
|
||||||
|
|
||||||
// Two instructions can materialize a 32-bit signed offset from the toc base.
|
// Two instructions can materialize a 32-bit signed offset from the toc base.
|
||||||
uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase();
|
uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase();
|
||||||
if (!isInt<32>(tocRelative))
|
if (!isInt<32>(tocRelative))
|
||||||
@ -454,7 +459,7 @@ void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned elf::getPPCDFormOp(unsigned secondaryOp) {
|
unsigned getPPCDFormOp(unsigned secondaryOp) {
|
||||||
switch (secondaryOp) {
|
switch (secondaryOp) {
|
||||||
case LBZX:
|
case LBZX:
|
||||||
return LBZ;
|
return LBZ;
|
||||||
@ -1089,7 +1094,10 @@ bool PPC64::adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getPPC64TargetInfo() {
|
TargetInfo *getPPC64TargetInfo() {
|
||||||
static PPC64 target;
|
static PPC64 target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -14,8 +14,9 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -439,7 +440,10 @@ void RISCV::relocateOne(uint8_t *loc, const RelType type,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getRISCVTargetInfo() {
|
TargetInfo *getRISCVTargetInfo() {
|
||||||
static RISCV target;
|
static RISCV target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class SPARCV9 final : public TargetInfo {
|
class SPARCV9 final : public TargetInfo {
|
||||||
@ -143,7 +144,10 @@ void SPARCV9::writePlt(uint8_t *buf, uint64_t gotEntryAddr,
|
|||||||
relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
|
relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getSPARCV9TargetInfo() {
|
TargetInfo *getSPARCV9TargetInfo() {
|
||||||
static SPARCV9 target;
|
static SPARCV9 target;
|
||||||
return ⌖
|
return ⌖
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -16,8 +16,9 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class X86 : public TargetInfo {
|
class X86 : public TargetInfo {
|
||||||
@ -539,7 +540,7 @@ void RetpolineNoPic::writePlt(uint8_t *buf, uint64_t gotPltEntryAddr,
|
|||||||
write32le(buf + 22, -off - 26);
|
write32le(buf + 22, -off - 26);
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getX86TargetInfo() {
|
TargetInfo *getX86TargetInfo() {
|
||||||
if (config->zRetpolineplt) {
|
if (config->zRetpolineplt) {
|
||||||
if (config->isPic) {
|
if (config->isPic) {
|
||||||
static RetpolinePic t;
|
static RetpolinePic t;
|
||||||
@ -552,3 +553,6 @@ TargetInfo *elf::getX86TargetInfo() {
|
|||||||
static X86 t;
|
static X86 t;
|
||||||
return &t;
|
return &t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -18,8 +18,9 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class X86_64 : public TargetInfo {
|
class X86_64 : public TargetInfo {
|
||||||
@ -698,4 +699,7 @@ static TargetInfo *getTargetInfo() {
|
|||||||
return &t;
|
return &t;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); }
|
TargetInfo *getX86_64TargetInfo() { return getTargetInfo(); }
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -22,6 +22,7 @@ add_lld_library(lldELF
|
|||||||
Arch/SPARCV9.cpp
|
Arch/SPARCV9.cpp
|
||||||
Arch/X86.cpp
|
Arch/X86.cpp
|
||||||
Arch/X86_64.cpp
|
Arch/X86_64.cpp
|
||||||
|
ARMErrataFix.cpp
|
||||||
CallGraphSort.cpp
|
CallGraphSort.cpp
|
||||||
DWARF.cpp
|
DWARF.cpp
|
||||||
Driver.cpp
|
Driver.cpp
|
||||||
|
@ -45,9 +45,12 @@
|
|||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
|
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct Edge {
|
struct Edge {
|
||||||
@ -56,7 +59,7 @@ struct Edge {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Cluster {
|
struct Cluster {
|
||||||
Cluster(int sec, size_t s) : sections{sec}, size(s) {}
|
Cluster(int sec, size_t s) : next(sec), prev(sec), size(s) {}
|
||||||
|
|
||||||
double getDensity() const {
|
double getDensity() const {
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
@ -64,7 +67,8 @@ struct Cluster {
|
|||||||
return double(weight) / double(size);
|
return double(weight) / double(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> sections;
|
int next;
|
||||||
|
int prev;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
uint64_t weight = 0;
|
uint64_t weight = 0;
|
||||||
uint64_t initialWeight = 0;
|
uint64_t initialWeight = 0;
|
||||||
@ -80,8 +84,6 @@ class CallGraphSort {
|
|||||||
private:
|
private:
|
||||||
std::vector<Cluster> clusters;
|
std::vector<Cluster> clusters;
|
||||||
std::vector<const InputSectionBase *> sections;
|
std::vector<const InputSectionBase *> sections;
|
||||||
|
|
||||||
void groupClusters();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Maximum ammount the combined cluster density can be worse than the original
|
// Maximum ammount the combined cluster density can be worse than the original
|
||||||
@ -103,7 +105,7 @@ CallGraphSort::CallGraphSort() {
|
|||||||
DenseMap<const InputSectionBase *, int> secToCluster;
|
DenseMap<const InputSectionBase *, int> secToCluster;
|
||||||
|
|
||||||
auto getOrCreateNode = [&](const InputSectionBase *isec) -> int {
|
auto getOrCreateNode = [&](const InputSectionBase *isec) -> int {
|
||||||
auto res = secToCluster.insert(std::make_pair(isec, clusters.size()));
|
auto res = secToCluster.try_emplace(isec, clusters.size());
|
||||||
if (res.second) {
|
if (res.second) {
|
||||||
sections.push_back(isec);
|
sections.push_back(isec);
|
||||||
clusters.emplace_back(clusters.size(), isec->getSize());
|
clusters.emplace_back(clusters.size(), isec->getSize());
|
||||||
@ -151,83 +153,88 @@ static bool isNewDensityBad(Cluster &a, Cluster &b) {
|
|||||||
return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
|
return newDensity < a.getDensity() / MAX_DENSITY_DEGRADATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mergeClusters(Cluster &into, Cluster &from) {
|
// Find the leader of V's belonged cluster (represented as an equivalence
|
||||||
into.sections.insert(into.sections.end(), from.sections.begin(),
|
// class). We apply union-find path-halving technique (simple to implement) in
|
||||||
from.sections.end());
|
// the meantime as it decreases depths and the time complexity.
|
||||||
|
static int getLeader(std::vector<int> &leaders, int v) {
|
||||||
|
while (leaders[v] != v) {
|
||||||
|
leaders[v] = leaders[leaders[v]];
|
||||||
|
v = leaders[v];
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mergeClusters(std::vector<Cluster> &cs, Cluster &into, int intoIdx,
|
||||||
|
Cluster &from, int fromIdx) {
|
||||||
|
int tail1 = into.prev, tail2 = from.prev;
|
||||||
|
into.prev = tail2;
|
||||||
|
cs[tail2].next = intoIdx;
|
||||||
|
from.prev = tail1;
|
||||||
|
cs[tail1].next = fromIdx;
|
||||||
into.size += from.size;
|
into.size += from.size;
|
||||||
into.weight += from.weight;
|
into.weight += from.weight;
|
||||||
from.sections.clear();
|
|
||||||
from.size = 0;
|
from.size = 0;
|
||||||
from.weight = 0;
|
from.weight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group InputSections into clusters using the Call-Chain Clustering heuristic
|
// Group InputSections into clusters using the Call-Chain Clustering heuristic
|
||||||
// then sort the clusters by density.
|
// then sort the clusters by density.
|
||||||
void CallGraphSort::groupClusters() {
|
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
|
||||||
std::vector<int> sortedSecs(clusters.size());
|
std::vector<int> sorted(clusters.size());
|
||||||
std::vector<Cluster *> secToCluster(clusters.size());
|
std::vector<int> leaders(clusters.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < clusters.size(); ++i) {
|
std::iota(leaders.begin(), leaders.end(), 0);
|
||||||
sortedSecs[i] = i;
|
std::iota(sorted.begin(), sorted.end(), 0);
|
||||||
secToCluster[i] = &clusters[i];
|
llvm::stable_sort(sorted, [&](int a, int b) {
|
||||||
}
|
|
||||||
|
|
||||||
llvm::stable_sort(sortedSecs, [&](int a, int b) {
|
|
||||||
return clusters[a].getDensity() > clusters[b].getDensity();
|
return clusters[a].getDensity() > clusters[b].getDensity();
|
||||||
});
|
});
|
||||||
|
|
||||||
for (int si : sortedSecs) {
|
for (int l : sorted) {
|
||||||
// clusters[si] is the same as secToClusters[si] here because it has not
|
// The cluster index is the same as the index of its leader here because
|
||||||
// been merged into another cluster yet.
|
// clusters[L] has not been merged into another cluster yet.
|
||||||
Cluster &c = clusters[si];
|
Cluster &c = clusters[l];
|
||||||
|
|
||||||
// Don't consider merging if the edge is unlikely.
|
// Don't consider merging if the edge is unlikely.
|
||||||
if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
|
if (c.bestPred.from == -1 || c.bestPred.weight * 10 <= c.initialWeight)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Cluster *predC = secToCluster[c.bestPred.from];
|
int predL = getLeader(leaders, c.bestPred.from);
|
||||||
if (predC == &c)
|
if (l == predL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
Cluster *predC = &clusters[predL];
|
||||||
if (c.size + predC->size > MAX_CLUSTER_SIZE)
|
if (c.size + predC->size > MAX_CLUSTER_SIZE)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (isNewDensityBad(*predC, c))
|
if (isNewDensityBad(*predC, c))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// NOTE: Consider using a disjoint-set to track section -> cluster mapping
|
leaders[l] = predL;
|
||||||
// if this is ever slow.
|
mergeClusters(clusters, *predC, predL, c, l);
|
||||||
for (int si : c.sections)
|
|
||||||
secToCluster[si] = predC;
|
|
||||||
|
|
||||||
mergeClusters(*predC, c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove empty or dead nodes. Invalidates all cluster indices.
|
// Sort remaining non-empty clusters by density.
|
||||||
llvm::erase_if(clusters, [](const Cluster &c) {
|
sorted.clear();
|
||||||
return c.size == 0 || c.sections.empty();
|
for (int i = 0, e = (int)clusters.size(); i != e; ++i)
|
||||||
|
if (clusters[i].size > 0)
|
||||||
|
sorted.push_back(i);
|
||||||
|
llvm::stable_sort(sorted, [&](int a, int b) {
|
||||||
|
return clusters[a].getDensity() > clusters[b].getDensity();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort by density.
|
|
||||||
llvm::stable_sort(clusters, [](const Cluster &a, const Cluster &b) {
|
|
||||||
return a.getDensity() > b.getDensity();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
|
|
||||||
groupClusters();
|
|
||||||
|
|
||||||
// Generate order.
|
|
||||||
DenseMap<const InputSectionBase *, int> orderMap;
|
DenseMap<const InputSectionBase *, int> orderMap;
|
||||||
ssize_t curOrder = 1;
|
int curOrder = 1;
|
||||||
|
for (int leader : sorted)
|
||||||
for (const Cluster &c : clusters)
|
for (int i = leader;;) {
|
||||||
for (int secIndex : c.sections)
|
orderMap[sections[i]] = curOrder++;
|
||||||
orderMap[sections[secIndex]] = curOrder++;
|
i = clusters[i].next;
|
||||||
|
if (i == leader)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!config->printSymbolOrder.empty()) {
|
if (!config->printSymbolOrder.empty()) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::F_None);
|
raw_fd_ostream os(config->printSymbolOrder, ec, sys::fs::OF_None);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
error("cannot open " + config->printSymbolOrder + ": " + ec.message());
|
error("cannot open " + config->printSymbolOrder + ": " + ec.message());
|
||||||
return orderMap;
|
return orderMap;
|
||||||
@ -235,15 +242,19 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
|
|||||||
|
|
||||||
// Print the symbols ordered by C3, in the order of increasing curOrder
|
// Print the symbols ordered by C3, in the order of increasing curOrder
|
||||||
// Instead of sorting all the orderMap, just repeat the loops above.
|
// Instead of sorting all the orderMap, just repeat the loops above.
|
||||||
for (const Cluster &c : clusters)
|
for (int leader : sorted)
|
||||||
for (int secIndex : c.sections)
|
for (int i = leader;;) {
|
||||||
// Search all the symbols in the file of the section
|
// Search all the symbols in the file of the section
|
||||||
// and find out a Defined symbol with name that is within the section.
|
// and find out a Defined symbol with name that is within the section.
|
||||||
for (Symbol *sym: sections[secIndex]->file->getSymbols())
|
for (Symbol *sym : sections[i]->file->getSymbols())
|
||||||
if (!sym->isSection()) // Filter out section-type symbols here.
|
if (!sym->isSection()) // Filter out section-type symbols here.
|
||||||
if (auto *d = dyn_cast<Defined>(sym))
|
if (auto *d = dyn_cast<Defined>(sym))
|
||||||
if (sections[secIndex] == d->section)
|
if (sections[i] == d->section)
|
||||||
os << sym->getName() << "\n";
|
os << sym->getName() << "\n";
|
||||||
|
i = clusters[i].next;
|
||||||
|
if (i == leader)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return orderMap;
|
return orderMap;
|
||||||
@ -254,6 +265,9 @@ DenseMap<const InputSectionBase *, int> CallGraphSort::run() {
|
|||||||
// This first builds a call graph based on the profile data then merges sections
|
// This first builds a call graph based on the profile data then merges sections
|
||||||
// according to the C³ huristic. All clusters are then sorted by a density
|
// according to the C³ huristic. All clusters are then sorted by a density
|
||||||
// metric to further improve locality.
|
// metric to further improve locality.
|
||||||
DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
|
DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder() {
|
||||||
return CallGraphSort().run();
|
return CallGraphSort().run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -61,6 +61,9 @@ enum class Target2Policy { Abs, Rel, GotRel };
|
|||||||
// For tracking ARM Float Argument PCS
|
// For tracking ARM Float Argument PCS
|
||||||
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
|
enum class ARMVFPArgKind { Default, Base, VFP, ToolChain };
|
||||||
|
|
||||||
|
// For -z noseparate-code, -z separate-code and -z separate-loadable-segments.
|
||||||
|
enum class SeparateSegmentKind { None, Code, Loadable };
|
||||||
|
|
||||||
struct SymbolVersion {
|
struct SymbolVersion {
|
||||||
llvm::StringRef name;
|
llvm::StringRef name;
|
||||||
bool isExternCpp;
|
bool isExternCpp;
|
||||||
@ -71,8 +74,8 @@ struct SymbolVersion {
|
|||||||
// can be found in version script if it is used for link.
|
// can be found in version script if it is used for link.
|
||||||
struct VersionDefinition {
|
struct VersionDefinition {
|
||||||
llvm::StringRef name;
|
llvm::StringRef name;
|
||||||
uint16_t id = 0;
|
uint16_t id;
|
||||||
std::vector<SymbolVersion> globals;
|
std::vector<SymbolVersion> patterns;
|
||||||
};
|
};
|
||||||
|
|
||||||
// This struct contains the global configuration for the linker.
|
// This struct contains the global configuration for the linker.
|
||||||
@ -117,8 +120,6 @@ struct Configuration {
|
|||||||
std::vector<llvm::StringRef> symbolOrderingFile;
|
std::vector<llvm::StringRef> symbolOrderingFile;
|
||||||
std::vector<llvm::StringRef> undefined;
|
std::vector<llvm::StringRef> undefined;
|
||||||
std::vector<SymbolVersion> dynamicList;
|
std::vector<SymbolVersion> dynamicList;
|
||||||
std::vector<SymbolVersion> versionScriptGlobals;
|
|
||||||
std::vector<SymbolVersion> versionScriptLocals;
|
|
||||||
std::vector<uint8_t> buildIdVector;
|
std::vector<uint8_t> buildIdVector;
|
||||||
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
|
llvm::MapVector<std::pair<const InputSectionBase *, const InputSectionBase *>,
|
||||||
uint64_t>
|
uint64_t>
|
||||||
@ -147,6 +148,7 @@ struct Configuration {
|
|||||||
bool executeOnly;
|
bool executeOnly;
|
||||||
bool exportDynamic;
|
bool exportDynamic;
|
||||||
bool fixCortexA53Errata843419;
|
bool fixCortexA53Errata843419;
|
||||||
|
bool fixCortexA8;
|
||||||
bool forceBTI;
|
bool forceBTI;
|
||||||
bool formatBinary = false;
|
bool formatBinary = false;
|
||||||
bool requireCET;
|
bool requireCET;
|
||||||
@ -222,8 +224,8 @@ struct Configuration {
|
|||||||
Target2Policy target2;
|
Target2Policy target2;
|
||||||
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
|
ARMVFPArgKind armVFPArgs = ARMVFPArgKind::Default;
|
||||||
BuildIdKind buildId = BuildIdKind::None;
|
BuildIdKind buildId = BuildIdKind::None;
|
||||||
|
SeparateSegmentKind zSeparate;
|
||||||
ELFKind ekind = ELFNoneKind;
|
ELFKind ekind = ELFNoneKind;
|
||||||
uint16_t defaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
|
|
||||||
uint16_t emachine = llvm::ELF::EM_NONE;
|
uint16_t emachine = llvm::ELF::EM_NONE;
|
||||||
llvm::Optional<uint64_t> imageBase;
|
llvm::Optional<uint64_t> imageBase;
|
||||||
uint64_t commonPageSize;
|
uint64_t commonPageSize;
|
||||||
@ -309,6 +311,12 @@ struct Configuration {
|
|||||||
// The only instance of Configuration struct.
|
// The only instance of Configuration struct.
|
||||||
extern Configuration *config;
|
extern Configuration *config;
|
||||||
|
|
||||||
|
// The first two elements of versionDefinitions represent VER_NDX_LOCAL and
|
||||||
|
// VER_NDX_GLOBAL. This helper returns other elements.
|
||||||
|
static inline ArrayRef<VersionDefinition> namedVersionDefs() {
|
||||||
|
return llvm::makeArrayRef(config->versionDefinitions).slice(2);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void errorOrWarn(const Twine &msg) {
|
static inline void errorOrWarn(const Twine &msg) {
|
||||||
if (!config->noinhibitExec)
|
if (!config->noinhibitExec)
|
||||||
error(msg);
|
error(msg);
|
||||||
|
@ -22,9 +22,9 @@
|
|||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
|
template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
|
||||||
for (InputSectionBase *sec : obj->getSections()) {
|
for (InputSectionBase *sec : obj->getSections()) {
|
||||||
if (!sec)
|
if (!sec)
|
||||||
@ -33,11 +33,12 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
|
|||||||
if (LLDDWARFSection *m =
|
if (LLDDWARFSection *m =
|
||||||
StringSwitch<LLDDWARFSection *>(sec->name)
|
StringSwitch<LLDDWARFSection *>(sec->name)
|
||||||
.Case(".debug_addr", &addrSection)
|
.Case(".debug_addr", &addrSection)
|
||||||
.Case(".debug_gnu_pubnames", &gnuPubNamesSection)
|
.Case(".debug_gnu_pubnames", &gnuPubnamesSection)
|
||||||
.Case(".debug_gnu_pubtypes", &gnuPubTypesSection)
|
.Case(".debug_gnu_pubtypes", &gnuPubtypesSection)
|
||||||
.Case(".debug_info", &infoSection)
|
.Case(".debug_info", &infoSection)
|
||||||
.Case(".debug_ranges", &rangeSection)
|
.Case(".debug_ranges", &rangesSection)
|
||||||
.Case(".debug_rnglists", &rngListsSection)
|
.Case(".debug_rnglists", &rnglistsSection)
|
||||||
|
.Case(".debug_str_offsets", &strOffsetsSection)
|
||||||
.Case(".debug_line", &lineSection)
|
.Case(".debug_line", &lineSection)
|
||||||
.Default(nullptr)) {
|
.Default(nullptr)) {
|
||||||
m->Data = toStringRef(sec->data());
|
m->Data = toStringRef(sec->data());
|
||||||
@ -50,7 +51,7 @@ template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
|
|||||||
else if (sec->name == ".debug_str")
|
else if (sec->name == ".debug_str")
|
||||||
strSection = toStringRef(sec->data());
|
strSection = toStringRef(sec->data());
|
||||||
else if (sec->name == ".debug_line_str")
|
else if (sec->name == ".debug_line_str")
|
||||||
lineStringSection = toStringRef(sec->data());
|
lineStrSection = toStringRef(sec->data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +124,10 @@ Optional<RelocAddrEntry> LLDDwarfObj<ELFT>::find(const llvm::DWARFSection &s,
|
|||||||
return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>());
|
return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template class elf::LLDDwarfObj<ELF32LE>;
|
template class LLDDwarfObj<ELF32LE>;
|
||||||
template class elf::LLDDwarfObj<ELF32BE>;
|
template class LLDDwarfObj<ELF32BE>;
|
||||||
template class elf::LLDDwarfObj<ELF64LE>;
|
template class LLDDwarfObj<ELF64LE>;
|
||||||
template class elf::LLDDwarfObj<ELF64BE>;
|
template class LLDDwarfObj<ELF64BE>;
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -32,12 +32,16 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
|||||||
f(infoSection);
|
f(infoSection);
|
||||||
}
|
}
|
||||||
|
|
||||||
const llvm::DWARFSection &getRangeSection() const override {
|
const llvm::DWARFSection &getRangesSection() const override {
|
||||||
return rangeSection;
|
return rangesSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
const llvm::DWARFSection &getRnglistsSection() const override {
|
const llvm::DWARFSection &getRnglistsSection() const override {
|
||||||
return rngListsSection;
|
return rnglistsSection;
|
||||||
|
}
|
||||||
|
|
||||||
|
const llvm::DWARFSection &getStrOffsetsSection() const override {
|
||||||
|
return strOffsetsSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
const llvm::DWARFSection &getLineSection() const override {
|
const llvm::DWARFSection &getLineSection() const override {
|
||||||
@ -48,18 +52,18 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
|||||||
return addrSection;
|
return addrSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
const llvm::DWARFSection &getGnuPubNamesSection() const override {
|
const llvm::DWARFSection &getGnuPubnamesSection() const override {
|
||||||
return gnuPubNamesSection;
|
return gnuPubnamesSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
const llvm::DWARFSection &getGnuPubTypesSection() const override {
|
const llvm::DWARFSection &getGnuPubtypesSection() const override {
|
||||||
return gnuPubTypesSection;
|
return gnuPubtypesSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringRef getFileName() const override { return ""; }
|
StringRef getFileName() const override { return ""; }
|
||||||
StringRef getAbbrevSection() const override { return abbrevSection; }
|
StringRef getAbbrevSection() const override { return abbrevSection; }
|
||||||
StringRef getStringSection() const override { return strSection; }
|
StringRef getStrSection() const override { return strSection; }
|
||||||
StringRef getLineStringSection() const override { return lineStringSection; }
|
StringRef getLineStrSection() const override { return lineStrSection; }
|
||||||
|
|
||||||
bool isLittleEndian() const override {
|
bool isLittleEndian() const override {
|
||||||
return ELFT::TargetEndianness == llvm::support::little;
|
return ELFT::TargetEndianness == llvm::support::little;
|
||||||
@ -74,16 +78,17 @@ template <class ELFT> class LLDDwarfObj final : public llvm::DWARFObject {
|
|||||||
uint64_t pos,
|
uint64_t pos,
|
||||||
ArrayRef<RelTy> rels) const;
|
ArrayRef<RelTy> rels) const;
|
||||||
|
|
||||||
LLDDWARFSection gnuPubNamesSection;
|
LLDDWARFSection gnuPubnamesSection;
|
||||||
LLDDWARFSection gnuPubTypesSection;
|
LLDDWARFSection gnuPubtypesSection;
|
||||||
LLDDWARFSection infoSection;
|
LLDDWARFSection infoSection;
|
||||||
LLDDWARFSection rangeSection;
|
LLDDWARFSection rangesSection;
|
||||||
LLDDWARFSection rngListsSection;
|
LLDDWARFSection rnglistsSection;
|
||||||
|
LLDDWARFSection strOffsetsSection;
|
||||||
LLDDWARFSection lineSection;
|
LLDDWARFSection lineSection;
|
||||||
LLDDWARFSection addrSection;
|
LLDDWARFSection addrSection;
|
||||||
StringRef abbrevSection;
|
StringRef abbrevSection;
|
||||||
StringRef strSection;
|
StringRef strSection;
|
||||||
StringRef lineStringSection;
|
StringRef lineStrSection;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace elf
|
} // namespace elf
|
||||||
|
@ -48,6 +48,7 @@
|
|||||||
#include "llvm/ADT/SetVector.h"
|
#include "llvm/ADT/SetVector.h"
|
||||||
#include "llvm/ADT/StringExtras.h"
|
#include "llvm/ADT/StringExtras.h"
|
||||||
#include "llvm/ADT/StringSwitch.h"
|
#include "llvm/ADT/StringSwitch.h"
|
||||||
|
#include "llvm/LTO/LTO.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/Compression.h"
|
#include "llvm/Support/Compression.h"
|
||||||
#include "llvm/Support/GlobPattern.h"
|
#include "llvm/Support/GlobPattern.h"
|
||||||
@ -65,24 +66,23 @@ using namespace llvm::object;
|
|||||||
using namespace llvm::sys;
|
using namespace llvm::sys;
|
||||||
using namespace llvm::support;
|
using namespace llvm::support;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
Configuration *elf::config;
|
Configuration *config;
|
||||||
LinkerDriver *elf::driver;
|
LinkerDriver *driver;
|
||||||
|
|
||||||
static void setConfigs(opt::InputArgList &args);
|
static void setConfigs(opt::InputArgList &args);
|
||||||
static void readConfigs(opt::InputArgList &args);
|
static void readConfigs(opt::InputArgList &args);
|
||||||
|
|
||||||
bool elf::link(ArrayRef<const char *> args, bool canExitEarly,
|
bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &error) {
|
||||||
raw_ostream &error) {
|
|
||||||
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
|
errorHandler().logName = args::getFilenameWithoutExe(args[0]);
|
||||||
errorHandler().errorLimitExceededMsg =
|
errorHandler().errorLimitExceededMsg =
|
||||||
"too many errors emitted, stopping now (use "
|
"too many errors emitted, stopping now (use "
|
||||||
"-error-limit=0 to see all errors)";
|
"-error-limit=0 to see all errors)";
|
||||||
errorHandler().errorOS = &error;
|
errorHandler().errorOS = &error;
|
||||||
errorHandler().exitEarly = canExitEarly;
|
errorHandler().exitEarly = canExitEarly;
|
||||||
errorHandler().colorDiagnostics = error.has_colors();
|
enableColors(error.has_colors());
|
||||||
|
|
||||||
inputSections.clear();
|
inputSections.clear();
|
||||||
outputSections.clear();
|
outputSections.clear();
|
||||||
@ -299,6 +299,9 @@ static void checkOptions() {
|
|||||||
if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64)
|
if (config->fixCortexA53Errata843419 && config->emachine != EM_AARCH64)
|
||||||
error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
|
error("--fix-cortex-a53-843419 is only supported on AArch64 targets");
|
||||||
|
|
||||||
|
if (config->fixCortexA8 && config->emachine != EM_ARM)
|
||||||
|
error("--fix-cortex-a8 is only supported on ARM targets");
|
||||||
|
|
||||||
if (config->tocOptimize && config->emachine != EM_PPC64)
|
if (config->tocOptimize && config->emachine != EM_PPC64)
|
||||||
error("--toc-optimize is only supported on the PowerPC64 target");
|
error("--toc-optimize is only supported on the PowerPC64 target");
|
||||||
|
|
||||||
@ -314,6 +317,9 @@ static void checkOptions() {
|
|||||||
if (!config->relocatable && !config->defineCommon)
|
if (!config->relocatable && !config->defineCommon)
|
||||||
error("-no-define-common not supported in non relocatable output");
|
error("-no-define-common not supported in non relocatable output");
|
||||||
|
|
||||||
|
if (config->strip == StripPolicy::All && config->emitRelocs)
|
||||||
|
error("--strip-all and --emit-relocs may not be used together");
|
||||||
|
|
||||||
if (config->zText && config->zIfuncNoplt)
|
if (config->zText && config->zIfuncNoplt)
|
||||||
error("-z text and -z ifunc-noplt may not be used together");
|
error("-z text and -z ifunc-noplt may not be used together");
|
||||||
|
|
||||||
@ -328,6 +334,8 @@ static void checkOptions() {
|
|||||||
error("-r and --icf may not be used together");
|
error("-r and --icf may not be used together");
|
||||||
if (config->pie)
|
if (config->pie)
|
||||||
error("-r and -pie may not be used together");
|
error("-r and -pie may not be used together");
|
||||||
|
if (config->exportDynamic)
|
||||||
|
error("-r and --export-dynamic may not be used together");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->executeOnly) {
|
if (config->executeOnly) {
|
||||||
@ -373,17 +381,32 @@ static bool getZFlag(opt::InputArgList &args, StringRef k1, StringRef k2,
|
|||||||
return Default;
|
return Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SeparateSegmentKind getZSeparate(opt::InputArgList &args) {
|
||||||
|
for (auto *arg : args.filtered_reverse(OPT_z)) {
|
||||||
|
StringRef v = arg->getValue();
|
||||||
|
if (v == "noseparate-code")
|
||||||
|
return SeparateSegmentKind::None;
|
||||||
|
if (v == "separate-code")
|
||||||
|
return SeparateSegmentKind::Code;
|
||||||
|
if (v == "separate-loadable-segments")
|
||||||
|
return SeparateSegmentKind::Loadable;
|
||||||
|
}
|
||||||
|
return SeparateSegmentKind::None;
|
||||||
|
}
|
||||||
|
|
||||||
static bool isKnownZFlag(StringRef s) {
|
static bool isKnownZFlag(StringRef s) {
|
||||||
return s == "combreloc" || s == "copyreloc" || s == "defs" ||
|
return s == "combreloc" || s == "copyreloc" || s == "defs" ||
|
||||||
s == "execstack" || s == "global" || s == "hazardplt" ||
|
s == "execstack" || s == "global" || s == "hazardplt" ||
|
||||||
s == "ifunc-noplt" || s == "initfirst" || s == "interpose" ||
|
s == "ifunc-noplt" || s == "initfirst" || s == "interpose" ||
|
||||||
s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
|
s == "keep-text-section-prefix" || s == "lazy" || s == "muldefs" ||
|
||||||
|
s == "separate-code" || s == "separate-loadable-segments" ||
|
||||||
s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
|
s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
|
||||||
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
|
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
|
||||||
s == "nokeep-text-section-prefix" || s == "norelro" || s == "notext" ||
|
s == "nokeep-text-section-prefix" || s == "norelro" ||
|
||||||
s == "now" || s == "origin" || s == "relro" || s == "retpolineplt" ||
|
s == "noseparate-code" || s == "notext" || s == "now" ||
|
||||||
s == "rodynamic" || s == "text" || s == "wxneeded" ||
|
s == "origin" || s == "relro" || s == "retpolineplt" ||
|
||||||
s.startswith("common-page-size") || s.startswith("max-page-size=") ||
|
s == "rodynamic" || s == "text" || s == "undefs" || s == "wxneeded" ||
|
||||||
|
s.startswith("common-page-size=") || s.startswith("max-page-size=") ||
|
||||||
s.startswith("stack-size=");
|
s.startswith("stack-size=");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,6 +536,8 @@ static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &args) {
|
|||||||
case OPT_z:
|
case OPT_z:
|
||||||
if (StringRef(arg->getValue()) == "defs")
|
if (StringRef(arg->getValue()) == "defs")
|
||||||
return errorOrWarn;
|
return errorOrWarn;
|
||||||
|
if (StringRef(arg->getValue()) == "undefs")
|
||||||
|
return UnresolvedPolicy::Ignore;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -747,6 +772,12 @@ static bool getCompressDebugSections(opt::InputArgList &args) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static StringRef getAliasSpelling(opt::Arg *arg) {
|
||||||
|
if (const opt::Arg *alias = arg->getAlias())
|
||||||
|
return alias->getSpelling();
|
||||||
|
return arg->getSpelling();
|
||||||
|
}
|
||||||
|
|
||||||
static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
|
static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
|
||||||
unsigned id) {
|
unsigned id) {
|
||||||
auto *arg = args.getLastArg(id);
|
auto *arg = args.getLastArg(id);
|
||||||
@ -756,7 +787,7 @@ static std::pair<StringRef, StringRef> getOldNewOptions(opt::InputArgList &args,
|
|||||||
StringRef s = arg->getValue();
|
StringRef s = arg->getValue();
|
||||||
std::pair<StringRef, StringRef> ret = s.split(';');
|
std::pair<StringRef, StringRef> ret = s.split(';');
|
||||||
if (ret.second.empty())
|
if (ret.second.empty())
|
||||||
error(arg->getSpelling() + " expects 'old;new' format, but got " + s);
|
error(getAliasSpelling(arg) + " expects 'old;new' format, but got " + s);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -829,6 +860,7 @@ static void readConfigs(opt::InputArgList &args) {
|
|||||||
config->filterList = args::getStrings(args, OPT_filter);
|
config->filterList = args::getStrings(args, OPT_filter);
|
||||||
config->fini = args.getLastArgValue(OPT_fini, "_fini");
|
config->fini = args.getLastArgValue(OPT_fini, "_fini");
|
||||||
config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419);
|
config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419);
|
||||||
|
config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8);
|
||||||
config->forceBTI = args.hasArg(OPT_force_bti);
|
config->forceBTI = args.hasArg(OPT_force_bti);
|
||||||
config->requireCET = args.hasArg(OPT_require_cet);
|
config->requireCET = args.hasArg(OPT_require_cet);
|
||||||
config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
|
config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
|
||||||
@ -847,7 +879,7 @@ static void readConfigs(opt::InputArgList &args) {
|
|||||||
config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager);
|
config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager);
|
||||||
config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
|
config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
|
||||||
config->ltoo = args::getInteger(args, OPT_lto_O, 2);
|
config->ltoo = args::getInteger(args, OPT_lto_O, 2);
|
||||||
config->ltoObjPath = args.getLastArgValue(OPT_plugin_opt_obj_path_eq);
|
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq);
|
||||||
config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
|
config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
|
||||||
config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
|
config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
|
||||||
config->mapFile = args.getLastArgValue(OPT_Map);
|
config->mapFile = args.getLastArgValue(OPT_Map);
|
||||||
@ -892,17 +924,15 @@ static void readConfigs(opt::InputArgList &args) {
|
|||||||
config->thinLTOCachePolicy = CHECK(
|
config->thinLTOCachePolicy = CHECK(
|
||||||
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
|
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
|
||||||
"--thinlto-cache-policy: invalid cache policy");
|
"--thinlto-cache-policy: invalid cache policy");
|
||||||
config->thinLTOEmitImportsFiles =
|
config->thinLTOEmitImportsFiles = args.hasArg(OPT_thinlto_emit_imports_files);
|
||||||
args.hasArg(OPT_plugin_opt_thinlto_emit_imports_files);
|
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
|
||||||
config->thinLTOIndexOnly = args.hasArg(OPT_plugin_opt_thinlto_index_only) ||
|
args.hasArg(OPT_thinlto_index_only_eq);
|
||||||
args.hasArg(OPT_plugin_opt_thinlto_index_only_eq);
|
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
|
||||||
config->thinLTOIndexOnlyArg =
|
|
||||||
args.getLastArgValue(OPT_plugin_opt_thinlto_index_only_eq);
|
|
||||||
config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
|
config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
|
||||||
config->thinLTOObjectSuffixReplace =
|
config->thinLTOObjectSuffixReplace =
|
||||||
getOldNewOptions(args, OPT_plugin_opt_thinlto_object_suffix_replace_eq);
|
getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
|
||||||
config->thinLTOPrefixReplace =
|
config->thinLTOPrefixReplace =
|
||||||
getOldNewOptions(args, OPT_plugin_opt_thinlto_prefix_replace_eq);
|
getOldNewOptions(args, OPT_thinlto_prefix_replace_eq);
|
||||||
config->trace = args.hasArg(OPT_trace);
|
config->trace = args.hasArg(OPT_trace);
|
||||||
config->undefined = args::getStrings(args, OPT_undefined);
|
config->undefined = args::getStrings(args, OPT_undefined);
|
||||||
config->undefinedVersion =
|
config->undefinedVersion =
|
||||||
@ -935,6 +965,7 @@ static void readConfigs(opt::InputArgList &args) {
|
|||||||
config->zRelro = getZFlag(args, "relro", "norelro", true);
|
config->zRelro = getZFlag(args, "relro", "norelro", true);
|
||||||
config->zRetpolineplt = hasZOption(args, "retpolineplt");
|
config->zRetpolineplt = hasZOption(args, "retpolineplt");
|
||||||
config->zRodynamic = hasZOption(args, "rodynamic");
|
config->zRodynamic = hasZOption(args, "rodynamic");
|
||||||
|
config->zSeparate = getZSeparate(args);
|
||||||
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
|
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
|
||||||
config->zText = getZFlag(args, "text", "notext", true);
|
config->zText = getZFlag(args, "text", "notext", true);
|
||||||
config->zWxneeded = hasZOption(args, "wxneeded");
|
config->zWxneeded = hasZOption(args, "wxneeded");
|
||||||
@ -1010,30 +1041,33 @@ static void readConfigs(opt::InputArgList &args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(config->versionDefinitions.empty());
|
||||||
|
config->versionDefinitions.push_back({"local", (uint16_t)VER_NDX_LOCAL, {}});
|
||||||
|
config->versionDefinitions.push_back(
|
||||||
|
{"global", (uint16_t)VER_NDX_GLOBAL, {}});
|
||||||
|
|
||||||
// If --retain-symbol-file is used, we'll keep only the symbols listed in
|
// If --retain-symbol-file is used, we'll keep only the symbols listed in
|
||||||
// the file and discard all others.
|
// the file and discard all others.
|
||||||
if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) {
|
if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) {
|
||||||
config->defaultSymbolVersion = VER_NDX_LOCAL;
|
config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(
|
||||||
|
{"*", /*isExternCpp=*/false, /*hasWildcard=*/true});
|
||||||
if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
|
if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
|
||||||
for (StringRef s : args::getLines(*buffer))
|
for (StringRef s : args::getLines(*buffer))
|
||||||
config->versionScriptGlobals.push_back(
|
config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(
|
||||||
{s, /*IsExternCpp*/ false, /*HasWildcard*/ false});
|
{s, /*isExternCpp=*/false, /*hasWildcard=*/false});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasExportDynamic =
|
|
||||||
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
|
|
||||||
|
|
||||||
// Parses -dynamic-list and -export-dynamic-symbol. They make some
|
// Parses -dynamic-list and -export-dynamic-symbol. They make some
|
||||||
// symbols private. Note that -export-dynamic takes precedence over them
|
// symbols private. Note that -export-dynamic takes precedence over them
|
||||||
// as it says all symbols should be exported.
|
// as it says all symbols should be exported.
|
||||||
if (!hasExportDynamic) {
|
if (!config->exportDynamic) {
|
||||||
for (auto *arg : args.filtered(OPT_dynamic_list))
|
for (auto *arg : args.filtered(OPT_dynamic_list))
|
||||||
if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
|
if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
|
||||||
readDynamicList(*buffer);
|
readDynamicList(*buffer);
|
||||||
|
|
||||||
for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
|
for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
|
||||||
config->dynamicList.push_back(
|
config->dynamicList.push_back(
|
||||||
{arg->getValue(), /*IsExternCpp*/ false, /*HasWildcard*/ false});
|
{arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/false});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If --export-dynamic-symbol=foo is given and symbol foo is defined in
|
// If --export-dynamic-symbol=foo is given and symbol foo is defined in
|
||||||
@ -1659,12 +1693,6 @@ template <class ELFT> static uint32_t getAndFeatures() {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *libcallRoutineNames[] = {
|
|
||||||
#define HANDLE_LIBCALL(code, name) name,
|
|
||||||
#include "llvm/IR/RuntimeLibcalls.def"
|
|
||||||
#undef HANDLE_LIBCALL
|
|
||||||
};
|
|
||||||
|
|
||||||
// Do actual linking. Note that when this function is called,
|
// Do actual linking. Note that when this function is called,
|
||||||
// all linker scripts have already been parsed.
|
// all linker scripts have already been parsed.
|
||||||
template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
|
template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
|
||||||
@ -1755,7 +1783,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
|
|||||||
// libcall symbols will be added to the link after LTO when we add the LTO
|
// libcall symbols will be added to the link after LTO when we add the LTO
|
||||||
// object file to the link.
|
// object file to the link.
|
||||||
if (!bitcodeFiles.empty())
|
if (!bitcodeFiles.empty())
|
||||||
for (const char *s : libcallRoutineNames)
|
for (auto *s : lto::LTO::getRuntimeLibcallSymbols())
|
||||||
handleLibcall(s);
|
handleLibcall(s);
|
||||||
|
|
||||||
// Return if there were name resolution errors.
|
// Return if there were name resolution errors.
|
||||||
@ -1880,20 +1908,54 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
|
|||||||
"feature detected");
|
"feature detected");
|
||||||
}
|
}
|
||||||
|
|
||||||
// This adds a .comment section containing a version string. We have to add it
|
// This adds a .comment section containing a version string.
|
||||||
// before mergeSections because the .comment section is a mergeable section.
|
|
||||||
if (!config->relocatable)
|
if (!config->relocatable)
|
||||||
inputSections.push_back(createCommentSection());
|
inputSections.push_back(createCommentSection());
|
||||||
|
|
||||||
// Replace common symbols with regular symbols.
|
// Replace common symbols with regular symbols.
|
||||||
replaceCommonSymbols();
|
replaceCommonSymbols();
|
||||||
|
|
||||||
// Do size optimizations: garbage collection, merging of SHF_MERGE sections
|
// Split SHF_MERGE and .eh_frame sections into pieces in preparation for garbage collection.
|
||||||
// and identical code folding.
|
|
||||||
splitSections<ELFT>();
|
splitSections<ELFT>();
|
||||||
|
|
||||||
|
// Garbage collection and removal of shared symbols from unused shared objects.
|
||||||
markLive<ELFT>();
|
markLive<ELFT>();
|
||||||
demoteSharedSymbols();
|
demoteSharedSymbols();
|
||||||
mergeSections();
|
|
||||||
|
// Make copies of any input sections that need to be copied into each
|
||||||
|
// partition.
|
||||||
|
copySectionsIntoPartitions();
|
||||||
|
|
||||||
|
// Create synthesized sections such as .got and .plt. This is called before
|
||||||
|
// processSectionCommands() so that they can be placed by SECTIONS commands.
|
||||||
|
createSyntheticSections<ELFT>();
|
||||||
|
|
||||||
|
// Some input sections that are used for exception handling need to be moved
|
||||||
|
// into synthetic sections. Do that now so that they aren't assigned to
|
||||||
|
// output sections in the usual way.
|
||||||
|
if (!config->relocatable)
|
||||||
|
combineEhSections();
|
||||||
|
|
||||||
|
// Create output sections described by SECTIONS commands.
|
||||||
|
script->processSectionCommands();
|
||||||
|
|
||||||
|
// Linker scripts control how input sections are assigned to output sections.
|
||||||
|
// Input sections that were not handled by scripts are called "orphans", and
|
||||||
|
// they are assigned to output sections by the default rule. Process that.
|
||||||
|
script->addOrphanSections();
|
||||||
|
|
||||||
|
// Migrate InputSectionDescription::sectionBases to sections. This includes
|
||||||
|
// merging MergeInputSections into a single MergeSyntheticSection. From this
|
||||||
|
// point onwards InputSectionDescription::sections should be used instead of
|
||||||
|
// sectionBases.
|
||||||
|
for (BaseCommand *base : script->sectionCommands)
|
||||||
|
if (auto *sec = dyn_cast<OutputSection>(base))
|
||||||
|
sec->finalizeInputSections();
|
||||||
|
llvm::erase_if(inputSections,
|
||||||
|
[](InputSectionBase *s) { return isa<MergeInputSection>(s); });
|
||||||
|
|
||||||
|
// Two input sections with different output sections should not be folded.
|
||||||
|
// ICF runs after processSectionCommands() so that we know the output sections.
|
||||||
if (config->icf != ICFLevel::None) {
|
if (config->icf != ICFLevel::None) {
|
||||||
findKeepUniqueSections<ELFT>(args);
|
findKeepUniqueSections<ELFT>(args);
|
||||||
doIcf<ELFT>();
|
doIcf<ELFT>();
|
||||||
@ -1910,3 +1972,6 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
|
|||||||
// Write the result to the file.
|
// Write the result to the file.
|
||||||
writeResult<ELFT>();
|
writeResult<ELFT>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -30,8 +30,8 @@ using namespace llvm;
|
|||||||
using namespace llvm::sys;
|
using namespace llvm::sys;
|
||||||
using namespace llvm::opt;
|
using namespace llvm::opt;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
// Create OptTable
|
// Create OptTable
|
||||||
|
|
||||||
@ -59,15 +59,15 @@ static void handleColorDiagnostics(opt::InputArgList &args) {
|
|||||||
if (!arg)
|
if (!arg)
|
||||||
return;
|
return;
|
||||||
if (arg->getOption().getID() == OPT_color_diagnostics) {
|
if (arg->getOption().getID() == OPT_color_diagnostics) {
|
||||||
errorHandler().colorDiagnostics = true;
|
enableColors(true);
|
||||||
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
|
} else if (arg->getOption().getID() == OPT_no_color_diagnostics) {
|
||||||
errorHandler().colorDiagnostics = false;
|
enableColors(false);
|
||||||
} else {
|
} else {
|
||||||
StringRef s = arg->getValue();
|
StringRef s = arg->getValue();
|
||||||
if (s == "always")
|
if (s == "always")
|
||||||
errorHandler().colorDiagnostics = true;
|
enableColors(true);
|
||||||
else if (s == "never")
|
else if (s == "never")
|
||||||
errorHandler().colorDiagnostics = false;
|
enableColors(false);
|
||||||
else if (s != "auto")
|
else if (s != "auto")
|
||||||
error("unknown option: --color-diagnostics=" + s);
|
error("unknown option: --color-diagnostics=" + s);
|
||||||
}
|
}
|
||||||
@ -143,7 +143,7 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> argv) {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
void elf::printHelp() {
|
void printHelp() {
|
||||||
ELFOptTable().PrintHelp(
|
ELFOptTable().PrintHelp(
|
||||||
outs(), (config->progName + " [options] file...").str().c_str(), "lld",
|
outs(), (config->progName + " [options] file...").str().c_str(), "lld",
|
||||||
false /*ShowHidden*/, true /*ShowAllAliases*/);
|
false /*ShowHidden*/, true /*ShowAllAliases*/);
|
||||||
@ -165,7 +165,7 @@ static std::string rewritePath(StringRef s) {
|
|||||||
|
|
||||||
// Reconstructs command line arguments so that so that you can re-run
|
// Reconstructs command line arguments so that so that you can re-run
|
||||||
// the same command with the same inputs. This is for --reproduce.
|
// the same command with the same inputs. This is for --reproduce.
|
||||||
std::string elf::createResponseFile(const opt::InputArgList &args) {
|
std::string createResponseFile(const opt::InputArgList &args) {
|
||||||
SmallString<0> data;
|
SmallString<0> data;
|
||||||
raw_svector_ostream os(data);
|
raw_svector_ostream os(data);
|
||||||
os << "--chroot .\n";
|
os << "--chroot .\n";
|
||||||
@ -216,7 +216,7 @@ static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<std::string> elf::findFromSearchPaths(StringRef path) {
|
Optional<std::string> findFromSearchPaths(StringRef path) {
|
||||||
for (StringRef dir : config->searchPaths)
|
for (StringRef dir : config->searchPaths)
|
||||||
if (Optional<std::string> s = findFile(dir, path))
|
if (Optional<std::string> s = findFile(dir, path))
|
||||||
return s;
|
return s;
|
||||||
@ -225,7 +225,7 @@ Optional<std::string> elf::findFromSearchPaths(StringRef path) {
|
|||||||
|
|
||||||
// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
|
// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
|
||||||
// search paths.
|
// search paths.
|
||||||
Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
|
Optional<std::string> searchLibraryBaseName(StringRef name) {
|
||||||
for (StringRef dir : config->searchPaths) {
|
for (StringRef dir : config->searchPaths) {
|
||||||
if (!config->isStatic)
|
if (!config->isStatic)
|
||||||
if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
|
if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
|
||||||
@ -237,17 +237,20 @@ Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is for -l<namespec>.
|
// This is for -l<namespec>.
|
||||||
Optional<std::string> elf::searchLibrary(StringRef name) {
|
Optional<std::string> searchLibrary(StringRef name) {
|
||||||
if (name.startswith(":"))
|
if (name.startswith(":"))
|
||||||
return findFromSearchPaths(name.substr(1));
|
return findFromSearchPaths(name.substr(1));
|
||||||
return searchLibraryBaseName (name);
|
return searchLibraryBaseName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a linker/version script doesn't exist in the current directory, we also
|
// If a linker/version script doesn't exist in the current directory, we also
|
||||||
// look for the script in the '-L' search paths. This matches the behaviour of
|
// look for the script in the '-L' search paths. This matches the behaviour of
|
||||||
// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
|
// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
|
||||||
Optional<std::string> elf::searchScript(StringRef name) {
|
Optional<std::string> searchScript(StringRef name) {
|
||||||
if (fs::exists(name))
|
if (fs::exists(name))
|
||||||
return name.str();
|
return name.str();
|
||||||
return findFromSearchPaths(name);
|
return findFromSearchPaths(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -30,9 +30,8 @@ using namespace llvm::ELF;
|
|||||||
using namespace llvm::dwarf;
|
using namespace llvm::dwarf;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
class EhReader {
|
class EhReader {
|
||||||
public:
|
public:
|
||||||
@ -57,7 +56,7 @@ class EhReader {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) {
|
size_t readEhRecordSize(InputSectionBase *s, size_t off) {
|
||||||
return EhReader(s, s->data().slice(off)).readEhRecordSize();
|
return EhReader(s, s->data().slice(off)).readEhRecordSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +148,7 @@ void EhReader::skipAugP() {
|
|||||||
d = d.slice(size);
|
d = d.slice(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
|
uint8_t getFdeEncoding(EhSectionPiece *p) {
|
||||||
return EhReader(p->sec, p->data()).getFdeEncoding();
|
return EhReader(p->sec, p->data()).getFdeEncoding();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,3 +194,6 @@ uint8_t EhReader::getFdeEncoding() {
|
|||||||
}
|
}
|
||||||
return DW_EH_PE_absptr;
|
return DW_EH_PE_absptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -74,6 +74,8 @@
|
|||||||
|
|
||||||
#include "ICF.h"
|
#include "ICF.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "LinkerScript.h"
|
||||||
|
#include "OutputSections.h"
|
||||||
#include "SymbolTable.h"
|
#include "SymbolTable.h"
|
||||||
#include "Symbols.h"
|
#include "Symbols.h"
|
||||||
#include "SyntheticSections.h"
|
#include "SyntheticSections.h"
|
||||||
@ -86,12 +88,12 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
namespace {
|
namespace {
|
||||||
template <class ELFT> class ICF {
|
template <class ELFT> class ICF {
|
||||||
public:
|
public:
|
||||||
@ -304,10 +306,8 @@ bool ICF<ELFT>::equalsConstant(const InputSection *a, const InputSection *b) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If two sections have different output sections, we cannot merge them.
|
// If two sections have different output sections, we cannot merge them.
|
||||||
// FIXME: This doesn't do the right thing in the case where there is a linker
|
assert(a->getParent() && b->getParent());
|
||||||
// script. We probably need to move output section assignment before ICF to
|
if (a->getParent() != b->getParent())
|
||||||
// get the correct behaviour here.
|
|
||||||
if (getOutputSectionName(a) != getOutputSectionName(b))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (a->areRelocsRela)
|
if (a->areRelocsRela)
|
||||||
@ -446,10 +446,11 @@ static void print(const Twine &s) {
|
|||||||
// The main function of ICF.
|
// The main function of ICF.
|
||||||
template <class ELFT> void ICF<ELFT>::run() {
|
template <class ELFT> void ICF<ELFT>::run() {
|
||||||
// Collect sections to merge.
|
// Collect sections to merge.
|
||||||
for (InputSectionBase *sec : inputSections)
|
for (InputSectionBase *sec : inputSections) {
|
||||||
if (auto *s = dyn_cast<InputSection>(sec))
|
auto *s = cast<InputSection>(sec);
|
||||||
if (isEligible(s))
|
if (isEligible(s))
|
||||||
sections.push_back(s);
|
sections.push_back(s);
|
||||||
|
}
|
||||||
|
|
||||||
// Initially, we use hash values to partition sections.
|
// Initially, we use hash values to partition sections.
|
||||||
parallelForEach(sections, [&](InputSection *s) {
|
parallelForEach(sections, [&](InputSection *s) {
|
||||||
@ -499,12 +500,24 @@ template <class ELFT> void ICF<ELFT>::run() {
|
|||||||
isec->markDead();
|
isec->markDead();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// InputSectionDescription::sections is populated by processSectionCommands().
|
||||||
|
// ICF may fold some input sections assigned to output sections. Remove them.
|
||||||
|
for (BaseCommand *base : script->sectionCommands)
|
||||||
|
if (auto *sec = dyn_cast<OutputSection>(base))
|
||||||
|
for (BaseCommand *sub_base : sec->sectionCommands)
|
||||||
|
if (auto *isd = dyn_cast<InputSectionDescription>(sub_base))
|
||||||
|
llvm::erase_if(isd->sections,
|
||||||
|
[](InputSection *isec) { return !isec->isLive(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ICF entry point function.
|
// ICF entry point function.
|
||||||
template <class ELFT> void elf::doIcf() { ICF<ELFT>().run(); }
|
template <class ELFT> void doIcf() { ICF<ELFT>().run(); }
|
||||||
|
|
||||||
template void elf::doIcf<ELF32LE>();
|
template void doIcf<ELF32LE>();
|
||||||
template void elf::doIcf<ELF32BE>();
|
template void doIcf<ELF32BE>();
|
||||||
template void elf::doIcf<ELF64LE>();
|
template void doIcf<ELF64LE>();
|
||||||
template void elf::doIcf<ELF64BE>();
|
template void doIcf<ELF64BE>();
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include "lld/Common/Memory.h"
|
#include "lld/Common/Memory.h"
|
||||||
#include "llvm/ADT/STLExtras.h"
|
#include "llvm/ADT/STLExtras.h"
|
||||||
#include "llvm/CodeGen/Analysis.h"
|
#include "llvm/CodeGen/Analysis.h"
|
||||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
|
||||||
#include "llvm/IR/LLVMContext.h"
|
#include "llvm/IR/LLVMContext.h"
|
||||||
#include "llvm/IR/Module.h"
|
#include "llvm/IR/Module.h"
|
||||||
#include "llvm/LTO/LTO.h"
|
#include "llvm/LTO/LTO.h"
|
||||||
@ -37,18 +36,31 @@ using namespace llvm::sys;
|
|||||||
using namespace llvm::sys::fs;
|
using namespace llvm::sys::fs;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
|
||||||
|
std::string toString(const elf::InputFile *f) {
|
||||||
|
if (!f)
|
||||||
|
return "<internal>";
|
||||||
|
|
||||||
|
if (f->toStringCache.empty()) {
|
||||||
|
if (f->archiveName.empty())
|
||||||
|
f->toStringCache = f->getName();
|
||||||
|
else
|
||||||
|
f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
|
||||||
|
}
|
||||||
|
return f->toStringCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace elf {
|
||||||
bool InputFile::isInGroup;
|
bool InputFile::isInGroup;
|
||||||
uint32_t InputFile::nextGroupId;
|
uint32_t InputFile::nextGroupId;
|
||||||
std::vector<BinaryFile *> elf::binaryFiles;
|
std::vector<BinaryFile *> binaryFiles;
|
||||||
std::vector<BitcodeFile *> elf::bitcodeFiles;
|
std::vector<BitcodeFile *> bitcodeFiles;
|
||||||
std::vector<LazyObjFile *> elf::lazyObjFiles;
|
std::vector<LazyObjFile *> lazyObjFiles;
|
||||||
std::vector<InputFile *> elf::objectFiles;
|
std::vector<InputFile *> objectFiles;
|
||||||
std::vector<SharedFile *> elf::sharedFiles;
|
std::vector<SharedFile *> sharedFiles;
|
||||||
|
|
||||||
std::unique_ptr<TarWriter> elf::tar;
|
std::unique_ptr<TarWriter> tar;
|
||||||
|
|
||||||
static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
|
static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
|
||||||
unsigned char size;
|
unsigned char size;
|
||||||
@ -88,7 +100,7 @@ InputFile::InputFile(Kind k, MemoryBufferRef m)
|
|||||||
++nextGroupId;
|
++nextGroupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<MemoryBufferRef> elf::readFile(StringRef path) {
|
Optional<MemoryBufferRef> readFile(StringRef path) {
|
||||||
// The --chroot option changes our virtual root directory.
|
// The --chroot option changes our virtual root directory.
|
||||||
// This is useful when you are dealing with files created by --reproduce.
|
// This is useful when you are dealing with files created by --reproduce.
|
||||||
if (!config->chroot.empty() && path.startswith("/"))
|
if (!config->chroot.empty() && path.startswith("/"))
|
||||||
@ -127,18 +139,18 @@ static bool isCompatible(InputFile *file) {
|
|||||||
|
|
||||||
if (!config->emulation.empty()) {
|
if (!config->emulation.empty()) {
|
||||||
error(toString(file) + " is incompatible with " + config->emulation);
|
error(toString(file) + " is incompatible with " + config->emulation);
|
||||||
} else {
|
return false;
|
||||||
InputFile *existing;
|
|
||||||
if (!objectFiles.empty())
|
|
||||||
existing = objectFiles[0];
|
|
||||||
else if (!sharedFiles.empty())
|
|
||||||
existing = sharedFiles[0];
|
|
||||||
else
|
|
||||||
existing = bitcodeFiles[0];
|
|
||||||
|
|
||||||
error(toString(file) + " is incompatible with " + toString(existing));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputFile *existing;
|
||||||
|
if (!objectFiles.empty())
|
||||||
|
existing = objectFiles[0];
|
||||||
|
else if (!sharedFiles.empty())
|
||||||
|
existing = sharedFiles[0];
|
||||||
|
else
|
||||||
|
existing = bitcodeFiles[0];
|
||||||
|
|
||||||
|
error(toString(file) + " is incompatible with " + toString(existing));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +200,7 @@ template <class ELFT> static void doParseFile(InputFile *file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add symbols in File to the symbol table.
|
// Add symbols in File to the symbol table.
|
||||||
void elf::parseFile(InputFile *file) {
|
void parseFile(InputFile *file) {
|
||||||
switch (config->ekind) {
|
switch (config->ekind) {
|
||||||
case ELF32LEKind:
|
case ELF32LEKind:
|
||||||
doParseFile<ELF32LE>(file);
|
doParseFile<ELF32LE>(file);
|
||||||
@ -252,57 +264,8 @@ std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
|
template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
|
||||||
dwarf = llvm::make_unique<DWARFContext>(make_unique<LLDDwarfObj<ELFT>>(this));
|
dwarf = make<DWARFCache>(std::make_unique<DWARFContext>(
|
||||||
for (std::unique_ptr<DWARFUnit> &cu : dwarf->compile_units()) {
|
std::make_unique<LLDDwarfObj<ELFT>>(this)));
|
||||||
auto report = [](Error err) {
|
|
||||||
handleAllErrors(std::move(err),
|
|
||||||
[](ErrorInfoBase &info) { warn(info.message()); });
|
|
||||||
};
|
|
||||||
Expected<const DWARFDebugLine::LineTable *> expectedLT =
|
|
||||||
dwarf->getLineTableForUnit(cu.get(), report);
|
|
||||||
const DWARFDebugLine::LineTable *lt = nullptr;
|
|
||||||
if (expectedLT)
|
|
||||||
lt = *expectedLT;
|
|
||||||
else
|
|
||||||
report(expectedLT.takeError());
|
|
||||||
if (!lt)
|
|
||||||
continue;
|
|
||||||
lineTables.push_back(lt);
|
|
||||||
|
|
||||||
// Loop over variable records and insert them to variableLoc.
|
|
||||||
for (const auto &entry : cu->dies()) {
|
|
||||||
DWARFDie die(cu.get(), &entry);
|
|
||||||
// Skip all tags that are not variables.
|
|
||||||
if (die.getTag() != dwarf::DW_TAG_variable)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Skip if a local variable because we don't need them for generating
|
|
||||||
// error messages. In general, only non-local symbols can fail to be
|
|
||||||
// linked.
|
|
||||||
if (!dwarf::toUnsigned(die.find(dwarf::DW_AT_external), 0))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Get the source filename index for the variable.
|
|
||||||
unsigned file = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_file), 0);
|
|
||||||
if (!lt->hasFileAtIndex(file))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Get the line number on which the variable is declared.
|
|
||||||
unsigned line = dwarf::toUnsigned(die.find(dwarf::DW_AT_decl_line), 0);
|
|
||||||
|
|
||||||
// Here we want to take the variable name to add it into variableLoc.
|
|
||||||
// Variable can have regular and linkage name associated. At first, we try
|
|
||||||
// to get linkage name as it can be different, for example when we have
|
|
||||||
// two variables in different namespaces of the same object. Use common
|
|
||||||
// name otherwise, but handle the case when it also absent in case if the
|
|
||||||
// input object file lacks some debug info.
|
|
||||||
StringRef name =
|
|
||||||
dwarf::toString(die.find(dwarf::DW_AT_linkage_name),
|
|
||||||
dwarf::toString(die.find(dwarf::DW_AT_name), ""));
|
|
||||||
if (!name.empty())
|
|
||||||
variableLoc.insert({name, {lt, file, line}});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the pair of file name and line number describing location of data
|
// Returns the pair of file name and line number describing location of data
|
||||||
@ -312,19 +275,7 @@ Optional<std::pair<std::string, unsigned>>
|
|||||||
ObjFile<ELFT>::getVariableLoc(StringRef name) {
|
ObjFile<ELFT>::getVariableLoc(StringRef name) {
|
||||||
llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
|
llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
|
||||||
|
|
||||||
// Return if we have no debug information about data object.
|
return dwarf->getVariableLoc(name);
|
||||||
auto it = variableLoc.find(name);
|
|
||||||
if (it == variableLoc.end())
|
|
||||||
return None;
|
|
||||||
|
|
||||||
// Take file name string from line table.
|
|
||||||
std::string fileName;
|
|
||||||
if (!it->second.lt->getFileNameByIndex(
|
|
||||||
it->second.file, {},
|
|
||||||
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, fileName))
|
|
||||||
return None;
|
|
||||||
|
|
||||||
return std::make_pair(fileName, it->second.line);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns source line information for a given offset
|
// Returns source line information for a given offset
|
||||||
@ -346,28 +297,7 @@ Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s,
|
|||||||
|
|
||||||
// Use fake address calcuated by adding section file offset and offset in
|
// Use fake address calcuated by adding section file offset and offset in
|
||||||
// section. See comments for ObjectInfo class.
|
// section. See comments for ObjectInfo class.
|
||||||
DILineInfo info;
|
return dwarf->getDILineInfo(s->getOffsetInFile() + offset, sectionIndex);
|
||||||
for (const llvm::DWARFDebugLine::LineTable *lt : lineTables) {
|
|
||||||
if (lt->getFileLineInfoForAddress(
|
|
||||||
{s->getOffsetInFile() + offset, sectionIndex}, nullptr,
|
|
||||||
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, info))
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
|
|
||||||
std::string lld::toString(const InputFile *f) {
|
|
||||||
if (!f)
|
|
||||||
return "<internal>";
|
|
||||||
|
|
||||||
if (f->toStringCache.empty()) {
|
|
||||||
if (f->archiveName.empty())
|
|
||||||
f->toStringCache = f->getName();
|
|
||||||
else
|
|
||||||
f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
|
|
||||||
}
|
|
||||||
return f->toStringCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
|
ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
|
||||||
@ -484,7 +414,8 @@ StringRef ObjFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> sections,
|
|||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec) {
|
template <class ELFT>
|
||||||
|
bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec, StringRef name) {
|
||||||
// On a regular link we don't merge sections if -O0 (default is -O1). This
|
// On a regular link we don't merge sections if -O0 (default is -O1). This
|
||||||
// sometimes makes the linker significantly faster, although the output will
|
// sometimes makes the linker significantly faster, although the output will
|
||||||
// be bigger.
|
// be bigger.
|
||||||
@ -516,14 +447,16 @@ template <class ELFT> bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec) {
|
|||||||
if (entSize == 0)
|
if (entSize == 0)
|
||||||
return false;
|
return false;
|
||||||
if (sec.sh_size % entSize)
|
if (sec.sh_size % entSize)
|
||||||
fatal(toString(this) +
|
fatal(toString(this) + ":(" + name + "): SHF_MERGE section size (" +
|
||||||
": SHF_MERGE section size must be a multiple of sh_entsize");
|
Twine(sec.sh_size) + ") must be a multiple of sh_entsize (" +
|
||||||
|
Twine(entSize) + ")");
|
||||||
|
|
||||||
uint64_t flags = sec.sh_flags;
|
uint64_t flags = sec.sh_flags;
|
||||||
if (!(flags & SHF_MERGE))
|
if (!(flags & SHF_MERGE))
|
||||||
return false;
|
return false;
|
||||||
if (flags & SHF_WRITE)
|
if (flags & SHF_WRITE)
|
||||||
fatal(toString(this) + ": writable SHF_MERGE section is not supported");
|
fatal(toString(this) + ":(" + name +
|
||||||
|
"): writable SHF_MERGE section is not supported");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -573,7 +506,7 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
|
|||||||
this->sectionStringTable =
|
this->sectionStringTable =
|
||||||
CHECK(obj.getSectionStringTable(objSections), this);
|
CHECK(obj.getSectionStringTable(objSections), this);
|
||||||
|
|
||||||
for (size_t i = 0, e = objSections.size(); i < e; i++) {
|
for (size_t i = 0, e = objSections.size(); i < e; ++i) {
|
||||||
if (this->sections[i] == &InputSection::discarded)
|
if (this->sections[i] == &InputSection::discarded)
|
||||||
continue;
|
continue;
|
||||||
const Elf_Shdr &sec = objSections[i];
|
const Elf_Shdr &sec = objSections[i];
|
||||||
@ -652,25 +585,29 @@ void ObjFile<ELFT>::initializeSections(bool ignoreComdats) {
|
|||||||
default:
|
default:
|
||||||
this->sections[i] = createInputSection(sec);
|
this->sections[i] = createInputSection(sec);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0, e = objSections.size(); i < e; ++i) {
|
||||||
|
if (this->sections[i] == &InputSection::discarded)
|
||||||
|
continue;
|
||||||
|
const Elf_Shdr &sec = objSections[i];
|
||||||
|
if (!(sec.sh_flags & SHF_LINK_ORDER))
|
||||||
|
continue;
|
||||||
|
|
||||||
// .ARM.exidx sections have a reverse dependency on the InputSection they
|
// .ARM.exidx sections have a reverse dependency on the InputSection they
|
||||||
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
|
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
|
||||||
if (sec.sh_flags & SHF_LINK_ORDER) {
|
InputSectionBase *linkSec = nullptr;
|
||||||
InputSectionBase *linkSec = nullptr;
|
if (sec.sh_link < this->sections.size())
|
||||||
if (sec.sh_link < this->sections.size())
|
linkSec = this->sections[sec.sh_link];
|
||||||
linkSec = this->sections[sec.sh_link];
|
if (!linkSec)
|
||||||
if (!linkSec)
|
fatal(toString(this) + ": invalid sh_link index: " + Twine(sec.sh_link));
|
||||||
fatal(toString(this) +
|
|
||||||
": invalid sh_link index: " + Twine(sec.sh_link));
|
|
||||||
|
|
||||||
InputSection *isec = cast<InputSection>(this->sections[i]);
|
InputSection *isec = cast<InputSection>(this->sections[i]);
|
||||||
linkSec->dependentSections.push_back(isec);
|
linkSec->dependentSections.push_back(isec);
|
||||||
if (!isa<InputSection>(linkSec))
|
if (!isa<InputSection>(linkSec))
|
||||||
error("a section " + isec->name +
|
error("a section " + isec->name +
|
||||||
" with SHF_LINK_ORDER should not refer a non-regular "
|
" with SHF_LINK_ORDER should not refer a non-regular section: " +
|
||||||
"section: " +
|
toString(linkSec));
|
||||||
toString(linkSec));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1030,7 +967,7 @@ InputSectionBase *ObjFile<ELFT>::createInputSection(const Elf_Shdr &sec) {
|
|||||||
if (name == ".eh_frame" && !config->relocatable)
|
if (name == ".eh_frame" && !config->relocatable)
|
||||||
return make<EhInputSection>(*this, sec, name);
|
return make<EhInputSection>(*this, sec, name);
|
||||||
|
|
||||||
if (shouldMerge(sec))
|
if (shouldMerge(sec, name))
|
||||||
return make<MergeInputSection>(*this, sec, name);
|
return make<MergeInputSection>(*this, sec, name);
|
||||||
return make<InputSection>(*this, sec, name);
|
return make<InputSection>(*this, sec, name);
|
||||||
}
|
}
|
||||||
@ -1094,6 +1031,7 @@ template <class ELFT> void ObjFile<ELFT>::initializeSymbols() {
|
|||||||
// Handle global undefined symbols.
|
// Handle global undefined symbols.
|
||||||
if (eSym.st_shndx == SHN_UNDEF) {
|
if (eSym.st_shndx == SHN_UNDEF) {
|
||||||
this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
|
this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
|
||||||
|
this->symbols[i]->referenced = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1470,10 +1408,12 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
|
|||||||
|
|
||||||
int c = objSym.getComdatIndex();
|
int c = objSym.getComdatIndex();
|
||||||
if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) {
|
if (objSym.isUndefined() || (c != -1 && !keptComdats[c])) {
|
||||||
Undefined New(&f, name, binding, visibility, type);
|
Undefined newSym(&f, name, binding, visibility, type);
|
||||||
if (canOmitFromDynSym)
|
if (canOmitFromDynSym)
|
||||||
New.exportDynamic = false;
|
newSym.exportDynamic = false;
|
||||||
return symtab->addSymbol(New);
|
Symbol *ret = symtab->addSymbol(newSym);
|
||||||
|
ret->referenced = true;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (objSym.isCommon())
|
if (objSym.isCommon())
|
||||||
@ -1481,10 +1421,10 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &keptComdats,
|
|||||||
CommonSymbol{&f, name, binding, visibility, STT_OBJECT,
|
CommonSymbol{&f, name, binding, visibility, STT_OBJECT,
|
||||||
objSym.getCommonAlignment(), objSym.getCommonSize()});
|
objSym.getCommonAlignment(), objSym.getCommonSize()});
|
||||||
|
|
||||||
Defined New(&f, name, binding, visibility, type, 0, 0, nullptr);
|
Defined newSym(&f, name, binding, visibility, type, 0, 0, nullptr);
|
||||||
if (canOmitFromDynSym)
|
if (canOmitFromDynSym)
|
||||||
New.exportDynamic = false;
|
newSym.exportDynamic = false;
|
||||||
return symtab->addSymbol(New);
|
return symtab->addSymbol(newSym);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void BitcodeFile::parse() {
|
template <class ELFT> void BitcodeFile::parse() {
|
||||||
@ -1523,8 +1463,8 @@ void BinaryFile::parse() {
|
|||||||
STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
|
STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
|
||||||
}
|
}
|
||||||
|
|
||||||
InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName,
|
InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName,
|
||||||
uint64_t offsetInArchive) {
|
uint64_t offsetInArchive) {
|
||||||
if (isBitcode(mb))
|
if (isBitcode(mb))
|
||||||
return make<BitcodeFile>(mb, archiveName, offsetInArchive);
|
return make<BitcodeFile>(mb, archiveName, offsetInArchive);
|
||||||
|
|
||||||
@ -1615,7 +1555,7 @@ template <class ELFT> void LazyObjFile::parse() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string elf::replaceThinLTOSuffix(StringRef path) {
|
std::string replaceThinLTOSuffix(StringRef path) {
|
||||||
StringRef suffix = config->thinLTOObjectSuffixReplace.first;
|
StringRef suffix = config->thinLTOObjectSuffixReplace.first;
|
||||||
StringRef repl = config->thinLTOObjectSuffixReplace.second;
|
StringRef repl = config->thinLTOObjectSuffixReplace.second;
|
||||||
|
|
||||||
@ -1634,12 +1574,15 @@ template void LazyObjFile::parse<ELF32BE>();
|
|||||||
template void LazyObjFile::parse<ELF64LE>();
|
template void LazyObjFile::parse<ELF64LE>();
|
||||||
template void LazyObjFile::parse<ELF64BE>();
|
template void LazyObjFile::parse<ELF64BE>();
|
||||||
|
|
||||||
template class elf::ObjFile<ELF32LE>;
|
template class ObjFile<ELF32LE>;
|
||||||
template class elf::ObjFile<ELF32BE>;
|
template class ObjFile<ELF32BE>;
|
||||||
template class elf::ObjFile<ELF64LE>;
|
template class ObjFile<ELF64LE>;
|
||||||
template class elf::ObjFile<ELF64BE>;
|
template class ObjFile<ELF64BE>;
|
||||||
|
|
||||||
template void SharedFile::parse<ELF32LE>();
|
template void SharedFile::parse<ELF32LE>();
|
||||||
template void SharedFile::parse<ELF32BE>();
|
template void SharedFile::parse<ELF32BE>();
|
||||||
template void SharedFile::parse<ELF64LE>();
|
template void SharedFile::parse<ELF64LE>();
|
||||||
template void SharedFile::parse<ELF64BE>();
|
template void SharedFile::parse<ELF64BE>();
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
#define LLD_ELF_INPUT_FILES_H
|
#define LLD_ELF_INPUT_FILES_H
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
#include "lld/Common/DWARF.h"
|
||||||
#include "lld/Common/ErrorHandler.h"
|
#include "lld/Common/ErrorHandler.h"
|
||||||
#include "lld/Common/LLVM.h"
|
#include "lld/Common/LLVM.h"
|
||||||
#include "lld/Common/Reproduce.h"
|
#include "lld/Common/Reproduce.h"
|
||||||
#include "llvm/ADT/CachedHashString.h"
|
#include "llvm/ADT/CachedHashString.h"
|
||||||
#include "llvm/ADT/DenseSet.h"
|
#include "llvm/ADT/DenseSet.h"
|
||||||
#include "llvm/ADT/STLExtras.h"
|
#include "llvm/ADT/STLExtras.h"
|
||||||
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
|
|
||||||
#include "llvm/IR/Comdat.h"
|
#include "llvm/IR/Comdat.h"
|
||||||
#include "llvm/Object/Archive.h"
|
#include "llvm/Object/Archive.h"
|
||||||
#include "llvm/Object/ELF.h"
|
#include "llvm/Object/ELF.h"
|
||||||
@ -26,22 +26,19 @@
|
|||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
class TarWriter;
|
class TarWriter;
|
||||||
struct DILineInfo;
|
|
||||||
namespace lto {
|
namespace lto {
|
||||||
class InputFile;
|
class InputFile;
|
||||||
}
|
}
|
||||||
} // namespace llvm
|
} // namespace llvm
|
||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
namespace elf {
|
|
||||||
class InputFile;
|
|
||||||
class InputSectionBase;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
|
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
|
||||||
std::string toString(const elf::InputFile *f);
|
std::string toString(const elf::InputFile *f);
|
||||||
|
|
||||||
namespace elf {
|
namespace elf {
|
||||||
|
class InputFile;
|
||||||
|
class InputSectionBase;
|
||||||
|
|
||||||
using llvm::object::Archive;
|
using llvm::object::Archive;
|
||||||
|
|
||||||
@ -261,7 +258,7 @@ template <class ELFT> class ObjFile : public ELFFileBase {
|
|||||||
InputSectionBase *createInputSection(const Elf_Shdr &sec);
|
InputSectionBase *createInputSection(const Elf_Shdr &sec);
|
||||||
StringRef getSectionName(const Elf_Shdr &sec);
|
StringRef getSectionName(const Elf_Shdr &sec);
|
||||||
|
|
||||||
bool shouldMerge(const Elf_Shdr &sec);
|
bool shouldMerge(const Elf_Shdr &sec, StringRef name);
|
||||||
|
|
||||||
// Each ELF symbol contains a section index which the symbol belongs to.
|
// Each ELF symbol contains a section index which the symbol belongs to.
|
||||||
// However, because the number of bits dedicated for that is limited, a
|
// However, because the number of bits dedicated for that is limited, a
|
||||||
@ -284,14 +281,7 @@ template <class ELFT> class ObjFile : public ELFFileBase {
|
|||||||
// reporting. Linker may find reasonable number of errors in a
|
// reporting. Linker may find reasonable number of errors in a
|
||||||
// single object file, so we cache debugging information in order to
|
// single object file, so we cache debugging information in order to
|
||||||
// parse it only once for each object file we link.
|
// parse it only once for each object file we link.
|
||||||
std::unique_ptr<llvm::DWARFContext> dwarf;
|
DWARFCache *dwarf;
|
||||||
std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
|
|
||||||
struct VarLoc {
|
|
||||||
const llvm::DWARFDebugLine::LineTable *lt;
|
|
||||||
unsigned file;
|
|
||||||
unsigned line;
|
|
||||||
};
|
|
||||||
llvm::DenseMap<StringRef, VarLoc> variableLoc;
|
|
||||||
llvm::once_flag initDwarfLine;
|
llvm::once_flag initDwarfLine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -37,16 +37,15 @@ using namespace llvm::support;
|
|||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::sys;
|
using namespace llvm::sys;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
std::vector<InputSectionBase *> elf::inputSections;
|
|
||||||
|
|
||||||
// Returns a string to construct an error message.
|
// Returns a string to construct an error message.
|
||||||
std::string lld::toString(const InputSectionBase *sec) {
|
std::string toString(const elf::InputSectionBase *sec) {
|
||||||
return (toString(sec->file) + ":(" + sec->name + ")").str();
|
return (toString(sec->file) + ":(" + sec->name + ")").str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace elf {
|
||||||
|
std::vector<InputSectionBase *> inputSections;
|
||||||
|
|
||||||
template <class ELFT>
|
template <class ELFT>
|
||||||
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
|
static ArrayRef<uint8_t> getSectionContents(ObjFile<ELFT> &file,
|
||||||
const typename ELFT::Shdr &hdr) {
|
const typename ELFT::Shdr &hdr) {
|
||||||
@ -608,26 +607,39 @@ static int64_t getTlsTpOffset(const Symbol &s) {
|
|||||||
if (&s == ElfSym::tlsModuleBase)
|
if (&s == ElfSym::tlsModuleBase)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
// There are 2 TLS layouts. Among targets we support, x86 uses TLS Variant 2
|
||||||
|
// while most others use Variant 1. At run time TP will be aligned to p_align.
|
||||||
|
|
||||||
|
// Variant 1. TP will be followed by an optional gap (which is the size of 2
|
||||||
|
// pointers on ARM/AArch64, 0 on other targets), followed by alignment
|
||||||
|
// padding, then the static TLS blocks. The alignment padding is added so that
|
||||||
|
// (TP + gap + padding) is congruent to p_vaddr modulo p_align.
|
||||||
|
//
|
||||||
|
// Variant 2. Static TLS blocks, followed by alignment padding are placed
|
||||||
|
// before TP. The alignment padding is added so that (TP - padding -
|
||||||
|
// p_memsz) is congruent to p_vaddr modulo p_align.
|
||||||
|
PhdrEntry *tls = Out::tlsPhdr;
|
||||||
switch (config->emachine) {
|
switch (config->emachine) {
|
||||||
|
// Variant 1.
|
||||||
case EM_ARM:
|
case EM_ARM:
|
||||||
case EM_AARCH64:
|
case EM_AARCH64:
|
||||||
// Variant 1. The thread pointer points to a TCB with a fixed 2-word size,
|
return s.getVA(0) + config->wordsize * 2 +
|
||||||
// followed by a variable amount of alignment padding, followed by the TLS
|
((tls->p_vaddr - config->wordsize * 2) & (tls->p_align - 1));
|
||||||
// segment.
|
case EM_MIPS:
|
||||||
return s.getVA(0) + alignTo(config->wordsize * 2, Out::tlsPhdr->p_align);
|
|
||||||
case EM_386:
|
|
||||||
case EM_X86_64:
|
|
||||||
// Variant 2. The TLS segment is located just before the thread pointer.
|
|
||||||
return s.getVA(0) - alignTo(Out::tlsPhdr->p_memsz, Out::tlsPhdr->p_align);
|
|
||||||
case EM_PPC:
|
case EM_PPC:
|
||||||
case EM_PPC64:
|
case EM_PPC64:
|
||||||
// The thread pointer points to a fixed offset from the start of the
|
// Adjusted Variant 1. TP is placed with a displacement of 0x7000, which is
|
||||||
// executable's TLS segment. An offset of 0x7000 allows a signed 16-bit
|
// to allow a signed 16-bit offset to reach 0x1000 of TCB/thread-library
|
||||||
// offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the
|
// data and 0xf000 of the program's TLS segment.
|
||||||
// program's TLS segment.
|
return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1)) - 0x7000;
|
||||||
return s.getVA(0) - 0x7000;
|
|
||||||
case EM_RISCV:
|
case EM_RISCV:
|
||||||
return s.getVA(0);
|
return s.getVA(0) + (tls->p_vaddr & (tls->p_align - 1));
|
||||||
|
|
||||||
|
// Variant 2.
|
||||||
|
case EM_386:
|
||||||
|
case EM_X86_64:
|
||||||
|
return s.getVA(0) - tls->p_memsz -
|
||||||
|
((-tls->p_vaddr - tls->p_memsz) & (tls->p_align - 1));
|
||||||
default:
|
default:
|
||||||
llvm_unreachable("unhandled Config->EMachine");
|
llvm_unreachable("unhandled Config->EMachine");
|
||||||
}
|
}
|
||||||
@ -671,8 +683,6 @@ static uint64_t getRelocTargetVA(const InputFile *file, RelType type, int64_t a,
|
|||||||
case R_GOT_PC:
|
case R_GOT_PC:
|
||||||
case R_RELAX_TLS_GD_TO_IE:
|
case R_RELAX_TLS_GD_TO_IE:
|
||||||
return sym.getGotVA() + a - p;
|
return sym.getGotVA() + a - p;
|
||||||
case R_HEXAGON_GOT:
|
|
||||||
return sym.getGotVA() - in.gotPlt->getVA();
|
|
||||||
case R_MIPS_GOTREL:
|
case R_MIPS_GOTREL:
|
||||||
return sym.getVA(a) - in.mipsGot->getGp(file);
|
return sym.getVA(a) - in.mipsGot->getGp(file);
|
||||||
case R_MIPS_GOT_GP:
|
case R_MIPS_GOT_GP:
|
||||||
@ -1071,7 +1081,7 @@ void InputSectionBase::adjustSplitStackFunctionPrologues(uint8_t *buf,
|
|||||||
end, f->stOther))
|
end, f->stOther))
|
||||||
continue;
|
continue;
|
||||||
if (!getFile<ELFT>()->someNoSplitStack)
|
if (!getFile<ELFT>()->someNoSplitStack)
|
||||||
error(lld::toString(this) + ": " + f->getName() +
|
error(toString(this) + ": " + f->getName() +
|
||||||
" (with -fsplit-stack) calls " + rel.sym->getName() +
|
" (with -fsplit-stack) calls " + rel.sym->getName() +
|
||||||
" (without -fsplit-stack), but couldn't adjust its prologue");
|
" (without -fsplit-stack), but couldn't adjust its prologue");
|
||||||
}
|
}
|
||||||
@ -1334,3 +1344,6 @@ template void EhInputSection::split<ELF32LE>();
|
|||||||
template void EhInputSection::split<ELF32BE>();
|
template void EhInputSection::split<ELF32BE>();
|
||||||
template void EhInputSection::split<ELF64LE>();
|
template void EhInputSection::split<ELF64LE>();
|
||||||
template void EhInputSection::split<ELF64BE>();
|
template void EhInputSection::split<ELF64BE>();
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -54,22 +54,9 @@ class SectionBase {
|
|||||||
|
|
||||||
unsigned sectionKind : 3;
|
unsigned sectionKind : 3;
|
||||||
|
|
||||||
// The next three bit fields are only used by InputSectionBase, but we
|
// The next two bit fields are only used by InputSectionBase, but we
|
||||||
// put them here so the struct packs better.
|
// put them here so the struct packs better.
|
||||||
|
|
||||||
// True if this section has already been placed to a linker script
|
|
||||||
// output section. This is needed because, in a linker script, you
|
|
||||||
// can refer to the same section more than once. For example, in
|
|
||||||
// the following linker script,
|
|
||||||
//
|
|
||||||
// .foo : { *(.text) }
|
|
||||||
// .bar : { *(.text) }
|
|
||||||
//
|
|
||||||
// .foo takes all .text sections, and .bar becomes empty. To achieve
|
|
||||||
// this, we need to memorize whether a section has been placed or
|
|
||||||
// not for each input section.
|
|
||||||
unsigned assigned : 1;
|
|
||||||
|
|
||||||
unsigned bss : 1;
|
unsigned bss : 1;
|
||||||
|
|
||||||
// Set for sections that should not be folded by ICF.
|
// Set for sections that should not be folded by ICF.
|
||||||
@ -108,9 +95,9 @@ class SectionBase {
|
|||||||
SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
|
SectionBase(Kind sectionKind, StringRef name, uint64_t flags,
|
||||||
uint64_t entsize, uint64_t alignment, uint32_t type,
|
uint64_t entsize, uint64_t alignment, uint32_t type,
|
||||||
uint32_t info, uint32_t link)
|
uint32_t info, uint32_t link)
|
||||||
: name(name), repl(this), sectionKind(sectionKind), assigned(false),
|
: name(name), repl(this), sectionKind(sectionKind), bss(false),
|
||||||
bss(false), keepUnique(false), partition(0), alignment(alignment),
|
keepUnique(false), partition(0), alignment(alignment), flags(flags),
|
||||||
flags(flags), entsize(entsize), type(type), link(link), info(info) {}
|
entsize(entsize), type(type), link(link), info(info) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This corresponds to a section of an input file.
|
// This corresponds to a section of an input file.
|
||||||
|
@ -42,15 +42,15 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
// Creates an empty file to store a list of object files for final
|
// Creates an empty file to store a list of object files for final
|
||||||
// linking of distributed ThinLTO.
|
// linking of distributed ThinLTO.
|
||||||
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
|
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
auto ret =
|
auto ret =
|
||||||
llvm::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::F_None);
|
std::make_unique<raw_fd_ostream>(file, ec, sys::fs::OpenFlags::OF_None);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
error("cannot open " + file + ": " + ec.message());
|
error("cannot open " + file + ": " + ec.message());
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -76,7 +76,9 @@ static lto::Config createConfig() {
|
|||||||
c.Options.FunctionSections = true;
|
c.Options.FunctionSections = true;
|
||||||
c.Options.DataSections = true;
|
c.Options.DataSections = true;
|
||||||
|
|
||||||
if (config->relocatable)
|
if (auto relocModel = getRelocModelFromCMModel())
|
||||||
|
c.RelocModel = *relocModel;
|
||||||
|
else if (config->relocatable)
|
||||||
c.RelocModel = None;
|
c.RelocModel = None;
|
||||||
else if (config->isPic)
|
else if (config->isPic)
|
||||||
c.RelocModel = Reloc::PIC_;
|
c.RelocModel = Reloc::PIC_;
|
||||||
@ -139,7 +141,7 @@ BitcodeCompiler::BitcodeCompiler() {
|
|||||||
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
|
backend = lto::createInProcessThinBackend(config->thinLTOJobs);
|
||||||
}
|
}
|
||||||
|
|
||||||
ltoObj = llvm::make_unique<lto::LTO>(createConfig(), backend,
|
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
|
||||||
config->ltoPartitions);
|
config->ltoPartitions);
|
||||||
|
|
||||||
// Initialize usedStartStop.
|
// Initialize usedStartStop.
|
||||||
@ -249,8 +251,8 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
|||||||
if (!bitcodeFiles.empty())
|
if (!bitcodeFiles.empty())
|
||||||
checkError(ltoObj->run(
|
checkError(ltoObj->run(
|
||||||
[&](size_t task) {
|
[&](size_t task) {
|
||||||
return llvm::make_unique<lto::NativeObjectStream>(
|
return std::make_unique<lto::NativeObjectStream>(
|
||||||
llvm::make_unique<raw_svector_ostream>(buf[task]));
|
std::make_unique<raw_svector_ostream>(buf[task]));
|
||||||
},
|
},
|
||||||
cache));
|
cache));
|
||||||
|
|
||||||
@ -301,3 +303,6 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
|||||||
ret.push_back(createObjectFile(*file));
|
ret.push_back(createObjectFile(*file));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -43,29 +43,27 @@ using namespace llvm;
|
|||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
LinkerScript *elf::script;
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
|
LinkerScript *script;
|
||||||
|
|
||||||
static uint64_t getOutputSectionVA(SectionBase *inputSec, StringRef loc) {
|
static uint64_t getOutputSectionVA(SectionBase *sec) {
|
||||||
if (OutputSection *os = inputSec->getOutputSection())
|
OutputSection *os = sec->getOutputSection();
|
||||||
return os->addr;
|
assert(os && "input section has no output section assigned");
|
||||||
error(loc + ": unable to evaluate expression: input section " +
|
return os ? os->addr : 0;
|
||||||
inputSec->name + " has no output section assigned");
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ExprValue::getValue() const {
|
uint64_t ExprValue::getValue() const {
|
||||||
if (sec)
|
if (sec)
|
||||||
return alignTo(sec->getOffset(val) + getOutputSectionVA(sec, loc),
|
return alignTo(sec->getOffset(val) + getOutputSectionVA(sec),
|
||||||
alignment);
|
alignment);
|
||||||
return alignTo(val, alignment);
|
return alignTo(val, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ExprValue::getSecAddr() const {
|
uint64_t ExprValue::getSecAddr() const {
|
||||||
if (sec)
|
if (sec)
|
||||||
return sec->getOffset(0) + getOutputSectionVA(sec, loc);
|
return sec->getOffset(0) + getOutputSectionVA(sec);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +71,7 @@ uint64_t ExprValue::getSectionOffset() const {
|
|||||||
// If the alignment is trivial, we don't have to compute the full
|
// If the alignment is trivial, we don't have to compute the full
|
||||||
// value to know the offset. This allows this function to succeed in
|
// value to know the offset. This allows this function to succeed in
|
||||||
// cases where the output section is not yet known.
|
// cases where the output section is not yet known.
|
||||||
if (alignment == 1 && (!sec || !sec->getOutputSection()))
|
if (alignment == 1 && !sec)
|
||||||
return val;
|
return val;
|
||||||
return getValue() - getSecAddr();
|
return getValue() - getSecAddr();
|
||||||
}
|
}
|
||||||
@ -157,8 +155,8 @@ static bool shouldDefineSym(SymbolAssignment *cmd) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is called from processSectionCommands,
|
// Called by processSymbolAssignments() to assign definitions to
|
||||||
// while we are fixing the output section layout.
|
// linker-script-defined symbols.
|
||||||
void LinkerScript::addSymbol(SymbolAssignment *cmd) {
|
void LinkerScript::addSymbol(SymbolAssignment *cmd) {
|
||||||
if (!shouldDefineSym(cmd))
|
if (!shouldDefineSym(cmd))
|
||||||
return;
|
return;
|
||||||
@ -181,12 +179,12 @@ void LinkerScript::addSymbol(SymbolAssignment *cmd) {
|
|||||||
// write expressions like this: `alignment = 16; . = ALIGN(., alignment)`.
|
// write expressions like this: `alignment = 16; . = ALIGN(., alignment)`.
|
||||||
uint64_t symValue = value.sec ? 0 : value.getValue();
|
uint64_t symValue = value.sec ? 0 : value.getValue();
|
||||||
|
|
||||||
Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, symValue,
|
Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE,
|
||||||
0, sec);
|
symValue, 0, sec);
|
||||||
|
|
||||||
Symbol *sym = symtab->insert(cmd->name);
|
Symbol *sym = symtab->insert(cmd->name);
|
||||||
sym->mergeProperties(New);
|
sym->mergeProperties(newSym);
|
||||||
sym->replace(New);
|
sym->replace(newSym);
|
||||||
cmd->sym = cast<Defined>(sym);
|
cmd->sym = cast<Defined>(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,19 +195,57 @@ static void declareSymbol(SymbolAssignment *cmd) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT;
|
uint8_t visibility = cmd->hidden ? STV_HIDDEN : STV_DEFAULT;
|
||||||
Defined New(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0,
|
Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE, 0, 0,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
// We can't calculate final value right now.
|
// We can't calculate final value right now.
|
||||||
Symbol *sym = symtab->insert(cmd->name);
|
Symbol *sym = symtab->insert(cmd->name);
|
||||||
sym->mergeProperties(New);
|
sym->mergeProperties(newSym);
|
||||||
sym->replace(New);
|
sym->replace(newSym);
|
||||||
|
|
||||||
cmd->sym = cast<Defined>(sym);
|
cmd->sym = cast<Defined>(sym);
|
||||||
cmd->provide = false;
|
cmd->provide = false;
|
||||||
sym->scriptDefined = true;
|
sym->scriptDefined = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using SymbolAssignmentMap =
|
||||||
|
DenseMap<const Defined *, std::pair<SectionBase *, uint64_t>>;
|
||||||
|
|
||||||
|
// Collect section/value pairs of linker-script-defined symbols. This is used to
|
||||||
|
// check whether symbol values converge.
|
||||||
|
static SymbolAssignmentMap
|
||||||
|
getSymbolAssignmentValues(const std::vector<BaseCommand *> §ionCommands) {
|
||||||
|
SymbolAssignmentMap ret;
|
||||||
|
for (BaseCommand *base : sectionCommands) {
|
||||||
|
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
|
||||||
|
if (cmd->sym) // sym is nullptr for dot.
|
||||||
|
ret.try_emplace(cmd->sym,
|
||||||
|
std::make_pair(cmd->sym->section, cmd->sym->value));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (BaseCommand *sub_base : cast<OutputSection>(base)->sectionCommands)
|
||||||
|
if (auto *cmd = dyn_cast<SymbolAssignment>(sub_base))
|
||||||
|
if (cmd->sym)
|
||||||
|
ret.try_emplace(cmd->sym,
|
||||||
|
std::make_pair(cmd->sym->section, cmd->sym->value));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the lexicographical smallest (for determinism) Defined whose
|
||||||
|
// section/value has changed.
|
||||||
|
static const Defined *
|
||||||
|
getChangedSymbolAssignment(const SymbolAssignmentMap &oldValues) {
|
||||||
|
const Defined *changed = nullptr;
|
||||||
|
for (auto &it : oldValues) {
|
||||||
|
const Defined *sym = it.first;
|
||||||
|
if (std::make_pair(sym->section, sym->value) != it.second &&
|
||||||
|
(!changed || sym->getName() < changed->getName()))
|
||||||
|
changed = sym;
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
// This method is used to handle INSERT AFTER statement. Here we rebuild
|
// This method is used to handle INSERT AFTER statement. Here we rebuild
|
||||||
// the list of script commands to mix sections inserted into.
|
// the list of script commands to mix sections inserted into.
|
||||||
void LinkerScript::processInsertCommands() {
|
void LinkerScript::processInsertCommands() {
|
||||||
@ -305,46 +341,44 @@ bool LinkerScript::shouldKeep(InputSectionBase *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A helper function for the SORT() command.
|
// A helper function for the SORT() command.
|
||||||
static std::function<bool(InputSectionBase *, InputSectionBase *)>
|
static bool matchConstraints(ArrayRef<InputSectionBase *> sections,
|
||||||
getComparator(SortSectionPolicy k) {
|
|
||||||
switch (k) {
|
|
||||||
case SortSectionPolicy::Alignment:
|
|
||||||
return [](InputSectionBase *a, InputSectionBase *b) {
|
|
||||||
// ">" is not a mistake. Sections with larger alignments are placed
|
|
||||||
// before sections with smaller alignments in order to reduce the
|
|
||||||
// amount of padding necessary. This is compatible with GNU.
|
|
||||||
return a->alignment > b->alignment;
|
|
||||||
};
|
|
||||||
case SortSectionPolicy::Name:
|
|
||||||
return [](InputSectionBase *a, InputSectionBase *b) {
|
|
||||||
return a->name < b->name;
|
|
||||||
};
|
|
||||||
case SortSectionPolicy::Priority:
|
|
||||||
return [](InputSectionBase *a, InputSectionBase *b) {
|
|
||||||
return getPriority(a->name) < getPriority(b->name);
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
llvm_unreachable("unknown sort policy");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A helper function for the SORT() command.
|
|
||||||
static bool matchConstraints(ArrayRef<InputSection *> sections,
|
|
||||||
ConstraintKind kind) {
|
ConstraintKind kind) {
|
||||||
if (kind == ConstraintKind::NoConstraint)
|
if (kind == ConstraintKind::NoConstraint)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
bool isRW = llvm::any_of(
|
bool isRW = llvm::any_of(
|
||||||
sections, [](InputSection *sec) { return sec->flags & SHF_WRITE; });
|
sections, [](InputSectionBase *sec) { return sec->flags & SHF_WRITE; });
|
||||||
|
|
||||||
return (isRW && kind == ConstraintKind::ReadWrite) ||
|
return (isRW && kind == ConstraintKind::ReadWrite) ||
|
||||||
(!isRW && kind == ConstraintKind::ReadOnly);
|
(!isRW && kind == ConstraintKind::ReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sortSections(MutableArrayRef<InputSection *> vec,
|
static void sortSections(MutableArrayRef<InputSectionBase *> vec,
|
||||||
SortSectionPolicy k) {
|
SortSectionPolicy k) {
|
||||||
if (k != SortSectionPolicy::Default && k != SortSectionPolicy::None)
|
auto alignmentComparator = [](InputSectionBase *a, InputSectionBase *b) {
|
||||||
llvm::stable_sort(vec, getComparator(k));
|
// ">" is not a mistake. Sections with larger alignments are placed
|
||||||
|
// before sections with smaller alignments in order to reduce the
|
||||||
|
// amount of padding necessary. This is compatible with GNU.
|
||||||
|
return a->alignment > b->alignment;
|
||||||
|
};
|
||||||
|
auto nameComparator = [](InputSectionBase *a, InputSectionBase *b) {
|
||||||
|
return a->name < b->name;
|
||||||
|
};
|
||||||
|
auto priorityComparator = [](InputSectionBase *a, InputSectionBase *b) {
|
||||||
|
return getPriority(a->name) < getPriority(b->name);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (k) {
|
||||||
|
case SortSectionPolicy::Default:
|
||||||
|
case SortSectionPolicy::None:
|
||||||
|
return;
|
||||||
|
case SortSectionPolicy::Alignment:
|
||||||
|
return llvm::stable_sort(vec, alignmentComparator);
|
||||||
|
case SortSectionPolicy::Name:
|
||||||
|
return llvm::stable_sort(vec, nameComparator);
|
||||||
|
case SortSectionPolicy::Priority:
|
||||||
|
return llvm::stable_sort(vec, priorityComparator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort sections as instructed by SORT-family commands and --sort-section
|
// Sort sections as instructed by SORT-family commands and --sort-section
|
||||||
@ -358,7 +392,7 @@ static void sortSections(MutableArrayRef<InputSection *> vec,
|
|||||||
// --sort-section is handled as an inner SORT command.
|
// --sort-section is handled as an inner SORT command.
|
||||||
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
|
// 3. If one SORT command is given, and if it is SORT_NONE, don't sort.
|
||||||
// 4. If no SORT command is given, sort according to --sort-section.
|
// 4. If no SORT command is given, sort according to --sort-section.
|
||||||
static void sortInputSections(MutableArrayRef<InputSection *> vec,
|
static void sortInputSections(MutableArrayRef<InputSectionBase *> vec,
|
||||||
const SectionPattern &pat) {
|
const SectionPattern &pat) {
|
||||||
if (pat.sortOuter == SortSectionPolicy::None)
|
if (pat.sortOuter == SortSectionPolicy::None)
|
||||||
return;
|
return;
|
||||||
@ -371,16 +405,16 @@ static void sortInputSections(MutableArrayRef<InputSection *> vec,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compute and remember which sections the InputSectionDescription matches.
|
// Compute and remember which sections the InputSectionDescription matches.
|
||||||
std::vector<InputSection *>
|
std::vector<InputSectionBase *>
|
||||||
LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
|
LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
|
||||||
std::vector<InputSection *> ret;
|
std::vector<InputSectionBase *> ret;
|
||||||
|
|
||||||
// Collects all sections that satisfy constraints of Cmd.
|
// Collects all sections that satisfy constraints of Cmd.
|
||||||
for (const SectionPattern &pat : cmd->sectionPatterns) {
|
for (const SectionPattern &pat : cmd->sectionPatterns) {
|
||||||
size_t sizeBefore = ret.size();
|
size_t sizeBefore = ret.size();
|
||||||
|
|
||||||
for (InputSectionBase *sec : inputSections) {
|
for (InputSectionBase *sec : inputSections) {
|
||||||
if (!sec->isLive() || sec->assigned)
|
if (!sec->isLive() || sec->parent)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// For -emit-relocs we have to ignore entries like
|
// For -emit-relocs we have to ignore entries like
|
||||||
@ -388,9 +422,9 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
|
|||||||
// which are common because they are in the default bfd script.
|
// which are common because they are in the default bfd script.
|
||||||
// We do not ignore SHT_REL[A] linker-synthesized sections here because
|
// We do not ignore SHT_REL[A] linker-synthesized sections here because
|
||||||
// want to support scripts that do custom layout for them.
|
// want to support scripts that do custom layout for them.
|
||||||
if (auto *isec = dyn_cast<InputSection>(sec))
|
if (isa<InputSection>(sec) &&
|
||||||
if (isec->getRelocatedSection())
|
cast<InputSection>(sec)->getRelocatedSection())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string filename = getFilename(sec->file);
|
std::string filename = getFilename(sec->file);
|
||||||
if (!cmd->filePat.match(filename) ||
|
if (!cmd->filePat.match(filename) ||
|
||||||
@ -398,88 +432,60 @@ LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
|
|||||||
!pat.sectionPat.match(sec->name))
|
!pat.sectionPat.match(sec->name))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// It is safe to assume that Sec is an InputSection
|
ret.push_back(sec);
|
||||||
// because mergeable or EH input sections have already been
|
|
||||||
// handled and eliminated.
|
|
||||||
ret.push_back(cast<InputSection>(sec));
|
|
||||||
sec->assigned = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sortInputSections(MutableArrayRef<InputSection *>(ret).slice(sizeBefore),
|
sortInputSections(
|
||||||
pat);
|
MutableArrayRef<InputSectionBase *>(ret).slice(sizeBefore), pat);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LinkerScript::discard(ArrayRef<InputSection *> v) {
|
void LinkerScript::discard(InputSectionBase *s) {
|
||||||
for (InputSection *s : v) {
|
if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
|
||||||
if (s == in.shStrTab || s == mainPart->relaDyn || s == mainPart->relrDyn)
|
error("discarding " + s->name + " section is not allowed");
|
||||||
error("discarding " + s->name + " section is not allowed");
|
|
||||||
|
|
||||||
// You can discard .hash and .gnu.hash sections by linker scripts. Since
|
// You can discard .hash and .gnu.hash sections by linker scripts. Since
|
||||||
// they are synthesized sections, we need to handle them differently than
|
// they are synthesized sections, we need to handle them differently than
|
||||||
// other regular sections.
|
// other regular sections.
|
||||||
if (s == mainPart->gnuHashTab)
|
if (s == mainPart->gnuHashTab)
|
||||||
mainPart->gnuHashTab = nullptr;
|
mainPart->gnuHashTab = nullptr;
|
||||||
if (s == mainPart->hashTab)
|
if (s == mainPart->hashTab)
|
||||||
mainPart->hashTab = nullptr;
|
mainPart->hashTab = nullptr;
|
||||||
|
|
||||||
s->assigned = false;
|
s->markDead();
|
||||||
s->markDead();
|
s->parent = nullptr;
|
||||||
discard(s->dependentSections);
|
for (InputSection *ds : s->dependentSections)
|
||||||
}
|
discard(ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<InputSection *>
|
std::vector<InputSectionBase *>
|
||||||
LinkerScript::createInputSectionList(OutputSection &outCmd) {
|
LinkerScript::createInputSectionList(OutputSection &outCmd) {
|
||||||
std::vector<InputSection *> ret;
|
std::vector<InputSectionBase *> ret;
|
||||||
|
|
||||||
for (BaseCommand *base : outCmd.sectionCommands) {
|
for (BaseCommand *base : outCmd.sectionCommands) {
|
||||||
if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
|
if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
|
||||||
cmd->sections = computeInputSections(cmd);
|
cmd->sectionBases = computeInputSections(cmd);
|
||||||
ret.insert(ret.end(), cmd->sections.begin(), cmd->sections.end());
|
for (InputSectionBase *s : cmd->sectionBases)
|
||||||
|
s->parent = &outCmd;
|
||||||
|
ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create output sections described by SECTIONS commands.
|
||||||
void LinkerScript::processSectionCommands() {
|
void LinkerScript::processSectionCommands() {
|
||||||
// A symbol can be assigned before any section is mentioned in the linker
|
|
||||||
// script. In an DSO, the symbol values are addresses, so the only important
|
|
||||||
// section values are:
|
|
||||||
// * SHN_UNDEF
|
|
||||||
// * SHN_ABS
|
|
||||||
// * Any value meaning a regular section.
|
|
||||||
// To handle that, create a dummy aether section that fills the void before
|
|
||||||
// the linker scripts switches to another section. It has an index of one
|
|
||||||
// which will map to whatever the first actual section is.
|
|
||||||
aether = make<OutputSection>("", 0, SHF_ALLOC);
|
|
||||||
aether->sectionIndex = 1;
|
|
||||||
|
|
||||||
// Ctx captures the local AddressState and makes it accessible deliberately.
|
|
||||||
// This is needed as there are some cases where we cannot just
|
|
||||||
// thread the current state through to a lambda function created by the
|
|
||||||
// script parser.
|
|
||||||
auto deleter = make_unique<AddressState>();
|
|
||||||
ctx = deleter.get();
|
|
||||||
ctx->outSec = aether;
|
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
// Add input sections to output sections.
|
|
||||||
for (BaseCommand *base : sectionCommands) {
|
for (BaseCommand *base : sectionCommands) {
|
||||||
// Handle symbol assignments outside of any output section.
|
|
||||||
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
|
|
||||||
addSymbol(cmd);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto *sec = dyn_cast<OutputSection>(base)) {
|
if (auto *sec = dyn_cast<OutputSection>(base)) {
|
||||||
std::vector<InputSection *> v = createInputSectionList(*sec);
|
std::vector<InputSectionBase *> v = createInputSectionList(*sec);
|
||||||
|
|
||||||
// The output section name `/DISCARD/' is special.
|
// The output section name `/DISCARD/' is special.
|
||||||
// Any input section assigned to it is discarded.
|
// Any input section assigned to it is discarded.
|
||||||
if (sec->name == "/DISCARD/") {
|
if (sec->name == "/DISCARD/") {
|
||||||
discard(v);
|
for (InputSectionBase *s : v)
|
||||||
|
discard(s);
|
||||||
sec->sectionCommands.clear();
|
sec->sectionCommands.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -493,17 +499,11 @@ void LinkerScript::processSectionCommands() {
|
|||||||
// way to "make it as if it wasn't present" is to make it empty.
|
// way to "make it as if it wasn't present" is to make it empty.
|
||||||
if (!matchConstraints(v, sec->constraint)) {
|
if (!matchConstraints(v, sec->constraint)) {
|
||||||
for (InputSectionBase *s : v)
|
for (InputSectionBase *s : v)
|
||||||
s->assigned = false;
|
s->parent = nullptr;
|
||||||
sec->sectionCommands.clear();
|
sec->sectionCommands.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A directive may contain symbol definitions like this:
|
|
||||||
// ".foo : { ...; bar = .; }". Handle them.
|
|
||||||
for (BaseCommand *base : sec->sectionCommands)
|
|
||||||
if (auto *outCmd = dyn_cast<SymbolAssignment>(base))
|
|
||||||
addSymbol(outCmd);
|
|
||||||
|
|
||||||
// Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
|
// Handle subalign (e.g. ".foo : SUBALIGN(32) { ... }"). If subalign
|
||||||
// is given, input sections are aligned to that value, whether the
|
// is given, input sections are aligned to that value, whether the
|
||||||
// given value is larger or smaller than the original section alignment.
|
// given value is larger or smaller than the original section alignment.
|
||||||
@ -513,17 +513,40 @@ void LinkerScript::processSectionCommands() {
|
|||||||
s->alignment = subalign;
|
s->alignment = subalign;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add input sections to an output section.
|
// Set the partition field the same way OutputSection::recordSection()
|
||||||
for (InputSection *s : v)
|
// does. Partitions cannot be used with the SECTIONS command, so this is
|
||||||
sec->addSection(s);
|
// always 1.
|
||||||
|
sec->partition = 1;
|
||||||
|
|
||||||
sec->sectionIndex = i++;
|
sec->sectionIndex = i++;
|
||||||
if (sec->noload)
|
|
||||||
sec->type = SHT_NOBITS;
|
|
||||||
if (sec->nonAlloc)
|
|
||||||
sec->flags &= ~(uint64_t)SHF_ALLOC;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinkerScript::processSymbolAssignments() {
|
||||||
|
// Dot outside an output section still represents a relative address, whose
|
||||||
|
// sh_shndx should not be SHN_UNDEF or SHN_ABS. Create a dummy aether section
|
||||||
|
// that fills the void outside a section. It has an index of one, which is
|
||||||
|
// indistinguishable from any other regular section index.
|
||||||
|
aether = make<OutputSection>("", 0, SHF_ALLOC);
|
||||||
|
aether->sectionIndex = 1;
|
||||||
|
|
||||||
|
// ctx captures the local AddressState and makes it accessible deliberately.
|
||||||
|
// This is needed as there are some cases where we cannot just thread the
|
||||||
|
// current state through to a lambda function created by the script parser.
|
||||||
|
AddressState state;
|
||||||
|
ctx = &state;
|
||||||
|
ctx->outSec = aether;
|
||||||
|
|
||||||
|
for (BaseCommand *base : sectionCommands) {
|
||||||
|
if (auto *cmd = dyn_cast<SymbolAssignment>(base))
|
||||||
|
addSymbol(cmd);
|
||||||
|
else
|
||||||
|
for (BaseCommand *sub_base : cast<OutputSection>(base)->sectionCommands)
|
||||||
|
if (auto *cmd = dyn_cast<SymbolAssignment>(sub_base))
|
||||||
|
addSymbol(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
ctx = nullptr;
|
ctx = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,7 +562,7 @@ static OutputSection *findByName(ArrayRef<BaseCommand *> vec,
|
|||||||
static OutputSection *createSection(InputSectionBase *isec,
|
static OutputSection *createSection(InputSectionBase *isec,
|
||||||
StringRef outsecName) {
|
StringRef outsecName) {
|
||||||
OutputSection *sec = script->createOutputSection(outsecName, "<internal>");
|
OutputSection *sec = script->createOutputSection(outsecName, "<internal>");
|
||||||
sec->addSection(cast<InputSection>(isec));
|
sec->recordSection(isec);
|
||||||
return sec;
|
return sec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +591,7 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
|
|||||||
OutputSection *out = sec->getRelocatedSection()->getOutputSection();
|
OutputSection *out = sec->getRelocatedSection()->getOutputSection();
|
||||||
|
|
||||||
if (out->relocationSection) {
|
if (out->relocationSection) {
|
||||||
out->relocationSection->addSection(sec);
|
out->relocationSection->recordSection(sec);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,12 +599,6 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
|
|||||||
return out->relocationSection;
|
return out->relocationSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When control reaches here, mergeable sections have already been merged into
|
|
||||||
// synthetic sections. For relocatable case we want to create one output
|
|
||||||
// section per syntetic section so that they have a valid sh_entsize.
|
|
||||||
if (config->relocatable && (isec->flags & SHF_MERGE))
|
|
||||||
return createSection(isec, outsecName);
|
|
||||||
|
|
||||||
// The ELF spec just says
|
// The ELF spec just says
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// In the first phase, input sections that match in name, type and
|
// In the first phase, input sections that match in name, type and
|
||||||
@ -628,7 +645,21 @@ addInputSec(StringMap<TinyPtrVector<OutputSection *>> &map,
|
|||||||
for (OutputSection *sec : v) {
|
for (OutputSection *sec : v) {
|
||||||
if (sec->partition != isec->partition)
|
if (sec->partition != isec->partition)
|
||||||
continue;
|
continue;
|
||||||
sec->addSection(cast<InputSection>(isec));
|
|
||||||
|
if (config->relocatable && (isec->flags & SHF_LINK_ORDER)) {
|
||||||
|
// Merging two SHF_LINK_ORDER sections with different sh_link fields will
|
||||||
|
// change their semantics, so we only merge them in -r links if they will
|
||||||
|
// end up being linked to the same output section. The casts are fine
|
||||||
|
// because everything in the map was created by the orphan placement code.
|
||||||
|
auto *firstIsec = cast<InputSectionBase>(
|
||||||
|
cast<InputSectionDescription>(sec->sectionCommands[0])
|
||||||
|
->sectionBases[0]);
|
||||||
|
if (firstIsec->getLinkOrderDep()->getOutputSection() !=
|
||||||
|
isec->getLinkOrderDep()->getOutputSection())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sec->recordSection(isec);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -642,25 +673,30 @@ void LinkerScript::addOrphanSections() {
|
|||||||
StringMap<TinyPtrVector<OutputSection *>> map;
|
StringMap<TinyPtrVector<OutputSection *>> map;
|
||||||
std::vector<OutputSection *> v;
|
std::vector<OutputSection *> v;
|
||||||
|
|
||||||
auto add = [&](InputSectionBase *s) {
|
std::function<void(InputSectionBase *)> add;
|
||||||
if (!s->isLive() || s->parent)
|
add = [&](InputSectionBase *s) {
|
||||||
return;
|
if (s->isLive() && !s->parent) {
|
||||||
|
StringRef name = getOutputSectionName(s);
|
||||||
|
|
||||||
StringRef name = getOutputSectionName(s);
|
if (config->orphanHandling == OrphanHandlingPolicy::Error)
|
||||||
|
error(toString(s) + " is being placed in '" + name + "'");
|
||||||
|
else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
|
||||||
|
warn(toString(s) + " is being placed in '" + name + "'");
|
||||||
|
|
||||||
if (config->orphanHandling == OrphanHandlingPolicy::Error)
|
if (OutputSection *sec = findByName(sectionCommands, name)) {
|
||||||
error(toString(s) + " is being placed in '" + name + "'");
|
sec->recordSection(s);
|
||||||
else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
|
} else {
|
||||||
warn(toString(s) + " is being placed in '" + name + "'");
|
if (OutputSection *os = addInputSec(map, s, name))
|
||||||
|
v.push_back(os);
|
||||||
if (OutputSection *sec = findByName(sectionCommands, name)) {
|
assert(isa<MergeInputSection>(s) ||
|
||||||
sec->addSection(cast<InputSection>(s));
|
s->getOutputSection()->sectionIndex == UINT32_MAX);
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OutputSection *os = addInputSec(map, s, name))
|
if (config->relocatable)
|
||||||
v.push_back(os);
|
for (InputSectionBase *depSec : s->dependentSections)
|
||||||
assert(s->getOutputSection()->sectionIndex == UINT32_MAX);
|
if (depSec->flags & SHF_LINK_ORDER)
|
||||||
|
add(depSec);
|
||||||
};
|
};
|
||||||
|
|
||||||
// For futher --emit-reloc handling code we need target output section
|
// For futher --emit-reloc handling code we need target output section
|
||||||
@ -668,6 +704,12 @@ void LinkerScript::addOrphanSections() {
|
|||||||
// to create target sections first. We do not want priority handling
|
// to create target sections first. We do not want priority handling
|
||||||
// for synthetic sections because them are special.
|
// for synthetic sections because them are special.
|
||||||
for (InputSectionBase *isec : inputSections) {
|
for (InputSectionBase *isec : inputSections) {
|
||||||
|
// In -r links, SHF_LINK_ORDER sections are added while adding their parent
|
||||||
|
// sections because we need to know the parent's output section before we
|
||||||
|
// can select an output section for the SHF_LINK_ORDER section.
|
||||||
|
if (config->relocatable && (isec->flags & SHF_LINK_ORDER))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (auto *sec = dyn_cast<InputSection>(isec))
|
if (auto *sec = dyn_cast<InputSection>(isec))
|
||||||
if (InputSectionBase *rel = sec->getRelocatedSection())
|
if (InputSectionBase *rel = sec->getRelocatedSection())
|
||||||
if (auto *relIS = dyn_cast_or_null<InputSectionBase>(rel->parent))
|
if (auto *relIS = dyn_cast_or_null<InputSectionBase>(rel->parent))
|
||||||
@ -772,6 +814,14 @@ void LinkerScript::assignOffsets(OutputSection *sec) {
|
|||||||
if ((sec->flags & SHF_ALLOC) && sec->addrExpr)
|
if ((sec->flags & SHF_ALLOC) && sec->addrExpr)
|
||||||
setDot(sec->addrExpr, sec->location, false);
|
setDot(sec->addrExpr, sec->location, false);
|
||||||
|
|
||||||
|
// If the address of the section has been moved forward by an explicit
|
||||||
|
// expression so that it now starts past the current curPos of the enclosing
|
||||||
|
// region, we need to expand the current region to account for the space
|
||||||
|
// between the previous section, if any, and the start of this section.
|
||||||
|
if (ctx->memRegion && ctx->memRegion->curPos < dot)
|
||||||
|
expandMemoryRegion(ctx->memRegion, dot - ctx->memRegion->curPos,
|
||||||
|
ctx->memRegion->name, sec->name);
|
||||||
|
|
||||||
switchTo(sec);
|
switchTo(sec);
|
||||||
|
|
||||||
if (sec->lmaExpr)
|
if (sec->lmaExpr)
|
||||||
@ -972,17 +1022,13 @@ static uint64_t computeBase(uint64_t min, bool allocateHeaders) {
|
|||||||
return alignDown(min, config->maxPageSize);
|
return alignDown(min, config->maxPageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find an address for the file and program headers output sections,
|
// When the SECTIONS command is used, try to find an address for the file and
|
||||||
// which were unconditionally added to the first PT_LOAD segment earlier.
|
// program headers output sections, which can be added to the first PT_LOAD
|
||||||
|
// segment when program headers are created.
|
||||||
//
|
//
|
||||||
// When using the default layout, we check if the headers fit below the first
|
// We check if the headers fit below the first allocated section. If there isn't
|
||||||
// allocated section. When using a linker script, we also check if the headers
|
// enough space for these sections, we'll remove them from the PT_LOAD segment,
|
||||||
// are covered by the output section. This allows omitting the headers by not
|
// and we'll also remove the PT_PHDR segment.
|
||||||
// leaving enough space for them in the linker script; this pattern is common
|
|
||||||
// in embedded systems.
|
|
||||||
//
|
|
||||||
// If there isn't enough space for these sections, we'll remove them from the
|
|
||||||
// PT_LOAD segment, and we'll also remove the PT_PHDR segment.
|
|
||||||
void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
|
void LinkerScript::allocateHeaders(std::vector<PhdrEntry *> &phdrs) {
|
||||||
uint64_t min = std::numeric_limits<uint64_t>::max();
|
uint64_t min = std::numeric_limits<uint64_t>::max();
|
||||||
for (OutputSection *sec : outputSections)
|
for (OutputSection *sec : outputSections)
|
||||||
@ -1028,32 +1074,30 @@ LinkerScript::AddressState::AddressState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t getInitialDot() {
|
|
||||||
// By default linker scripts use an initial value of 0 for '.',
|
|
||||||
// but prefer -image-base if set.
|
|
||||||
if (script->hasSectionsCommand)
|
|
||||||
return config->imageBase ? *config->imageBase : 0;
|
|
||||||
|
|
||||||
uint64_t startAddr = UINT64_MAX;
|
|
||||||
// The sections with -T<section> have been sorted in order of ascending
|
|
||||||
// address. We must lower startAddr if the lowest -T<section address> as
|
|
||||||
// calls to setDot() must be monotonically increasing.
|
|
||||||
for (auto &kv : config->sectionStartMap)
|
|
||||||
startAddr = std::min(startAddr, kv.second);
|
|
||||||
return std::min(startAddr, target->getImageBase() + elf::getHeaderSize());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we assign addresses as instructed by linker script SECTIONS
|
// Here we assign addresses as instructed by linker script SECTIONS
|
||||||
// sub-commands. Doing that allows us to use final VA values, so here
|
// sub-commands. Doing that allows us to use final VA values, so here
|
||||||
// we also handle rest commands like symbol assignments and ASSERTs.
|
// we also handle rest commands like symbol assignments and ASSERTs.
|
||||||
void LinkerScript::assignAddresses() {
|
// Returns a symbol that has changed its section or value, or nullptr if no
|
||||||
dot = getInitialDot();
|
// symbol has changed.
|
||||||
|
const Defined *LinkerScript::assignAddresses() {
|
||||||
|
if (script->hasSectionsCommand) {
|
||||||
|
// With a linker script, assignment of addresses to headers is covered by
|
||||||
|
// allocateHeaders().
|
||||||
|
dot = config->imageBase.getValueOr(0);
|
||||||
|
} else {
|
||||||
|
// Assign addresses to headers right now.
|
||||||
|
dot = target->getImageBase();
|
||||||
|
Out::elfHeader->addr = dot;
|
||||||
|
Out::programHeaders->addr = dot + Out::elfHeader->size;
|
||||||
|
dot += getHeaderSize();
|
||||||
|
}
|
||||||
|
|
||||||
auto deleter = make_unique<AddressState>();
|
auto deleter = std::make_unique<AddressState>();
|
||||||
ctx = deleter.get();
|
ctx = deleter.get();
|
||||||
errorOnMissingSection = true;
|
errorOnMissingSection = true;
|
||||||
switchTo(aether);
|
switchTo(aether);
|
||||||
|
|
||||||
|
SymbolAssignmentMap oldValues = getSymbolAssignmentValues(sectionCommands);
|
||||||
for (BaseCommand *base : sectionCommands) {
|
for (BaseCommand *base : sectionCommands) {
|
||||||
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
|
if (auto *cmd = dyn_cast<SymbolAssignment>(base)) {
|
||||||
cmd->addr = dot;
|
cmd->addr = dot;
|
||||||
@ -1063,7 +1107,9 @@ void LinkerScript::assignAddresses() {
|
|||||||
}
|
}
|
||||||
assignOffsets(cast<OutputSection>(base));
|
assignOffsets(cast<OutputSection>(base));
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = nullptr;
|
ctx = nullptr;
|
||||||
|
return getChangedSymbolAssignment(oldValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates program headers as instructed by PHDRS linker script command.
|
// Creates program headers as instructed by PHDRS linker script command.
|
||||||
@ -1156,3 +1202,6 @@ std::vector<size_t> LinkerScript::getPhdrIndices(OutputSection *cmd) {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -168,6 +168,12 @@ struct InputSectionDescription : BaseCommand {
|
|||||||
// will be associated with this InputSectionDescription.
|
// will be associated with this InputSectionDescription.
|
||||||
std::vector<SectionPattern> sectionPatterns;
|
std::vector<SectionPattern> sectionPatterns;
|
||||||
|
|
||||||
|
// Includes InputSections and MergeInputSections. Used temporarily during
|
||||||
|
// assignment of input sections to output sections.
|
||||||
|
std::vector<InputSectionBase *> sectionBases;
|
||||||
|
|
||||||
|
// Used after the finalizeInputSections() pass. MergeInputSections have been
|
||||||
|
// merged into MergeSyntheticSections.
|
||||||
std::vector<InputSection *> sections;
|
std::vector<InputSection *> sections;
|
||||||
|
|
||||||
// Temporary record of synthetic ThunkSection instances and the pass that
|
// Temporary record of synthetic ThunkSection instances and the pass that
|
||||||
@ -226,10 +232,10 @@ class LinkerScript final {
|
|||||||
void expandOutputSection(uint64_t size);
|
void expandOutputSection(uint64_t size);
|
||||||
void expandMemoryRegions(uint64_t size);
|
void expandMemoryRegions(uint64_t size);
|
||||||
|
|
||||||
std::vector<InputSection *>
|
std::vector<InputSectionBase *>
|
||||||
computeInputSections(const InputSectionDescription *);
|
computeInputSections(const InputSectionDescription *);
|
||||||
|
|
||||||
std::vector<InputSection *> createInputSectionList(OutputSection &cmd);
|
std::vector<InputSectionBase *> createInputSectionList(OutputSection &cmd);
|
||||||
|
|
||||||
std::vector<size_t> getPhdrIndices(OutputSection *sec);
|
std::vector<size_t> getPhdrIndices(OutputSection *sec);
|
||||||
|
|
||||||
@ -259,7 +265,7 @@ class LinkerScript final {
|
|||||||
|
|
||||||
bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
|
bool hasPhdrsCommands() { return !phdrsCommands.empty(); }
|
||||||
uint64_t getDot() { return dot; }
|
uint64_t getDot() { return dot; }
|
||||||
void discard(ArrayRef<InputSection *> v);
|
void discard(InputSectionBase *s);
|
||||||
|
|
||||||
ExprValue getSymbolValue(StringRef name, const Twine &loc);
|
ExprValue getSymbolValue(StringRef name, const Twine &loc);
|
||||||
|
|
||||||
@ -271,9 +277,10 @@ class LinkerScript final {
|
|||||||
bool needsInterpSection();
|
bool needsInterpSection();
|
||||||
|
|
||||||
bool shouldKeep(InputSectionBase *s);
|
bool shouldKeep(InputSectionBase *s);
|
||||||
void assignAddresses();
|
const Defined *assignAddresses();
|
||||||
void allocateHeaders(std::vector<PhdrEntry *> &phdrs);
|
void allocateHeaders(std::vector<PhdrEntry *> &phdrs);
|
||||||
void processSectionCommands();
|
void processSectionCommands();
|
||||||
|
void processSymbolAssignments();
|
||||||
void declareSymbols();
|
void declareSymbols();
|
||||||
|
|
||||||
// Used to handle INSERT AFTER statements.
|
// Used to handle INSERT AFTER statements.
|
||||||
|
@ -34,13 +34,12 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
|
using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
|
||||||
|
|
||||||
static const std::string indent8 = " "; // 8 spaces
|
static constexpr char indent8[] = " "; // 8 spaces
|
||||||
static const std::string indent16 = " "; // 16 spaces
|
static constexpr char indent16[] = " "; // 16 spaces
|
||||||
|
|
||||||
// Print out the first three columns of a line.
|
// Print out the first three columns of a line.
|
||||||
static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma,
|
static void writeHeader(raw_ostream &os, uint64_t vma, uint64_t lma,
|
||||||
@ -139,13 +138,13 @@ static void printEhFrame(raw_ostream &os, const EhFrameSection *sec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void elf::writeMapFile() {
|
void writeMapFile() {
|
||||||
if (config->mapFile.empty())
|
if (config->mapFile.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Open a map file for writing.
|
// Open a map file for writing.
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
raw_fd_ostream os(config->mapFile, ec, sys::fs::F_None);
|
raw_fd_ostream os(config->mapFile, ec, sys::fs::OF_None);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
error("cannot open " + config->mapFile + ": " + ec.message());
|
error("cannot open " + config->mapFile + ": " + ec.message());
|
||||||
return;
|
return;
|
||||||
@ -228,7 +227,7 @@ static void print(StringRef a, StringRef b) {
|
|||||||
//
|
//
|
||||||
// In this case, strlen is defined by libc.so.6 and used by other two
|
// In this case, strlen is defined by libc.so.6 and used by other two
|
||||||
// files.
|
// files.
|
||||||
void elf::writeCrossReferenceTable() {
|
void writeCrossReferenceTable() {
|
||||||
if (!config->cref)
|
if (!config->cref)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -259,3 +258,6 @@ void elf::writeCrossReferenceTable() {
|
|||||||
print("", toString(file));
|
print("", toString(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -37,11 +37,11 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
|
||||||
|
|
||||||
using namespace lld;
|
namespace endian = llvm::support::endian;
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
namespace {
|
namespace {
|
||||||
template <class ELFT> class MarkLive {
|
template <class ELFT> class MarkLive {
|
||||||
public:
|
public:
|
||||||
@ -141,7 +141,7 @@ void MarkLive<ELFT>::scanEhFrameSection(EhInputSection &eh,
|
|||||||
if (firstRelI == (unsigned)-1)
|
if (firstRelI == (unsigned)-1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
|
if (endian::read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
|
||||||
// This is a CIE, we only need to worry about the first relocation. It is
|
// This is a CIE, we only need to worry about the first relocation. It is
|
||||||
// known to point to the personality function.
|
// known to point to the personality function.
|
||||||
resolveReloc(eh, rels[firstRelI], false);
|
resolveReloc(eh, rels[firstRelI], false);
|
||||||
@ -291,6 +291,10 @@ template <class ELFT> void MarkLive<ELFT>::mark() {
|
|||||||
// GOT, which means that the ifunc must be available when the main partition is
|
// GOT, which means that the ifunc must be available when the main partition is
|
||||||
// loaded) and TLS symbols (because we only know how to correctly process TLS
|
// loaded) and TLS symbols (because we only know how to correctly process TLS
|
||||||
// relocations for the main partition).
|
// relocations for the main partition).
|
||||||
|
//
|
||||||
|
// We also need to move sections whose names are C identifiers that are referred
|
||||||
|
// to from __start_/__stop_ symbols because there will only be one set of
|
||||||
|
// symbols for the whole program.
|
||||||
template <class ELFT> void MarkLive<ELFT>::moveToMain() {
|
template <class ELFT> void MarkLive<ELFT>::moveToMain() {
|
||||||
for (InputFile *file : objectFiles)
|
for (InputFile *file : objectFiles)
|
||||||
for (Symbol *s : file->getSymbols())
|
for (Symbol *s : file->getSymbols())
|
||||||
@ -299,13 +303,21 @@ template <class ELFT> void MarkLive<ELFT>::moveToMain() {
|
|||||||
d->section->isLive())
|
d->section->isLive())
|
||||||
markSymbol(s);
|
markSymbol(s);
|
||||||
|
|
||||||
|
for (InputSectionBase *sec : inputSections) {
|
||||||
|
if (!sec->isLive() || !isValidCIdentifier(sec->name))
|
||||||
|
continue;
|
||||||
|
if (symtab->find(("__start_" + sec->name).str()) ||
|
||||||
|
symtab->find(("__stop_" + sec->name).str()))
|
||||||
|
enqueue(sec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
mark();
|
mark();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before calling this function, Live bits are off for all
|
// Before calling this function, Live bits are off for all
|
||||||
// input sections. This function make some or all of them on
|
// input sections. This function make some or all of them on
|
||||||
// so that they are emitted to the output file.
|
// so that they are emitted to the output file.
|
||||||
template <class ELFT> void elf::markLive() {
|
template <class ELFT> void markLive() {
|
||||||
// If -gc-sections is not given, no sections are removed.
|
// If -gc-sections is not given, no sections are removed.
|
||||||
if (!config->gcSections) {
|
if (!config->gcSections) {
|
||||||
for (InputSectionBase *sec : inputSections)
|
for (InputSectionBase *sec : inputSections)
|
||||||
@ -367,7 +379,10 @@ template <class ELFT> void elf::markLive() {
|
|||||||
message("removing unused section " + toString(sec));
|
message("removing unused section " + toString(sec));
|
||||||
}
|
}
|
||||||
|
|
||||||
template void elf::markLive<ELF32LE>();
|
template void markLive<ELF32LE>();
|
||||||
template void elf::markLive<ELF32BE>();
|
template void markLive<ELF32BE>();
|
||||||
template void elf::markLive<ELF64LE>();
|
template void markLive<ELF64LE>();
|
||||||
template void elf::markLive<ELF64BE>();
|
template void markLive<ELF64BE>();
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -171,6 +171,9 @@ defm fini: Eq<"fini", "Specify a finalizer function">, MetaVarName<"<symbol>">;
|
|||||||
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
|
def fix_cortex_a53_843419: F<"fix-cortex-a53-843419">,
|
||||||
HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
|
HelpText<"Apply fixes for AArch64 Cortex-A53 erratum 843419">;
|
||||||
|
|
||||||
|
def fix_cortex_a8: F<"fix-cortex-a8">,
|
||||||
|
HelpText<"Apply fixes for ARM Cortex-A8 erratum 657417">;
|
||||||
|
|
||||||
// This option is intentionally hidden from the user as the implementation
|
// This option is intentionally hidden from the user as the implementation
|
||||||
// is not complete.
|
// is not complete.
|
||||||
def require_cet: F<"require-cet">;
|
def require_cet: F<"require-cet">;
|
||||||
@ -306,7 +309,7 @@ def push_state: F<"push-state">,
|
|||||||
def print_map: F<"print-map">,
|
def print_map: F<"print-map">,
|
||||||
HelpText<"Print a link map to the standard output">;
|
HelpText<"Print a link map to the standard output">;
|
||||||
|
|
||||||
defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">;
|
defm reproduce: Eq<"reproduce", "Write a tar file containing input files and command line options to reproduce link">;
|
||||||
|
|
||||||
defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
|
defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
|
||||||
|
|
||||||
@ -478,6 +481,7 @@ def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
|
|||||||
HelpText<"Perform context senstive PGO instrumentation">;
|
HelpText<"Perform context senstive PGO instrumentation">;
|
||||||
def lto_cs_profile_file: J<"lto-cs-profile-file=">,
|
def lto_cs_profile_file: J<"lto-cs-profile-file=">,
|
||||||
HelpText<"Context sensitive profile file path">;
|
HelpText<"Context sensitive profile file path">;
|
||||||
|
def lto_obj_path_eq: J<"lto-obj-path=">;
|
||||||
def lto_sample_profile: J<"lto-sample-profile=">,
|
def lto_sample_profile: J<"lto-sample-profile=">,
|
||||||
HelpText<"Sample profile file path">;
|
HelpText<"Sample profile file path">;
|
||||||
def disable_verify: F<"disable-verify">;
|
def disable_verify: F<"disable-verify">;
|
||||||
@ -495,7 +499,12 @@ def save_temps: F<"save-temps">;
|
|||||||
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
|
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
|
||||||
HelpText<"Path to ThinLTO cached object file directory">;
|
HelpText<"Path to ThinLTO cached object file directory">;
|
||||||
defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
|
defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
|
||||||
|
def thinlto_emit_imports_files: F<"thinlto-emit-imports-files">;
|
||||||
|
def thinlto_index_only: F<"thinlto-index-only">;
|
||||||
|
def thinlto_index_only_eq: J<"thinlto-index-only=">;
|
||||||
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
|
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
|
||||||
|
def thinlto_object_suffix_replace_eq: J<"thinlto-object-suffix-replace=">;
|
||||||
|
def thinlto_prefix_replace_eq: J<"thinlto-prefix-replace=">;
|
||||||
|
|
||||||
def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">;
|
def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">;
|
||||||
def: F<"plugin-opt=debug-pass-manager">,
|
def: F<"plugin-opt=debug-pass-manager">,
|
||||||
@ -509,19 +518,31 @@ def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for
|
|||||||
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
|
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
|
||||||
def: F<"plugin-opt=new-pass-manager">,
|
def: F<"plugin-opt=new-pass-manager">,
|
||||||
Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
|
Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
|
||||||
def plugin_opt_obj_path_eq: J<"plugin-opt=obj-path=">;
|
|
||||||
def: F<"plugin-opt=cs-profile-generate">,
|
def: F<"plugin-opt=cs-profile-generate">,
|
||||||
Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">;
|
Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">;
|
||||||
def: J<"plugin-opt=cs-profile-path=">,
|
def: J<"plugin-opt=cs-profile-path=">,
|
||||||
Alias<lto_cs_profile_file>, HelpText<"Alias for -lto-cs-profile-file">;
|
Alias<lto_cs_profile_file>, HelpText<"Alias for -lto-cs-profile-file">;
|
||||||
|
def: J<"plugin-opt=obj-path=">,
|
||||||
|
Alias<lto_obj_path_eq>,
|
||||||
|
HelpText<"Alias for -lto-obj-path=">;
|
||||||
def: J<"plugin-opt=sample-profile=">,
|
def: J<"plugin-opt=sample-profile=">,
|
||||||
Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
|
Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
|
||||||
def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
|
def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
|
||||||
def plugin_opt_thinlto_emit_imports_files: F<"plugin-opt=thinlto-emit-imports-files">;
|
def: F<"plugin-opt=thinlto-emit-imports-files">,
|
||||||
def plugin_opt_thinlto_index_only: F<"plugin-opt=thinlto-index-only">;
|
Alias<thinlto_emit_imports_files>,
|
||||||
def plugin_opt_thinlto_index_only_eq: J<"plugin-opt=thinlto-index-only=">;
|
HelpText<"Alias for -thinlto-emit-imports-files">;
|
||||||
def plugin_opt_thinlto_object_suffix_replace_eq: J<"plugin-opt=thinlto-object-suffix-replace=">;
|
def: F<"plugin-opt=thinlto-index-only">,
|
||||||
def plugin_opt_thinlto_prefix_replace_eq: J<"plugin-opt=thinlto-prefix-replace=">;
|
Alias<thinlto_index_only>,
|
||||||
|
HelpText<"Alias for -thinlto-index-only">;
|
||||||
|
def: J<"plugin-opt=thinlto-index-only=">,
|
||||||
|
Alias<thinlto_index_only_eq>,
|
||||||
|
HelpText<"Alias for -thinlto-index-only=">;
|
||||||
|
def: J<"plugin-opt=thinlto-object-suffix-replace=">,
|
||||||
|
Alias<thinlto_object_suffix_replace_eq>,
|
||||||
|
HelpText<"Alias for -thinlto-object-suffix-replace=">;
|
||||||
|
def: J<"plugin-opt=thinlto-prefix-replace=">,
|
||||||
|
Alias<thinlto_prefix_replace_eq>,
|
||||||
|
HelpText<"Alias for -thinlto-prefix-replace=">;
|
||||||
|
|
||||||
// Ignore LTO plugin-related options.
|
// Ignore LTO plugin-related options.
|
||||||
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
|
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
|
||||||
|
@ -27,9 +27,8 @@ using namespace llvm::object;
|
|||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
uint8_t *Out::bufferStart;
|
uint8_t *Out::bufferStart;
|
||||||
uint8_t Out::first;
|
uint8_t Out::first;
|
||||||
PhdrEntry *Out::tlsPhdr;
|
PhdrEntry *Out::tlsPhdr;
|
||||||
@ -39,7 +38,7 @@ OutputSection *Out::preinitArray;
|
|||||||
OutputSection *Out::initArray;
|
OutputSection *Out::initArray;
|
||||||
OutputSection *Out::finiArray;
|
OutputSection *Out::finiArray;
|
||||||
|
|
||||||
std::vector<OutputSection *> elf::outputSections;
|
std::vector<OutputSection *> outputSections;
|
||||||
|
|
||||||
uint32_t OutputSection::getPhdrFlags() const {
|
uint32_t OutputSection::getPhdrFlags() const {
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
@ -83,12 +82,32 @@ static bool canMergeToProgbits(unsigned type) {
|
|||||||
type == SHT_NOTE;
|
type == SHT_NOTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputSection::addSection(InputSection *isec) {
|
// Record that isec will be placed in the OutputSection. isec does not become
|
||||||
|
// permanent until finalizeInputSections() is called. The function should not be
|
||||||
|
// used after finalizeInputSections() is called. If you need to add an
|
||||||
|
// InputSection post finalizeInputSections(), then you must do the following:
|
||||||
|
//
|
||||||
|
// 1. Find or create an InputSectionDescription to hold InputSection.
|
||||||
|
// 2. Add the InputSection to the InputSectionDesciption::sections.
|
||||||
|
// 3. Call commitSection(isec).
|
||||||
|
void OutputSection::recordSection(InputSectionBase *isec) {
|
||||||
|
partition = isec->partition;
|
||||||
|
isec->parent = this;
|
||||||
|
if (sectionCommands.empty() ||
|
||||||
|
!isa<InputSectionDescription>(sectionCommands.back()))
|
||||||
|
sectionCommands.push_back(make<InputSectionDescription>(""));
|
||||||
|
auto *isd = cast<InputSectionDescription>(sectionCommands.back());
|
||||||
|
isd->sectionBases.push_back(isec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update fields (type, flags, alignment, etc) according to the InputSection
|
||||||
|
// isec. Also check whether the InputSection flags and type are consistent with
|
||||||
|
// other InputSections.
|
||||||
|
void OutputSection::commitSection(InputSection *isec) {
|
||||||
if (!hasInputSections) {
|
if (!hasInputSections) {
|
||||||
// If IS is the first section to be added to this section,
|
// If IS is the first section to be added to this section,
|
||||||
// initialize Partition, Type, Entsize and flags from IS.
|
// initialize type, entsize and flags from isec.
|
||||||
hasInputSections = true;
|
hasInputSections = true;
|
||||||
partition = isec->partition;
|
|
||||||
type = isec->type;
|
type = isec->type;
|
||||||
entsize = isec->entsize;
|
entsize = isec->entsize;
|
||||||
flags = isec->flags;
|
flags = isec->flags;
|
||||||
@ -110,6 +129,8 @@ void OutputSection::addSection(InputSection *isec) {
|
|||||||
type = SHT_PROGBITS;
|
type = SHT_PROGBITS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (noload)
|
||||||
|
type = SHT_NOBITS;
|
||||||
|
|
||||||
isec->parent = this;
|
isec->parent = this;
|
||||||
uint64_t andMask =
|
uint64_t andMask =
|
||||||
@ -118,6 +139,8 @@ void OutputSection::addSection(InputSection *isec) {
|
|||||||
uint64_t andFlags = (flags & isec->flags) & andMask;
|
uint64_t andFlags = (flags & isec->flags) & andMask;
|
||||||
uint64_t orFlags = (flags | isec->flags) & orMask;
|
uint64_t orFlags = (flags | isec->flags) & orMask;
|
||||||
flags = andFlags | orFlags;
|
flags = andFlags | orFlags;
|
||||||
|
if (nonAlloc)
|
||||||
|
flags &= ~(uint64_t)SHF_ALLOC;
|
||||||
|
|
||||||
alignment = std::max(alignment, isec->alignment);
|
alignment = std::max(alignment, isec->alignment);
|
||||||
|
|
||||||
@ -126,15 +149,69 @@ void OutputSection::addSection(InputSection *isec) {
|
|||||||
// set sh_entsize to 0.
|
// set sh_entsize to 0.
|
||||||
if (entsize != isec->entsize)
|
if (entsize != isec->entsize)
|
||||||
entsize = 0;
|
entsize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isec->assigned) {
|
// This function scans over the InputSectionBase list sectionBases to create
|
||||||
isec->assigned = true;
|
// InputSectionDescription::sections.
|
||||||
if (sectionCommands.empty() ||
|
//
|
||||||
!isa<InputSectionDescription>(sectionCommands.back()))
|
// It removes MergeInputSections from the input section array and adds
|
||||||
sectionCommands.push_back(make<InputSectionDescription>(""));
|
// new synthetic sections at the location of the first input section
|
||||||
auto *isd = cast<InputSectionDescription>(sectionCommands.back());
|
// that it replaces. It then finalizes each synthetic section in order
|
||||||
isd->sections.push_back(isec);
|
// to compute an output offset for each piece of each input section.
|
||||||
|
void OutputSection::finalizeInputSections() {
|
||||||
|
std::vector<MergeSyntheticSection *> mergeSections;
|
||||||
|
for (BaseCommand *base : sectionCommands) {
|
||||||
|
auto *cmd = dyn_cast<InputSectionDescription>(base);
|
||||||
|
if (!cmd)
|
||||||
|
continue;
|
||||||
|
cmd->sections.reserve(cmd->sectionBases.size());
|
||||||
|
for (InputSectionBase *s : cmd->sectionBases) {
|
||||||
|
MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
|
||||||
|
if (!ms) {
|
||||||
|
cmd->sections.push_back(cast<InputSection>(s));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not want to handle sections that are not alive, so just remove
|
||||||
|
// them instead of trying to merge.
|
||||||
|
if (!ms->isLive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) {
|
||||||
|
// While we could create a single synthetic section for two different
|
||||||
|
// values of Entsize, it is better to take Entsize into consideration.
|
||||||
|
//
|
||||||
|
// With a single synthetic section no two pieces with different Entsize
|
||||||
|
// could be equal, so we may as well have two sections.
|
||||||
|
//
|
||||||
|
// Using Entsize in here also allows us to propagate it to the synthetic
|
||||||
|
// section.
|
||||||
|
//
|
||||||
|
// SHF_STRINGS section with different alignments should not be merged.
|
||||||
|
return sec->flags == ms->flags && sec->entsize == ms->entsize &&
|
||||||
|
(sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
|
||||||
|
});
|
||||||
|
if (i == mergeSections.end()) {
|
||||||
|
MergeSyntheticSection *syn =
|
||||||
|
createMergeSynthetic(name, ms->type, ms->flags, ms->alignment);
|
||||||
|
mergeSections.push_back(syn);
|
||||||
|
i = std::prev(mergeSections.end());
|
||||||
|
syn->entsize = ms->entsize;
|
||||||
|
cmd->sections.push_back(syn);
|
||||||
|
}
|
||||||
|
(*i)->addSection(ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sectionBases should not be used from this point onwards. Clear it to
|
||||||
|
// catch misuses.
|
||||||
|
cmd->sectionBases.clear();
|
||||||
|
|
||||||
|
// Some input sections may be removed from the list after ICF.
|
||||||
|
for (InputSection *s : cmd->sections)
|
||||||
|
commitSection(s);
|
||||||
}
|
}
|
||||||
|
for (auto *ms : mergeSections)
|
||||||
|
ms->finalizeContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sortByOrder(MutableArrayRef<InputSection *> in,
|
static void sortByOrder(MutableArrayRef<InputSection *> in,
|
||||||
@ -148,7 +225,7 @@ static void sortByOrder(MutableArrayRef<InputSection *> in,
|
|||||||
in[i] = v[i].second;
|
in[i] = v[i].second;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t elf::getHeaderSize() {
|
uint64_t getHeaderSize() {
|
||||||
if (config->oFormatBinary)
|
if (config->oFormatBinary)
|
||||||
return 0;
|
return 0;
|
||||||
return Out::elfHeader->size + Out::programHeaders->size;
|
return Out::elfHeader->size + Out::programHeaders->size;
|
||||||
@ -368,7 +445,7 @@ void OutputSection::sortCtorsDtors() {
|
|||||||
// If an input string is in the form of "foo.N" where N is a number,
|
// If an input string is in the form of "foo.N" where N is a number,
|
||||||
// return N. Otherwise, returns 65536, which is one greater than the
|
// return N. Otherwise, returns 65536, which is one greater than the
|
||||||
// lowest priority.
|
// lowest priority.
|
||||||
int elf::getPriority(StringRef s) {
|
int getPriority(StringRef s) {
|
||||||
size_t pos = s.rfind('.');
|
size_t pos = s.rfind('.');
|
||||||
if (pos == StringRef::npos)
|
if (pos == StringRef::npos)
|
||||||
return 65536;
|
return 65536;
|
||||||
@ -378,7 +455,7 @@ int elf::getPriority(StringRef s) {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<InputSection *> elf::getInputSections(OutputSection *os) {
|
std::vector<InputSection *> getInputSections(OutputSection *os) {
|
||||||
std::vector<InputSection *> ret;
|
std::vector<InputSection *> ret;
|
||||||
for (BaseCommand *base : os->sectionCommands)
|
for (BaseCommand *base : os->sectionCommands)
|
||||||
if (auto *isd = dyn_cast<InputSectionDescription>(base))
|
if (auto *isd = dyn_cast<InputSectionDescription>(base))
|
||||||
@ -419,3 +496,6 @@ template void OutputSection::maybeCompress<ELF32LE>();
|
|||||||
template void OutputSection::maybeCompress<ELF32BE>();
|
template void OutputSection::maybeCompress<ELF32BE>();
|
||||||
template void OutputSection::maybeCompress<ELF64LE>();
|
template void OutputSection::maybeCompress<ELF64LE>();
|
||||||
template void OutputSection::maybeCompress<ELF64BE>();
|
template void OutputSection::maybeCompress<ELF64BE>();
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -71,7 +71,9 @@ class OutputSection final : public BaseCommand, public SectionBase {
|
|||||||
uint64_t addr = 0;
|
uint64_t addr = 0;
|
||||||
uint32_t shName = 0;
|
uint32_t shName = 0;
|
||||||
|
|
||||||
void addSection(InputSection *isec);
|
void recordSection(InputSectionBase *isec);
|
||||||
|
void commitSection(InputSection *isec);
|
||||||
|
void finalizeInputSections();
|
||||||
|
|
||||||
// The following members are normally only used in linker scripts.
|
// The following members are normally only used in linker scripts.
|
||||||
MemoryRegion *memRegion = nullptr;
|
MemoryRegion *memRegion = nullptr;
|
||||||
|
@ -62,9 +62,8 @@ using namespace llvm::ELF;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
|
static Optional<std::string> getLinkerScriptLocation(const Symbol &sym) {
|
||||||
for (BaseCommand *base : script->sectionCommands)
|
for (BaseCommand *base : script->sectionCommands)
|
||||||
if (auto *cmd = dyn_cast<SymbolAssignment>(base))
|
if (auto *cmd = dyn_cast<SymbolAssignment>(base))
|
||||||
@ -344,9 +343,9 @@ static bool needsPlt(RelExpr expr) {
|
|||||||
// returns false for TLS variables even though they need GOT, because
|
// returns false for TLS variables even though they need GOT, because
|
||||||
// TLS variables uses GOT differently than the regular variables.
|
// TLS variables uses GOT differently than the regular variables.
|
||||||
static bool needsGot(RelExpr expr) {
|
static bool needsGot(RelExpr expr) {
|
||||||
return oneof<R_GOT, R_GOT_OFF, R_HEXAGON_GOT, R_MIPS_GOT_LOCAL_PAGE,
|
return oneof<R_GOT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF,
|
||||||
R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC,
|
R_MIPS_GOT_OFF32, R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTPLT>(
|
||||||
R_GOT_PC, R_GOTPLT>(expr);
|
expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// True if this expression is of the form Sym - X, where X is a position in the
|
// True if this expression is of the form Sym - X, where X is a position in the
|
||||||
@ -369,7 +368,7 @@ static bool isRelExpr(RelExpr expr) {
|
|||||||
static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
|
static bool isStaticLinkTimeConstant(RelExpr e, RelType type, const Symbol &sym,
|
||||||
InputSectionBase &s, uint64_t relOff) {
|
InputSectionBase &s, uint64_t relOff) {
|
||||||
// These expressions always compute a constant
|
// These expressions always compute a constant
|
||||||
if (oneof<R_DTPREL, R_GOTPLT, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
|
if (oneof<R_DTPREL, R_GOTPLT, R_GOT_OFF, R_TLSLD_GOT_OFF,
|
||||||
R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
|
R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
|
||||||
R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
|
R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
|
||||||
R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
|
R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
|
||||||
@ -510,10 +509,8 @@ static void replaceWithDefined(Symbol &sym, SectionBase *sec, uint64_t value,
|
|||||||
sym.gotIndex = old.gotIndex;
|
sym.gotIndex = old.gotIndex;
|
||||||
sym.verdefIndex = old.verdefIndex;
|
sym.verdefIndex = old.verdefIndex;
|
||||||
sym.ppc64BranchltIndex = old.ppc64BranchltIndex;
|
sym.ppc64BranchltIndex = old.ppc64BranchltIndex;
|
||||||
sym.isPreemptible = true;
|
|
||||||
sym.exportDynamic = true;
|
sym.exportDynamic = true;
|
||||||
sym.isUsedInRegularObj = true;
|
sym.isUsedInRegularObj = true;
|
||||||
sym.used = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reserve space in .bss or .bss.rel.ro for copy relocation.
|
// Reserve space in .bss or .bss.rel.ro for copy relocation.
|
||||||
@ -569,10 +566,16 @@ template <class ELFT> static void addCopyRelSymbol(SharedSymbol &ss) {
|
|||||||
bool isRO = isReadOnly<ELFT>(ss);
|
bool isRO = isReadOnly<ELFT>(ss);
|
||||||
BssSection *sec =
|
BssSection *sec =
|
||||||
make<BssSection>(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment);
|
make<BssSection>(isRO ? ".bss.rel.ro" : ".bss", symSize, ss.alignment);
|
||||||
if (isRO)
|
OutputSection *osec = (isRO ? in.bssRelRo : in.bss)->getParent();
|
||||||
in.bssRelRo->getParent()->addSection(sec);
|
|
||||||
else
|
// At this point, sectionBases has been migrated to sections. Append sec to
|
||||||
in.bss->getParent()->addSection(sec);
|
// sections.
|
||||||
|
if (osec->sectionCommands.empty() ||
|
||||||
|
!isa<InputSectionDescription>(osec->sectionCommands.back()))
|
||||||
|
osec->sectionCommands.push_back(make<InputSectionDescription>(""));
|
||||||
|
auto *isd = cast<InputSectionDescription>(osec->sectionCommands.back());
|
||||||
|
isd->sections.push_back(sec);
|
||||||
|
osec->commitSection(sec);
|
||||||
|
|
||||||
// Look through the DSO's dynamic symbol table for aliases and create a
|
// Look through the DSO's dynamic symbol table for aliases and create a
|
||||||
// dynamic symbol for each one. This causes the copy relocation to correctly
|
// dynamic symbol for each one. This causes the copy relocation to correctly
|
||||||
@ -693,8 +696,75 @@ struct UndefinedDiag {
|
|||||||
|
|
||||||
static std::vector<UndefinedDiag> undefs;
|
static std::vector<UndefinedDiag> undefs;
|
||||||
|
|
||||||
|
// Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns
|
||||||
|
// the suggested symbol, which is either in the symbol table, or in the same
|
||||||
|
// file of sym.
|
||||||
|
static const Symbol *getAlternativeSpelling(const Undefined &sym) {
|
||||||
|
// Build a map of local defined symbols.
|
||||||
|
DenseMap<StringRef, const Symbol *> map;
|
||||||
|
if (sym.file && !isa<SharedFile>(sym.file)) {
|
||||||
|
for (const Symbol *s : sym.file->getSymbols())
|
||||||
|
if (s->isLocal() && s->isDefined())
|
||||||
|
map.try_emplace(s->getName(), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto suggest = [&](StringRef newName) -> const Symbol * {
|
||||||
|
// If defined locally.
|
||||||
|
if (const Symbol *s = map.lookup(newName))
|
||||||
|
return s;
|
||||||
|
|
||||||
|
// If in the symbol table and not undefined.
|
||||||
|
if (const Symbol *s = symtab->find(newName))
|
||||||
|
if (!s->isUndefined())
|
||||||
|
return s;
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This loop enumerates all strings of Levenshtein distance 1 as typo
|
||||||
|
// correction candidates and suggests the one that exists as a non-undefined
|
||||||
|
// symbol.
|
||||||
|
StringRef name = sym.getName();
|
||||||
|
for (size_t i = 0, e = name.size(); i != e + 1; ++i) {
|
||||||
|
// Insert a character before name[i].
|
||||||
|
std::string newName = (name.substr(0, i) + "0" + name.substr(i)).str();
|
||||||
|
for (char c = '0'; c <= 'z'; ++c) {
|
||||||
|
newName[i] = c;
|
||||||
|
if (const Symbol *s = suggest(newName))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
if (i == e)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Substitute name[i].
|
||||||
|
newName = name;
|
||||||
|
for (char c = '0'; c <= 'z'; ++c) {
|
||||||
|
newName[i] = c;
|
||||||
|
if (const Symbol *s = suggest(newName))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transpose name[i] and name[i+1]. This is of edit distance 2 but it is
|
||||||
|
// common.
|
||||||
|
if (i + 1 < e) {
|
||||||
|
newName[i] = name[i + 1];
|
||||||
|
newName[i + 1] = name[i];
|
||||||
|
if (const Symbol *s = suggest(newName))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete name[i].
|
||||||
|
newName = (name.substr(0, i) + name.substr(i + 1)).str();
|
||||||
|
if (const Symbol *s = suggest(newName))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
template <class ELFT>
|
template <class ELFT>
|
||||||
static void reportUndefinedSymbol(const UndefinedDiag &undef) {
|
static void reportUndefinedSymbol(const UndefinedDiag &undef,
|
||||||
|
bool correctSpelling) {
|
||||||
Symbol &sym = *undef.sym;
|
Symbol &sym = *undef.sym;
|
||||||
|
|
||||||
auto visibility = [&]() -> std::string {
|
auto visibility = [&]() -> std::string {
|
||||||
@ -734,6 +804,14 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef) {
|
|||||||
msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times")
|
msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times")
|
||||||
.str();
|
.str();
|
||||||
|
|
||||||
|
if (correctSpelling)
|
||||||
|
if (const Symbol *corrected =
|
||||||
|
getAlternativeSpelling(cast<Undefined>(sym))) {
|
||||||
|
msg += "\n>>> did you mean: " + toString(*corrected);
|
||||||
|
if (corrected->file)
|
||||||
|
msg += "\n>>> defined in: " + toString(corrected->file);
|
||||||
|
}
|
||||||
|
|
||||||
if (sym.getName().startswith("_ZTV"))
|
if (sym.getName().startswith("_ZTV"))
|
||||||
msg += "\nthe vtable symbol may be undefined because the class is missing "
|
msg += "\nthe vtable symbol may be undefined because the class is missing "
|
||||||
"its key function (see https://lld.llvm.org/missingkeyfunction)";
|
"its key function (see https://lld.llvm.org/missingkeyfunction)";
|
||||||
@ -744,7 +822,7 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef) {
|
|||||||
error(msg);
|
error(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void elf::reportUndefinedSymbols() {
|
template <class ELFT> void reportUndefinedSymbols() {
|
||||||
// Find the first "undefined symbol" diagnostic for each diagnostic, and
|
// Find the first "undefined symbol" diagnostic for each diagnostic, and
|
||||||
// collect all "referenced from" lines at the first diagnostic.
|
// collect all "referenced from" lines at the first diagnostic.
|
||||||
DenseMap<Symbol *, UndefinedDiag *> firstRef;
|
DenseMap<Symbol *, UndefinedDiag *> firstRef;
|
||||||
@ -757,23 +835,21 @@ template <class ELFT> void elf::reportUndefinedSymbols() {
|
|||||||
firstRef[undef.sym] = &undef;
|
firstRef[undef.sym] = &undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const UndefinedDiag &undef : undefs) {
|
// Enable spell corrector for the first 2 diagnostics.
|
||||||
if (!undef.locs.empty())
|
for (auto it : enumerate(undefs))
|
||||||
reportUndefinedSymbol<ELFT>(undef);
|
if (!it.value().locs.empty())
|
||||||
}
|
reportUndefinedSymbol<ELFT>(it.value(), it.index() < 2);
|
||||||
undefs.clear();
|
undefs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Report an undefined symbol if necessary.
|
// Report an undefined symbol if necessary.
|
||||||
// Returns true if the undefined symbol will produce an error message.
|
// Returns true if the undefined symbol will produce an error message.
|
||||||
template <class ELFT>
|
|
||||||
static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
|
static bool maybeReportUndefined(Symbol &sym, InputSectionBase &sec,
|
||||||
uint64_t offset) {
|
uint64_t offset) {
|
||||||
if (!sym.isUndefined() || sym.isWeak())
|
if (!sym.isUndefined() || sym.isWeak())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool canBeExternal = !sym.isLocal() && sym.computeBinding() != STB_LOCAL &&
|
bool canBeExternal = !sym.isLocal() && sym.visibility == STV_DEFAULT;
|
||||||
sym.visibility == STV_DEFAULT;
|
|
||||||
if (config->unresolvedSymbols == UnresolvedPolicy::Ignore && canBeExternal)
|
if (config->unresolvedSymbols == UnresolvedPolicy::Ignore && canBeExternal)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -997,56 +1073,29 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canWrite && (config->isPic && !isRelExpr(expr))) {
|
// When producing an executable, we can perform copy relocations (for
|
||||||
error(
|
// STT_OBJECT) and canonical PLT (for STT_FUNC).
|
||||||
"can't create dynamic relocation " + toString(type) + " against " +
|
if (!config->shared) {
|
||||||
(sym.getName().empty() ? "local symbol" : "symbol: " + toString(sym)) +
|
if (!canDefineSymbolInExecutable(sym)) {
|
||||||
" in readonly segment; recompile object files with -fPIC "
|
errorOrWarn("cannot preempt symbol: " + toString(sym) +
|
||||||
"or pass '-Wl,-z,notext' to allow text relocations in the output" +
|
getLocation(sec, sym, offset));
|
||||||
getLocation(sec, sym, offset));
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
if (sym.isObject()) {
|
||||||
// Copy relocations (for STT_OBJECT) and canonical PLT (for STT_FUNC) are only
|
// Produce a copy relocation.
|
||||||
// possible in an executable.
|
if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
|
||||||
//
|
if (!config->zCopyreloc)
|
||||||
// Among R_ABS relocatoin types, symbolicRel has the same size as the word
|
error("unresolvable relocation " + toString(type) +
|
||||||
// size. Others have fewer bits and may cause runtime overflow in -pie/-shared
|
" against symbol '" + toString(*ss) +
|
||||||
// mode. Disallow them.
|
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
|
||||||
if (config->shared ||
|
getLocation(sec, sym, offset));
|
||||||
(config->pie && expr == R_ABS && type != target->symbolicRel)) {
|
addCopyRelSymbol<ELFT>(*ss);
|
||||||
errorOrWarn(
|
}
|
||||||
"relocation " + toString(type) + " cannot be used against " +
|
sec.relocations.push_back({expr, type, offset, addend, &sym});
|
||||||
(sym.getName().empty() ? "local symbol" : "symbol " + toString(sym)) +
|
return;
|
||||||
"; recompile with -fPIC" + getLocation(sec, sym, offset));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the symbol is undefined we already reported any relevant errors.
|
|
||||||
if (sym.isUndefined())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!canDefineSymbolInExecutable(sym)) {
|
|
||||||
error("cannot preempt symbol: " + toString(sym) +
|
|
||||||
getLocation(sec, sym, offset));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sym.isObject()) {
|
|
||||||
// Produce a copy relocation.
|
|
||||||
if (auto *ss = dyn_cast<SharedSymbol>(&sym)) {
|
|
||||||
if (!config->zCopyreloc)
|
|
||||||
error("unresolvable relocation " + toString(type) +
|
|
||||||
" against symbol '" + toString(*ss) +
|
|
||||||
"'; recompile with -fPIC or remove '-z nocopyreloc'" +
|
|
||||||
getLocation(sec, sym, offset));
|
|
||||||
addCopyRelSymbol<ELFT>(*ss);
|
|
||||||
}
|
}
|
||||||
sec.relocations.push_back({expr, type, offset, addend, &sym});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sym.isFunc()) {
|
|
||||||
// This handles a non PIC program call to function in a shared library. In
|
// This handles a non PIC program call to function in a shared library. In
|
||||||
// an ideal world, we could just report an error saying the relocation can
|
// an ideal world, we could just report an error saying the relocation can
|
||||||
// overflow at runtime. In the real world with glibc, crt1.o has a
|
// overflow at runtime. In the real world with glibc, crt1.o has a
|
||||||
@ -1074,18 +1123,37 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
|
|||||||
// compiled without -fPIE/-fPIC and doesn't maintain ebx.
|
// compiled without -fPIE/-fPIC and doesn't maintain ebx.
|
||||||
// * If a library definition gets preempted to the executable, it will have
|
// * If a library definition gets preempted to the executable, it will have
|
||||||
// the wrong ebx value.
|
// the wrong ebx value.
|
||||||
if (config->pie && config->emachine == EM_386)
|
if (sym.isFunc()) {
|
||||||
errorOrWarn("symbol '" + toString(sym) +
|
if (config->pie && config->emachine == EM_386)
|
||||||
"' cannot be preempted; recompile with -fPIE" +
|
errorOrWarn("symbol '" + toString(sym) +
|
||||||
getLocation(sec, sym, offset));
|
"' cannot be preempted; recompile with -fPIE" +
|
||||||
if (!sym.isInPlt())
|
getLocation(sec, sym, offset));
|
||||||
addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
|
if (!sym.isInPlt())
|
||||||
if (!sym.isDefined())
|
addPltEntry<ELFT>(in.plt, in.gotPlt, in.relaPlt, target->pltRel, sym);
|
||||||
replaceWithDefined(
|
if (!sym.isDefined())
|
||||||
sym, in.plt,
|
replaceWithDefined(
|
||||||
target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
|
sym, in.plt,
|
||||||
sym.needsPltAddr = true;
|
target->pltHeaderSize + target->pltEntrySize * sym.pltIndex, 0);
|
||||||
sec.relocations.push_back({expr, type, offset, addend, &sym});
|
sym.needsPltAddr = true;
|
||||||
|
sec.relocations.push_back({expr, type, offset, addend, &sym});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->isPic) {
|
||||||
|
if (!canWrite && !isRelExpr(expr))
|
||||||
|
errorOrWarn(
|
||||||
|
"can't create dynamic relocation " + toString(type) + " against " +
|
||||||
|
(sym.getName().empty() ? "local symbol"
|
||||||
|
: "symbol: " + toString(sym)) +
|
||||||
|
" in readonly segment; recompile object files with -fPIC "
|
||||||
|
"or pass '-Wl,-z,notext' to allow text relocations in the output" +
|
||||||
|
getLocation(sec, sym, offset));
|
||||||
|
else
|
||||||
|
errorOrWarn(
|
||||||
|
"relocation " + toString(type) + " cannot be used against " +
|
||||||
|
(sym.getName().empty() ? "local symbol" : "symbol " + toString(sym)) +
|
||||||
|
"; recompile with -fPIC" + getLocation(sec, sym, offset));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1093,15 +1161,6 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type,
|
|||||||
getLocation(sec, sym, offset));
|
getLocation(sec, sym, offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IRelativeReloc {
|
|
||||||
RelType type;
|
|
||||||
InputSectionBase *sec;
|
|
||||||
uint64_t offset;
|
|
||||||
Symbol *sym;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::vector<IRelativeReloc> iRelativeRelocs;
|
|
||||||
|
|
||||||
template <class ELFT, class RelTy>
|
template <class ELFT, class RelTy>
|
||||||
static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
|
static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
|
||||||
RelTy *end) {
|
RelTy *end) {
|
||||||
@ -1125,7 +1184,7 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
|
|||||||
|
|
||||||
// Error if the target symbol is undefined. Symbol index 0 may be used by
|
// Error if the target symbol is undefined. Symbol index 0 may be used by
|
||||||
// marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
|
// marker relocations, e.g. R_*_NONE and R_ARM_V4BX. Don't error on them.
|
||||||
if (symIndex != 0 && maybeReportUndefined<ELFT>(sym, sec, rel.r_offset))
|
if (symIndex != 0 && maybeReportUndefined(sym, sec, rel.r_offset))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset;
|
const uint8_t *relocatedAddr = sec.data().begin() + rel.r_offset;
|
||||||
@ -1269,12 +1328,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
|
|||||||
// correctly, the IRELATIVE relocations are stored in an array which a
|
// correctly, the IRELATIVE relocations are stored in an array which a
|
||||||
// statically linked executable's startup code must enumerate using the
|
// statically linked executable's startup code must enumerate using the
|
||||||
// linker-defined symbols __rela?_iplt_{start,end}.
|
// linker-defined symbols __rela?_iplt_{start,end}.
|
||||||
//
|
|
||||||
// - An absolute relocation to a non-preemptible ifunc (such as a global
|
|
||||||
// variable containing a pointer to the ifunc) needs to be relocated in
|
|
||||||
// the exact same way as a GOT entry, so we can avoid needing to make the
|
|
||||||
// PLT entry canonical by translating such relocations into IRELATIVE
|
|
||||||
// relocations in the relaIplt.
|
|
||||||
if (!sym.isInPlt()) {
|
if (!sym.isInPlt()) {
|
||||||
// Create PLT and GOTPLT slots for the symbol.
|
// Create PLT and GOTPLT slots for the symbol.
|
||||||
sym.isInIplt = true;
|
sym.isInIplt = true;
|
||||||
@ -1291,17 +1344,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i,
|
|||||||
*directSym);
|
*directSym);
|
||||||
sym.pltIndex = directSym->pltIndex;
|
sym.pltIndex = directSym->pltIndex;
|
||||||
}
|
}
|
||||||
if (expr == R_ABS && addend == 0 && (sec.flags & SHF_WRITE)) {
|
|
||||||
// We might be able to represent this as an IRELATIVE. But we don't know
|
|
||||||
// yet whether some later relocation will make the symbol point to a
|
|
||||||
// canonical PLT, which would make this either a dynamic RELATIVE (PIC) or
|
|
||||||
// static (non-PIC) relocation. So we keep a record of the information
|
|
||||||
// required to process the relocation, and after scanRelocs() has been
|
|
||||||
// called on all relocations, the relocation is resolved by
|
|
||||||
// addIRelativeRelocs().
|
|
||||||
iRelativeRelocs.push_back({type, &sec, offset, &sym});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (needsGot(expr)) {
|
if (needsGot(expr)) {
|
||||||
// Redirect GOT accesses to point to the Igot.
|
// Redirect GOT accesses to point to the Igot.
|
||||||
//
|
//
|
||||||
@ -1362,28 +1404,13 @@ static void scanRelocs(InputSectionBase &sec, ArrayRef<RelTy> rels) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void elf::scanRelocations(InputSectionBase &s) {
|
template <class ELFT> void scanRelocations(InputSectionBase &s) {
|
||||||
if (s.areRelocsRela)
|
if (s.areRelocsRela)
|
||||||
scanRelocs<ELFT>(s, s.relas<ELFT>());
|
scanRelocs<ELFT>(s, s.relas<ELFT>());
|
||||||
else
|
else
|
||||||
scanRelocs<ELFT>(s, s.rels<ELFT>());
|
scanRelocs<ELFT>(s, s.rels<ELFT>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out which representation to use for any absolute relocs to
|
|
||||||
// non-preemptible ifuncs that we visited during scanRelocs().
|
|
||||||
void elf::addIRelativeRelocs() {
|
|
||||||
for (IRelativeReloc &r : iRelativeRelocs) {
|
|
||||||
if (r.sym->type == STT_GNU_IFUNC)
|
|
||||||
in.relaIplt->addReloc(
|
|
||||||
{target->iRelativeRel, r.sec, r.offset, true, r.sym, 0});
|
|
||||||
else if (config->isPic)
|
|
||||||
addRelativeReloc(r.sec, r.offset, r.sym, 0, R_ABS, r.type);
|
|
||||||
else
|
|
||||||
r.sec->relocations.push_back({R_ABS, r.type, r.offset, 0, r.sym});
|
|
||||||
}
|
|
||||||
iRelativeRelocs.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool mergeCmp(const InputSection *a, const InputSection *b) {
|
static bool mergeCmp(const InputSection *a, const InputSection *b) {
|
||||||
// std::merge requires a strict weak ordering.
|
// std::merge requires a strict weak ordering.
|
||||||
if (a->outSecOff < b->outSecOff)
|
if (a->outSecOff < b->outSecOff)
|
||||||
@ -1745,11 +1772,6 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
|
|||||||
if (pass == 0 && target->getThunkSectionSpacing())
|
if (pass == 0 && target->getThunkSectionSpacing())
|
||||||
createInitialThunkSections(outputSections);
|
createInitialThunkSections(outputSections);
|
||||||
|
|
||||||
// With Thunk Size much smaller than branch range we expect to
|
|
||||||
// converge quickly; if we get to 10 something has gone wrong.
|
|
||||||
if (pass == 10)
|
|
||||||
fatal("thunk creation not converged");
|
|
||||||
|
|
||||||
// Create all the Thunks and insert them into synthetic ThunkSections. The
|
// Create all the Thunks and insert them into synthetic ThunkSections. The
|
||||||
// ThunkSections are later inserted back into InputSectionDescriptions.
|
// ThunkSections are later inserted back into InputSectionDescriptions.
|
||||||
// We separate the creation of ThunkSections from the insertion of the
|
// We separate the creation of ThunkSections from the insertion of the
|
||||||
@ -1809,11 +1831,14 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
|
|||||||
return addressesChanged;
|
return addressesChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
template void elf::scanRelocations<ELF32LE>(InputSectionBase &);
|
template void scanRelocations<ELF32LE>(InputSectionBase &);
|
||||||
template void elf::scanRelocations<ELF32BE>(InputSectionBase &);
|
template void scanRelocations<ELF32BE>(InputSectionBase &);
|
||||||
template void elf::scanRelocations<ELF64LE>(InputSectionBase &);
|
template void scanRelocations<ELF64LE>(InputSectionBase &);
|
||||||
template void elf::scanRelocations<ELF64BE>(InputSectionBase &);
|
template void scanRelocations<ELF64BE>(InputSectionBase &);
|
||||||
template void elf::reportUndefinedSymbols<ELF32LE>();
|
template void reportUndefinedSymbols<ELF32LE>();
|
||||||
template void elf::reportUndefinedSymbols<ELF32BE>();
|
template void reportUndefinedSymbols<ELF32BE>();
|
||||||
template void elf::reportUndefinedSymbols<ELF64LE>();
|
template void reportUndefinedSymbols<ELF64LE>();
|
||||||
template void elf::reportUndefinedSymbols<ELF64BE>();
|
template void reportUndefinedSymbols<ELF64BE>();
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -82,7 +82,6 @@ enum RelExpr {
|
|||||||
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
|
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
|
||||||
R_AARCH64_TLSDESC_PAGE,
|
R_AARCH64_TLSDESC_PAGE,
|
||||||
R_ARM_SBREL,
|
R_ARM_SBREL,
|
||||||
R_HEXAGON_GOT,
|
|
||||||
R_MIPS_GOTREL,
|
R_MIPS_GOTREL,
|
||||||
R_MIPS_GOT_GP,
|
R_MIPS_GOT_GP,
|
||||||
R_MIPS_GOT_GP_PC,
|
R_MIPS_GOT_GP_PC,
|
||||||
|
@ -36,9 +36,9 @@
|
|||||||
#include "llvm/ADT/Twine.h"
|
#include "llvm/ADT/Twine.h"
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
// Returns a whole line containing the current token.
|
// Returns a whole line containing the current token.
|
||||||
StringRef ScriptLexer::getLine() {
|
StringRef ScriptLexer::getLine() {
|
||||||
StringRef s = getCurrentMB().getBuffer();
|
StringRef s = getCurrentMB().getBuffer();
|
||||||
@ -298,3 +298,6 @@ MemoryBufferRef ScriptLexer::getCurrentMB() {
|
|||||||
return mb;
|
return mb;
|
||||||
llvm_unreachable("getCurrentMB: failed to find a token");
|
llvm_unreachable("getCurrentMB: failed to find a token");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -37,9 +37,9 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
namespace {
|
namespace {
|
||||||
class ScriptParser final : ScriptLexer {
|
class ScriptParser final : ScriptLexer {
|
||||||
public:
|
public:
|
||||||
@ -720,7 +720,7 @@ Expr ScriptParser::readAssert() {
|
|||||||
|
|
||||||
return [=] {
|
return [=] {
|
||||||
if (!e().getValue())
|
if (!e().getValue())
|
||||||
error(msg);
|
errorOrWarn(msg);
|
||||||
return script->getDot();
|
return script->getDot();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1268,7 +1268,7 @@ Expr ScriptParser::readPrimary() {
|
|||||||
return [=] { return cmd->size; };
|
return [=] { return cmd->size; };
|
||||||
}
|
}
|
||||||
if (tok == "SIZEOF_HEADERS")
|
if (tok == "SIZEOF_HEADERS")
|
||||||
return [=] { return elf::getHeaderSize(); };
|
return [=] { return getHeaderSize(); };
|
||||||
|
|
||||||
// Tok is the dot.
|
// Tok is the dot.
|
||||||
if (tok == ".")
|
if (tok == ".")
|
||||||
@ -1344,16 +1344,10 @@ void ScriptParser::readAnonymousDeclaration() {
|
|||||||
std::vector<SymbolVersion> locals;
|
std::vector<SymbolVersion> locals;
|
||||||
std::vector<SymbolVersion> globals;
|
std::vector<SymbolVersion> globals;
|
||||||
std::tie(locals, globals) = readSymbols();
|
std::tie(locals, globals) = readSymbols();
|
||||||
|
for (const SymbolVersion &pat : locals)
|
||||||
for (SymbolVersion v : locals) {
|
config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat);
|
||||||
if (v.name == "*")
|
for (const SymbolVersion &pat : globals)
|
||||||
config->defaultSymbolVersion = VER_NDX_LOCAL;
|
config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(pat);
|
||||||
else
|
|
||||||
config->versionScriptLocals.push_back(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (SymbolVersion v : globals)
|
|
||||||
config->versionScriptGlobals.push_back(v);
|
|
||||||
|
|
||||||
expect(";");
|
expect(";");
|
||||||
}
|
}
|
||||||
@ -1365,22 +1359,14 @@ void ScriptParser::readVersionDeclaration(StringRef verStr) {
|
|||||||
std::vector<SymbolVersion> locals;
|
std::vector<SymbolVersion> locals;
|
||||||
std::vector<SymbolVersion> globals;
|
std::vector<SymbolVersion> globals;
|
||||||
std::tie(locals, globals) = readSymbols();
|
std::tie(locals, globals) = readSymbols();
|
||||||
|
for (const SymbolVersion &pat : locals)
|
||||||
for (SymbolVersion v : locals) {
|
config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat);
|
||||||
if (v.name == "*")
|
|
||||||
config->defaultSymbolVersion = VER_NDX_LOCAL;
|
|
||||||
else
|
|
||||||
config->versionScriptLocals.push_back(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new version definition and add that to the global symbols.
|
// Create a new version definition and add that to the global symbols.
|
||||||
VersionDefinition ver;
|
VersionDefinition ver;
|
||||||
ver.name = verStr;
|
ver.name = verStr;
|
||||||
ver.globals = globals;
|
ver.patterns = globals;
|
||||||
|
ver.id = config->versionDefinitions.size();
|
||||||
// User-defined version number starts from 2 because 0 and 1 are
|
|
||||||
// reserved for VER_NDX_LOCAL and VER_NDX_GLOBAL, respectively.
|
|
||||||
ver.id = config->versionDefinitions.size() + 2;
|
|
||||||
config->versionDefinitions.push_back(ver);
|
config->versionDefinitions.push_back(ver);
|
||||||
|
|
||||||
// Each version may have a parent version. For example, "Ver2"
|
// Each version may have a parent version. For example, "Ver2"
|
||||||
@ -1525,18 +1511,19 @@ std::pair<uint32_t, uint32_t> ScriptParser::readMemoryAttributes() {
|
|||||||
return {flags, negFlags};
|
return {flags, negFlags};
|
||||||
}
|
}
|
||||||
|
|
||||||
void elf::readLinkerScript(MemoryBufferRef mb) {
|
void readLinkerScript(MemoryBufferRef mb) {
|
||||||
ScriptParser(mb).readLinkerScript();
|
ScriptParser(mb).readLinkerScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
void elf::readVersionScript(MemoryBufferRef mb) {
|
void readVersionScript(MemoryBufferRef mb) {
|
||||||
ScriptParser(mb).readVersionScript();
|
ScriptParser(mb).readVersionScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
void elf::readDynamicList(MemoryBufferRef mb) {
|
void readDynamicList(MemoryBufferRef mb) { ScriptParser(mb).readDynamicList(); }
|
||||||
ScriptParser(mb).readDynamicList();
|
|
||||||
}
|
|
||||||
|
|
||||||
void elf::readDefsym(StringRef name, MemoryBufferRef mb) {
|
void readDefsym(StringRef name, MemoryBufferRef mb) {
|
||||||
ScriptParser(mb).readDefsym(name);
|
ScriptParser(mb).readDefsym(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -27,10 +27,9 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
SymbolTable *symtab;
|
||||||
SymbolTable *elf::symtab;
|
|
||||||
|
|
||||||
void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
|
void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
|
||||||
// Swap symbols as instructed by -wrap.
|
// Swap symbols as instructed by -wrap.
|
||||||
@ -71,21 +70,26 @@ Symbol *SymbolTable::insert(StringRef name) {
|
|||||||
Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
|
||||||
symVector.push_back(sym);
|
symVector.push_back(sym);
|
||||||
|
|
||||||
|
// *sym was not initialized by a constructor. Fields that may get referenced
|
||||||
|
// when it is a placeholder must be initialized here.
|
||||||
sym->setName(name);
|
sym->setName(name);
|
||||||
sym->symbolKind = Symbol::PlaceholderKind;
|
sym->symbolKind = Symbol::PlaceholderKind;
|
||||||
sym->versionId = config->defaultSymbolVersion;
|
sym->versionId = VER_NDX_GLOBAL;
|
||||||
sym->visibility = STV_DEFAULT;
|
sym->visibility = STV_DEFAULT;
|
||||||
sym->isUsedInRegularObj = false;
|
sym->isUsedInRegularObj = false;
|
||||||
sym->exportDynamic = false;
|
sym->exportDynamic = false;
|
||||||
|
sym->inDynamicList = false;
|
||||||
sym->canInline = true;
|
sym->canInline = true;
|
||||||
|
sym->referenced = false;
|
||||||
|
sym->traced = false;
|
||||||
sym->scriptDefined = false;
|
sym->scriptDefined = false;
|
||||||
sym->partition = 1;
|
sym->partition = 1;
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
Symbol *SymbolTable::addSymbol(const Symbol &New) {
|
Symbol *SymbolTable::addSymbol(const Symbol &newSym) {
|
||||||
Symbol *sym = symtab->insert(New.getName());
|
Symbol *sym = symtab->insert(newSym.getName());
|
||||||
sym->resolve(New);
|
sym->resolve(newSym);
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,10 +122,7 @@ StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
|
|||||||
for (Symbol *sym : symVector) {
|
for (Symbol *sym : symVector) {
|
||||||
if (!sym->isDefined() && !sym->isCommon())
|
if (!sym->isDefined() && !sym->isCommon())
|
||||||
continue;
|
continue;
|
||||||
if (Optional<std::string> s = demangleItanium(sym->getName()))
|
(*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
|
||||||
(*demangledSyms)[*s].push_back(sym);
|
|
||||||
else
|
|
||||||
(*demangledSyms)[sym->getName()].push_back(sym);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return *demangledSyms;
|
return *demangledSyms;
|
||||||
@ -162,12 +163,8 @@ void SymbolTable::handleDynamicList() {
|
|||||||
else
|
else
|
||||||
syms = findByVersion(ver);
|
syms = findByVersion(ver);
|
||||||
|
|
||||||
for (Symbol *b : syms) {
|
for (Symbol *sym : syms)
|
||||||
if (!config->shared)
|
sym->inDynamicList = true;
|
||||||
b->exportDynamic = true;
|
|
||||||
else if (b->includeInDynsym())
|
|
||||||
b->isPreemptible = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +189,7 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
|
|||||||
return "VER_NDX_LOCAL";
|
return "VER_NDX_LOCAL";
|
||||||
if (ver == VER_NDX_GLOBAL)
|
if (ver == VER_NDX_GLOBAL)
|
||||||
return "VER_NDX_GLOBAL";
|
return "VER_NDX_GLOBAL";
|
||||||
return ("version '" + config->versionDefinitions[ver - 2].name + "'").str();
|
return ("version '" + config->versionDefinitions[ver].name + "'").str();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assign the version.
|
// Assign the version.
|
||||||
@ -203,8 +200,12 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
|
|||||||
if (sym->getName().contains('@'))
|
if (sym->getName().contains('@'))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (sym->versionId == config->defaultSymbolVersion)
|
// If the version has not been assigned, verdefIndex is -1. Use an arbitrary
|
||||||
|
// number (0) to indicate the version has been assigned.
|
||||||
|
if (sym->verdefIndex == UINT32_C(-1)) {
|
||||||
|
sym->verdefIndex = 0;
|
||||||
sym->versionId = versionId;
|
sym->versionId = versionId;
|
||||||
|
}
|
||||||
if (sym->versionId == versionId)
|
if (sym->versionId == versionId)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -214,15 +215,14 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
|
void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
|
||||||
if (!ver.hasWildcard)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Exact matching takes precendence over fuzzy matching,
|
// Exact matching takes precendence over fuzzy matching,
|
||||||
// so we set a version to a symbol only if no version has been assigned
|
// so we set a version to a symbol only if no version has been assigned
|
||||||
// to the symbol. This behavior is compatible with GNU.
|
// to the symbol. This behavior is compatible with GNU.
|
||||||
for (Symbol *b : findAllByVersion(ver))
|
for (Symbol *sym : findAllByVersion(ver))
|
||||||
if (b->versionId == config->defaultSymbolVersion)
|
if (sym->verdefIndex == UINT32_C(-1)) {
|
||||||
b->versionId = versionId;
|
sym->verdefIndex = 0;
|
||||||
|
sym->versionId = versionId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function processes version scripts by updating the versionId
|
// This function processes version scripts by updating the versionId
|
||||||
@ -233,26 +233,24 @@ void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) {
|
|||||||
void SymbolTable::scanVersionScript() {
|
void SymbolTable::scanVersionScript() {
|
||||||
// First, we assign versions to exact matching symbols,
|
// First, we assign versions to exact matching symbols,
|
||||||
// i.e. version definitions not containing any glob meta-characters.
|
// i.e. version definitions not containing any glob meta-characters.
|
||||||
for (SymbolVersion &ver : config->versionScriptGlobals)
|
|
||||||
assignExactVersion(ver, VER_NDX_GLOBAL, "global");
|
|
||||||
for (SymbolVersion &ver : config->versionScriptLocals)
|
|
||||||
assignExactVersion(ver, VER_NDX_LOCAL, "local");
|
|
||||||
for (VersionDefinition &v : config->versionDefinitions)
|
for (VersionDefinition &v : config->versionDefinitions)
|
||||||
for (SymbolVersion &ver : v.globals)
|
for (SymbolVersion &pat : v.patterns)
|
||||||
assignExactVersion(ver, v.id, v.name);
|
assignExactVersion(pat, v.id, v.name);
|
||||||
|
|
||||||
// Next, we assign versions to fuzzy matching symbols,
|
// Next, assign versions to wildcards that are not "*". Note that because the
|
||||||
// i.e. version definitions containing glob meta-characters.
|
// last match takes precedence over previous matches, we iterate over the
|
||||||
for (SymbolVersion &ver : config->versionScriptGlobals)
|
// definitions in the reverse order.
|
||||||
assignWildcardVersion(ver, VER_NDX_GLOBAL);
|
|
||||||
for (SymbolVersion &ver : config->versionScriptLocals)
|
|
||||||
assignWildcardVersion(ver, VER_NDX_LOCAL);
|
|
||||||
|
|
||||||
// Note that because the last match takes precedence over previous matches,
|
|
||||||
// we iterate over the definitions in the reverse order.
|
|
||||||
for (VersionDefinition &v : llvm::reverse(config->versionDefinitions))
|
for (VersionDefinition &v : llvm::reverse(config->versionDefinitions))
|
||||||
for (SymbolVersion &ver : v.globals)
|
for (SymbolVersion &pat : v.patterns)
|
||||||
assignWildcardVersion(ver, v.id);
|
if (pat.hasWildcard && pat.name != "*")
|
||||||
|
assignWildcardVersion(pat, v.id);
|
||||||
|
|
||||||
|
// Then, assign versions to "*". In GNU linkers they have lower priority than
|
||||||
|
// other wildcards.
|
||||||
|
for (VersionDefinition &v : config->versionDefinitions)
|
||||||
|
for (SymbolVersion &pat : v.patterns)
|
||||||
|
if (pat.hasWildcard && pat.name == "*")
|
||||||
|
assignWildcardVersion(pat, v.id);
|
||||||
|
|
||||||
// Symbol themselves might know their versions because symbols
|
// Symbol themselves might know their versions because symbols
|
||||||
// can contain versions in the form of <name>@<version>.
|
// can contain versions in the form of <name>@<version>.
|
||||||
@ -262,7 +260,10 @@ void SymbolTable::scanVersionScript() {
|
|||||||
|
|
||||||
// isPreemptible is false at this point. To correctly compute the binding of a
|
// isPreemptible is false at this point. To correctly compute the binding of a
|
||||||
// Defined (which is used by includeInDynsym()), we need to know if it is
|
// Defined (which is used by includeInDynsym()), we need to know if it is
|
||||||
// VER_NDX_LOCAL or not. If defaultSymbolVersion is VER_NDX_LOCAL, we should
|
// VER_NDX_LOCAL or not. Compute symbol versions before handling
|
||||||
// compute symbol versions before handling --dynamic-list.
|
// --dynamic-list.
|
||||||
handleDynamicList();
|
handleDynamicList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -43,7 +43,7 @@ class SymbolTable {
|
|||||||
|
|
||||||
Symbol *insert(StringRef name);
|
Symbol *insert(StringRef name);
|
||||||
|
|
||||||
Symbol *addSymbol(const Symbol &New);
|
Symbol *addSymbol(const Symbol &newSym);
|
||||||
|
|
||||||
void scanVersionScript();
|
void scanVersionScript();
|
||||||
|
|
||||||
|
@ -23,9 +23,20 @@ using namespace llvm;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
// Returns a symbol for an error message.
|
||||||
|
static std::string demangle(StringRef symName) {
|
||||||
|
if (elf::config->demangle)
|
||||||
|
return demangleItanium(symName);
|
||||||
|
return symName;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toString(const elf::Symbol &b) { return demangle(b.getName()); }
|
||||||
|
std::string toELFString(const Archive::Symbol &b) {
|
||||||
|
return demangle(b.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace elf {
|
||||||
Defined *ElfSym::bss;
|
Defined *ElfSym::bss;
|
||||||
Defined *ElfSym::etext1;
|
Defined *ElfSym::etext1;
|
||||||
Defined *ElfSym::etext2;
|
Defined *ElfSym::etext2;
|
||||||
@ -42,20 +53,6 @@ Defined *ElfSym::relaIpltEnd;
|
|||||||
Defined *ElfSym::riscvGlobalPointer;
|
Defined *ElfSym::riscvGlobalPointer;
|
||||||
Defined *ElfSym::tlsModuleBase;
|
Defined *ElfSym::tlsModuleBase;
|
||||||
|
|
||||||
// Returns a symbol for an error message.
|
|
||||||
static std::string demangle(StringRef symName) {
|
|
||||||
if (config->demangle)
|
|
||||||
if (Optional<std::string> s = demangleItanium(symName))
|
|
||||||
return *s;
|
|
||||||
return symName;
|
|
||||||
}
|
|
||||||
namespace lld {
|
|
||||||
std::string toString(const Symbol &b) { return demangle(b.getName()); }
|
|
||||||
std::string toELFString(const Archive::Symbol &b) {
|
|
||||||
return demangle(b.getName());
|
|
||||||
}
|
|
||||||
} // namespace lld
|
|
||||||
|
|
||||||
static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
|
static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
|
||||||
switch (sym.kind()) {
|
switch (sym.kind()) {
|
||||||
case Symbol::DefinedKind: {
|
case Symbol::DefinedKind: {
|
||||||
@ -227,7 +224,7 @@ void Symbol::parseSymbolVersion() {
|
|||||||
if (isDefault)
|
if (isDefault)
|
||||||
verstr = verstr.substr(1);
|
verstr = verstr.substr(1);
|
||||||
|
|
||||||
for (VersionDefinition &ver : config->versionDefinitions) {
|
for (const VersionDefinition &ver : namedVersionDefs()) {
|
||||||
if (ver.name != verstr)
|
if (ver.name != verstr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -276,9 +273,8 @@ MemoryBufferRef LazyArchive::getMemberBuffer() {
|
|||||||
uint8_t Symbol::computeBinding() const {
|
uint8_t Symbol::computeBinding() const {
|
||||||
if (config->relocatable)
|
if (config->relocatable)
|
||||||
return binding;
|
return binding;
|
||||||
if (visibility != STV_DEFAULT && visibility != STV_PROTECTED)
|
if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
|
||||||
return STB_LOCAL;
|
versionId == VER_NDX_LOCAL)
|
||||||
if (versionId == VER_NDX_LOCAL && isDefined() && !isPreemptible)
|
|
||||||
return STB_LOCAL;
|
return STB_LOCAL;
|
||||||
if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
|
if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
|
||||||
return STB_GLOBAL;
|
return STB_GLOBAL;
|
||||||
@ -296,11 +292,11 @@ bool Symbol::includeInDynsym() const {
|
|||||||
if (isUndefWeak() && config->pie && sharedFiles.empty())
|
if (isUndefWeak() && config->pie && sharedFiles.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return isUndefined() || isShared() || exportDynamic;
|
return isUndefined() || isShared() || exportDynamic || inDynamicList;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print out a log message for --trace-symbol.
|
// Print out a log message for --trace-symbol.
|
||||||
void elf::printTraceSymbol(const Symbol *sym) {
|
void printTraceSymbol(const Symbol *sym) {
|
||||||
std::string s;
|
std::string s;
|
||||||
if (sym->isUndefined())
|
if (sym->isUndefined())
|
||||||
s = ": reference to ";
|
s = ": reference to ";
|
||||||
@ -316,7 +312,7 @@ void elf::printTraceSymbol(const Symbol *sym) {
|
|||||||
message(toString(sym->file) + s + sym->getName());
|
message(toString(sym->file) + s + sym->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
|
void maybeWarnUnorderableSymbol(const Symbol *sym) {
|
||||||
if (!config->warnSymbolOrdering)
|
if (!config->warnSymbolOrdering)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -492,17 +488,13 @@ void Symbol::resolveUndefined(const Undefined &other) {
|
|||||||
if (dyn_cast_or_null<SharedFile>(other.file))
|
if (dyn_cast_or_null<SharedFile>(other.file))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isUndefined()) {
|
if (isUndefined() || isShared()) {
|
||||||
// The binding may "upgrade" from weak to non-weak.
|
// The binding will be weak if there is at least one reference and all are
|
||||||
if (other.binding != STB_WEAK)
|
// weak. The binding has one opportunity to change to weak: if the first
|
||||||
|
// reference is weak.
|
||||||
|
if (other.binding != STB_WEAK || !referenced)
|
||||||
binding = other.binding;
|
binding = other.binding;
|
||||||
} else if (auto *s = dyn_cast<SharedSymbol>(this)) {
|
referenced = true;
|
||||||
// The binding of a SharedSymbol will be weak if there is at least one
|
|
||||||
// reference and all are weak. The binding has one opportunity to change to
|
|
||||||
// weak: if the first reference is weak.
|
|
||||||
if (other.binding != STB_WEAK || !s->referenced)
|
|
||||||
binding = other.binding;
|
|
||||||
s->referenced = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,7 +552,7 @@ int Symbol::compare(const Symbol *other) const {
|
|||||||
auto *oldSym = cast<Defined>(this);
|
auto *oldSym = cast<Defined>(this);
|
||||||
auto *newSym = cast<Defined>(other);
|
auto *newSym = cast<Defined>(other);
|
||||||
|
|
||||||
if (other->file && isa<BitcodeFile>(other->file))
|
if (dyn_cast_or_null<BitcodeFile>(other->file))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!oldSym->section && !newSym->section && oldSym->value == newSym->value &&
|
if (!oldSym->section && !newSym->section && oldSym->value == newSym->value &&
|
||||||
@ -658,6 +650,9 @@ void Symbol::resolveShared(const SharedSymbol &other) {
|
|||||||
uint8_t bind = binding;
|
uint8_t bind = binding;
|
||||||
replace(other);
|
replace(other);
|
||||||
binding = bind;
|
binding = bind;
|
||||||
cast<SharedSymbol>(this)->referenced = true;
|
referenced = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -21,6 +21,13 @@
|
|||||||
#include "llvm/Object/ELF.h"
|
#include "llvm/Object/ELF.h"
|
||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
|
std::string toString(const elf::Symbol &);
|
||||||
|
|
||||||
|
// There are two different ways to convert an Archive::Symbol to a string:
|
||||||
|
// One for Microsoft name mangling and one for Itanium name mangling.
|
||||||
|
// Call the functions toCOFFString and toELFString, not just toString.
|
||||||
|
std::string toELFString(const llvm::object::Archive::Symbol &);
|
||||||
|
|
||||||
namespace elf {
|
namespace elf {
|
||||||
class CommonSymbol;
|
class CommonSymbol;
|
||||||
class Defined;
|
class Defined;
|
||||||
@ -30,16 +37,6 @@ class LazyObject;
|
|||||||
class SharedSymbol;
|
class SharedSymbol;
|
||||||
class Symbol;
|
class Symbol;
|
||||||
class Undefined;
|
class Undefined;
|
||||||
} // namespace elf
|
|
||||||
|
|
||||||
std::string toString(const elf::Symbol &);
|
|
||||||
|
|
||||||
// There are two different ways to convert an Archive::Symbol to a string:
|
|
||||||
// One for Microsoft name mangling and one for Itanium name mangling.
|
|
||||||
// Call the functions toCOFFString and toELFString, not just toString.
|
|
||||||
std::string toELFString(const elf::Archive::Symbol &);
|
|
||||||
|
|
||||||
namespace elf {
|
|
||||||
|
|
||||||
// This is a StringRef-like container that doesn't run strlen().
|
// This is a StringRef-like container that doesn't run strlen().
|
||||||
//
|
//
|
||||||
@ -108,29 +105,43 @@ class Symbol {
|
|||||||
|
|
||||||
// Symbol visibility. This is the computed minimum visibility of all
|
// Symbol visibility. This is the computed minimum visibility of all
|
||||||
// observed non-DSO symbols.
|
// observed non-DSO symbols.
|
||||||
uint8_t visibility : 2;
|
unsigned visibility : 2;
|
||||||
|
|
||||||
// True if the symbol was used for linking and thus need to be added to the
|
// True if the symbol was used for linking and thus need to be added to the
|
||||||
// output file's symbol table. This is true for all symbols except for
|
// output file's symbol table. This is true for all symbols except for
|
||||||
// unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
|
// unreferenced DSO symbols, lazy (archive) symbols, and bitcode symbols that
|
||||||
// are unreferenced except by other bitcode objects.
|
// are unreferenced except by other bitcode objects.
|
||||||
uint8_t isUsedInRegularObj : 1;
|
unsigned isUsedInRegularObj : 1;
|
||||||
|
|
||||||
// If this flag is true and the symbol has protected or default visibility, it
|
// Used by a Defined symbol with protected or default visibility, to record
|
||||||
// will appear in .dynsym. This flag is set by interposable DSO symbols in
|
// whether it is required to be exported into .dynsym. This is set when any of
|
||||||
// executables, by most symbols in DSOs and executables built with
|
// the following conditions hold:
|
||||||
// --export-dynamic, and by dynamic lists.
|
//
|
||||||
uint8_t exportDynamic : 1;
|
// - If there is an interposable symbol from a DSO.
|
||||||
|
// - If -shared or --export-dynamic is specified, any symbol in an object
|
||||||
|
// file/bitcode sets this property, unless suppressed by LTO
|
||||||
|
// canBeOmittedFromSymbolTable().
|
||||||
|
unsigned exportDynamic : 1;
|
||||||
|
|
||||||
|
// True if the symbol is in the --dynamic-list file. A Defined symbol with
|
||||||
|
// protected or default visibility with this property is required to be
|
||||||
|
// exported into .dynsym.
|
||||||
|
unsigned inDynamicList : 1;
|
||||||
|
|
||||||
// False if LTO shouldn't inline whatever this symbol points to. If a symbol
|
// False if LTO shouldn't inline whatever this symbol points to. If a symbol
|
||||||
// is overwritten after LTO, LTO shouldn't inline the symbol because it
|
// is overwritten after LTO, LTO shouldn't inline the symbol because it
|
||||||
// doesn't know the final contents of the symbol.
|
// doesn't know the final contents of the symbol.
|
||||||
uint8_t canInline : 1;
|
unsigned canInline : 1;
|
||||||
|
|
||||||
|
// Used by Undefined and SharedSymbol to track if there has been at least one
|
||||||
|
// undefined reference to the symbol. The binding may change to STB_WEAK if
|
||||||
|
// the first undefined reference from a non-shared object is weak.
|
||||||
|
unsigned referenced : 1;
|
||||||
|
|
||||||
// True if this symbol is specified by --trace-symbol option.
|
// True if this symbol is specified by --trace-symbol option.
|
||||||
uint8_t traced : 1;
|
unsigned traced : 1;
|
||||||
|
|
||||||
inline void replace(const Symbol &New);
|
inline void replace(const Symbol &newSym);
|
||||||
|
|
||||||
bool includeInDynsym() const;
|
bool includeInDynsym() const;
|
||||||
uint8_t computeBinding() const;
|
uint8_t computeBinding() const;
|
||||||
@ -228,36 +239,37 @@ class Symbol {
|
|||||||
: file(file), nameData(name.data), nameSize(name.size), binding(binding),
|
: file(file), nameData(name.data), nameSize(name.size), binding(binding),
|
||||||
type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3),
|
type(type), stOther(stOther), symbolKind(k), visibility(stOther & 3),
|
||||||
isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind),
|
isUsedInRegularObj(!file || file->kind() == InputFile::ObjKind),
|
||||||
exportDynamic(isExportDynamic(k, visibility)), canInline(false),
|
exportDynamic(isExportDynamic(k, visibility)), inDynamicList(false),
|
||||||
traced(false), needsPltAddr(false), isInIplt(false), gotInIgot(false),
|
canInline(false), referenced(false), traced(false), needsPltAddr(false),
|
||||||
isPreemptible(false), used(!config->gcSections), needsTocRestore(false),
|
isInIplt(false), gotInIgot(false), isPreemptible(false),
|
||||||
|
used(!config->gcSections), needsTocRestore(false),
|
||||||
scriptDefined(false) {}
|
scriptDefined(false) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// True the symbol should point to its PLT entry.
|
// True the symbol should point to its PLT entry.
|
||||||
// For SharedSymbol only.
|
// For SharedSymbol only.
|
||||||
uint8_t needsPltAddr : 1;
|
unsigned needsPltAddr : 1;
|
||||||
|
|
||||||
// True if this symbol is in the Iplt sub-section of the Plt and the Igot
|
// True if this symbol is in the Iplt sub-section of the Plt and the Igot
|
||||||
// sub-section of the .got.plt or .got.
|
// sub-section of the .got.plt or .got.
|
||||||
uint8_t isInIplt : 1;
|
unsigned isInIplt : 1;
|
||||||
|
|
||||||
// True if this symbol needs a GOT entry and its GOT entry is actually in
|
// True if this symbol needs a GOT entry and its GOT entry is actually in
|
||||||
// Igot. This will be true only for certain non-preemptible ifuncs.
|
// Igot. This will be true only for certain non-preemptible ifuncs.
|
||||||
uint8_t gotInIgot : 1;
|
unsigned gotInIgot : 1;
|
||||||
|
|
||||||
// True if this symbol is preemptible at load time.
|
// True if this symbol is preemptible at load time.
|
||||||
uint8_t isPreemptible : 1;
|
unsigned isPreemptible : 1;
|
||||||
|
|
||||||
// True if an undefined or shared symbol is used from a live section.
|
// True if an undefined or shared symbol is used from a live section.
|
||||||
uint8_t used : 1;
|
unsigned used : 1;
|
||||||
|
|
||||||
// True if a call to this symbol needs to be followed by a restore of the
|
// True if a call to this symbol needs to be followed by a restore of the
|
||||||
// PPC64 toc pointer.
|
// PPC64 toc pointer.
|
||||||
uint8_t needsTocRestore : 1;
|
unsigned needsTocRestore : 1;
|
||||||
|
|
||||||
// True if this symbol is defined by a linker script.
|
// True if this symbol is defined by a linker script.
|
||||||
uint8_t scriptDefined : 1;
|
unsigned scriptDefined : 1;
|
||||||
|
|
||||||
// The partition whose dynamic symbol table contains this symbol's definition.
|
// The partition whose dynamic symbol table contains this symbol's definition.
|
||||||
uint8_t partition = 1;
|
uint8_t partition = 1;
|
||||||
@ -367,11 +379,6 @@ class SharedSymbol : public Symbol {
|
|||||||
uint64_t value; // st_value
|
uint64_t value; // st_value
|
||||||
uint64_t size; // st_size
|
uint64_t size; // st_size
|
||||||
uint32_t alignment;
|
uint32_t alignment;
|
||||||
|
|
||||||
// This is true if there has been at least one undefined reference to the
|
|
||||||
// symbol. The binding may change to STB_WEAK if the first undefined reference
|
|
||||||
// is weak.
|
|
||||||
bool referenced = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// LazyArchive and LazyObject represent a symbols that is not yet in the link,
|
// LazyArchive and LazyObject represent a symbols that is not yet in the link,
|
||||||
@ -511,7 +518,7 @@ size_t Symbol::getSymbolSize() const {
|
|||||||
// replace() replaces "this" object with a given symbol by memcpy'ing
|
// replace() replaces "this" object with a given symbol by memcpy'ing
|
||||||
// it over to "this". This function is called as a result of name
|
// it over to "this". This function is called as a result of name
|
||||||
// resolution, e.g. to replace an undefind symbol with a defined symbol.
|
// resolution, e.g. to replace an undefind symbol with a defined symbol.
|
||||||
void Symbol::replace(const Symbol &New) {
|
void Symbol::replace(const Symbol &newSym) {
|
||||||
using llvm::ELF::STT_TLS;
|
using llvm::ELF::STT_TLS;
|
||||||
|
|
||||||
// Symbols representing thread-local variables must be referenced by
|
// Symbols representing thread-local variables must be referenced by
|
||||||
@ -519,22 +526,23 @@ void Symbol::replace(const Symbol &New) {
|
|||||||
// non-TLS relocations, so there's a clear distinction between TLS
|
// non-TLS relocations, so there's a clear distinction between TLS
|
||||||
// and non-TLS symbols. It is an error if the same symbol is defined
|
// and non-TLS symbols. It is an error if the same symbol is defined
|
||||||
// as a TLS symbol in one file and as a non-TLS symbol in other file.
|
// as a TLS symbol in one file and as a non-TLS symbol in other file.
|
||||||
if (symbolKind != PlaceholderKind && !isLazy() && !New.isLazy()) {
|
if (symbolKind != PlaceholderKind && !isLazy() && !newSym.isLazy() &&
|
||||||
bool tlsMismatch = (type == STT_TLS && New.type != STT_TLS) ||
|
(type == STT_TLS) != (newSym.type == STT_TLS))
|
||||||
(type != STT_TLS && New.type == STT_TLS);
|
error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
|
||||||
if (tlsMismatch)
|
toString(newSym.file) + "\n>>> defined in " + toString(file));
|
||||||
error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
|
|
||||||
toString(New.file) + "\n>>> defined in " + toString(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
Symbol old = *this;
|
Symbol old = *this;
|
||||||
memcpy(this, &New, New.getSymbolSize());
|
memcpy(this, &newSym, newSym.getSymbolSize());
|
||||||
|
|
||||||
|
// old may be a placeholder. The referenced fields must be initialized in
|
||||||
|
// SymbolTable::insert.
|
||||||
versionId = old.versionId;
|
versionId = old.versionId;
|
||||||
visibility = old.visibility;
|
visibility = old.visibility;
|
||||||
isUsedInRegularObj = old.isUsedInRegularObj;
|
isUsedInRegularObj = old.isUsedInRegularObj;
|
||||||
exportDynamic = old.exportDynamic;
|
exportDynamic = old.exportDynamic;
|
||||||
|
inDynamicList = old.inDynamicList;
|
||||||
canInline = old.canInline;
|
canInline = old.canInline;
|
||||||
|
referenced = old.referenced;
|
||||||
traced = old.traced;
|
traced = old.traced;
|
||||||
isPreemptible = old.isPreemptible;
|
isPreemptible = old.isPreemptible;
|
||||||
scriptDefined = old.scriptDefined;
|
scriptDefined = old.scriptDefined;
|
||||||
|
@ -45,13 +45,12 @@ using namespace llvm::ELF;
|
|||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::support;
|
using namespace llvm::support;
|
||||||
|
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
using llvm::support::endian::read32le;
|
using llvm::support::endian::read32le;
|
||||||
using llvm::support::endian::write32le;
|
using llvm::support::endian::write32le;
|
||||||
using llvm::support::endian::write64le;
|
using llvm::support::endian::write64le;
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
namespace elf {
|
||||||
constexpr size_t MergeNoTailSection::numShards;
|
constexpr size_t MergeNoTailSection::numShards;
|
||||||
|
|
||||||
static uint64_t readUint(uint8_t *buf) {
|
static uint64_t readUint(uint8_t *buf) {
|
||||||
@ -82,7 +81,7 @@ static ArrayRef<uint8_t> getVersion() {
|
|||||||
// With this feature, you can identify LLD-generated binaries easily
|
// With this feature, you can identify LLD-generated binaries easily
|
||||||
// by "readelf --string-dump .comment <file>".
|
// by "readelf --string-dump .comment <file>".
|
||||||
// The returned object is a mergeable string section.
|
// The returned object is a mergeable string section.
|
||||||
MergeInputSection *elf::createCommentSection() {
|
MergeInputSection *createCommentSection() {
|
||||||
return make<MergeInputSection>(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1,
|
return make<MergeInputSection>(SHF_MERGE | SHF_STRINGS, SHT_PROGBITS, 1,
|
||||||
getVersion(), ".comment");
|
getVersion(), ".comment");
|
||||||
}
|
}
|
||||||
@ -138,7 +137,7 @@ MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::create() {
|
|||||||
flags.ases |= s->ases;
|
flags.ases |= s->ases;
|
||||||
flags.flags1 |= s->flags1;
|
flags.flags1 |= s->flags1;
|
||||||
flags.flags2 |= s->flags2;
|
flags.flags2 |= s->flags2;
|
||||||
flags.fp_abi = elf::getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename);
|
flags.fp_abi = getMipsFpAbiFlag(flags.fp_abi, s->fp_abi, filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (create)
|
if (create)
|
||||||
@ -252,19 +251,17 @@ MipsReginfoSection<ELFT> *MipsReginfoSection<ELFT>::create() {
|
|||||||
return make<MipsReginfoSection<ELFT>>(reginfo);
|
return make<MipsReginfoSection<ELFT>>(reginfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputSection *elf::createInterpSection() {
|
InputSection *createInterpSection() {
|
||||||
// StringSaver guarantees that the returned string ends with '\0'.
|
// StringSaver guarantees that the returned string ends with '\0'.
|
||||||
StringRef s = saver.save(config->dynamicLinker);
|
StringRef s = saver.save(config->dynamicLinker);
|
||||||
ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
|
ArrayRef<uint8_t> contents = {(const uint8_t *)s.data(), s.size() + 1};
|
||||||
|
|
||||||
auto *sec = make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents,
|
return make<InputSection>(nullptr, SHF_ALLOC, SHT_PROGBITS, 1, contents,
|
||||||
".interp");
|
".interp");
|
||||||
sec->markLive();
|
|
||||||
return sec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Defined *elf::addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
|
Defined *addSyntheticLocal(StringRef name, uint8_t type, uint64_t value,
|
||||||
uint64_t size, InputSectionBase §ion) {
|
uint64_t size, InputSectionBase §ion) {
|
||||||
auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type,
|
auto *s = make<Defined>(section.file, name, STB_LOCAL, STV_DEFAULT, type,
|
||||||
value, size, §ion);
|
value, size, §ion);
|
||||||
if (in.symTab)
|
if (in.symTab)
|
||||||
@ -402,7 +399,7 @@ bool EhFrameSection::isFdeLive(EhSectionPiece &fde, ArrayRef<RelTy> rels) {
|
|||||||
// a list of FDEs. This function searches an existing CIE or create a new
|
// a list of FDEs. This function searches an existing CIE or create a new
|
||||||
// one and associates FDEs to the CIE.
|
// one and associates FDEs to the CIE.
|
||||||
template <class ELFT, class RelTy>
|
template <class ELFT, class RelTy>
|
||||||
void EhFrameSection::addSectionAux(EhInputSection *sec, ArrayRef<RelTy> rels) {
|
void EhFrameSection::addRecords(EhInputSection *sec, ArrayRef<RelTy> rels) {
|
||||||
offsetToCie.clear();
|
offsetToCie.clear();
|
||||||
for (EhSectionPiece &piece : sec->pieces) {
|
for (EhSectionPiece &piece : sec->pieces) {
|
||||||
// The empty record is the end marker.
|
// The empty record is the end marker.
|
||||||
@ -428,8 +425,17 @@ void EhFrameSection::addSectionAux(EhInputSection *sec, ArrayRef<RelTy> rels) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void EhFrameSection::addSection(InputSectionBase *c) {
|
template <class ELFT>
|
||||||
auto *sec = cast<EhInputSection>(c);
|
void EhFrameSection::addSectionAux(EhInputSection *sec) {
|
||||||
|
if (!sec->isLive())
|
||||||
|
return;
|
||||||
|
if (sec->areRelocsRela)
|
||||||
|
addRecords<ELFT>(sec, sec->template relas<ELFT>());
|
||||||
|
else
|
||||||
|
addRecords<ELFT>(sec, sec->template rels<ELFT>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EhFrameSection::addSection(EhInputSection *sec) {
|
||||||
sec->parent = this;
|
sec->parent = this;
|
||||||
|
|
||||||
alignment = std::max(alignment, sec->alignment);
|
alignment = std::max(alignment, sec->alignment);
|
||||||
@ -437,14 +443,6 @@ template <class ELFT> void EhFrameSection::addSection(InputSectionBase *c) {
|
|||||||
|
|
||||||
for (auto *ds : sec->dependentSections)
|
for (auto *ds : sec->dependentSections)
|
||||||
dependentSections.push_back(ds);
|
dependentSections.push_back(ds);
|
||||||
|
|
||||||
if (sec->pieces.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (sec->areRelocsRela)
|
|
||||||
addSectionAux<ELFT>(sec, sec->template relas<ELFT>());
|
|
||||||
else
|
|
||||||
addSectionAux<ELFT>(sec, sec->template rels<ELFT>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
|
static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
|
||||||
@ -461,6 +459,28 @@ static void writeCieFde(uint8_t *buf, ArrayRef<uint8_t> d) {
|
|||||||
|
|
||||||
void EhFrameSection::finalizeContents() {
|
void EhFrameSection::finalizeContents() {
|
||||||
assert(!this->size); // Not finalized.
|
assert(!this->size); // Not finalized.
|
||||||
|
|
||||||
|
switch (config->ekind) {
|
||||||
|
case ELFNoneKind:
|
||||||
|
llvm_unreachable("invalid ekind");
|
||||||
|
case ELF32LEKind:
|
||||||
|
for (EhInputSection *sec : sections)
|
||||||
|
addSectionAux<ELF32LE>(sec);
|
||||||
|
break;
|
||||||
|
case ELF32BEKind:
|
||||||
|
for (EhInputSection *sec : sections)
|
||||||
|
addSectionAux<ELF32BE>(sec);
|
||||||
|
break;
|
||||||
|
case ELF64LEKind:
|
||||||
|
for (EhInputSection *sec : sections)
|
||||||
|
addSectionAux<ELF64LE>(sec);
|
||||||
|
break;
|
||||||
|
case ELF64BEKind:
|
||||||
|
for (EhInputSection *sec : sections)
|
||||||
|
addSectionAux<ELF64BE>(sec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
size_t off = 0;
|
size_t off = 0;
|
||||||
for (CieRecord *rec : cieRecords) {
|
for (CieRecord *rec : cieRecords) {
|
||||||
rec->cie->outputOff = off;
|
rec->cie->outputOff = off;
|
||||||
@ -1162,10 +1182,12 @@ void StringTableSection::writeTo(uint8_t *buf) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the number of version definition entries. Because the first entry
|
// Returns the number of entries in .gnu.version_d: the number of
|
||||||
// is for the version definition itself, it is the number of versioned symbols
|
// non-VER_NDX_LOCAL-non-VER_NDX_GLOBAL definitions, plus 1.
|
||||||
// plus one. Note that we don't support multiple versions yet.
|
// Note that we don't support vd_cnt > 1 yet.
|
||||||
static unsigned getVerDefNum() { return config->versionDefinitions.size() + 1; }
|
static unsigned getVerDefNum() {
|
||||||
|
return namedVersionDefs().size() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
template <class ELFT>
|
template <class ELFT>
|
||||||
DynamicSection<ELFT>::DynamicSection()
|
DynamicSection<ELFT>::DynamicSection()
|
||||||
@ -1218,6 +1240,25 @@ void DynamicSection<ELFT>::addSym(int32_t tag, Symbol *sym) {
|
|||||||
entries.push_back({tag, [=] { return sym->getVA(); }});
|
entries.push_back({tag, [=] { return sym->getVA(); }});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The output section .rela.dyn may include these synthetic sections:
|
||||||
|
//
|
||||||
|
// - part.relaDyn
|
||||||
|
// - in.relaIplt: this is included if in.relaIplt is named .rela.dyn
|
||||||
|
// - in.relaPlt: this is included if a linker script places .rela.plt inside
|
||||||
|
// .rela.dyn
|
||||||
|
//
|
||||||
|
// DT_RELASZ is the total size of the included sections.
|
||||||
|
static std::function<uint64_t()> addRelaSz(RelocationBaseSection *relaDyn) {
|
||||||
|
return [=]() {
|
||||||
|
size_t size = relaDyn->getSize();
|
||||||
|
if (in.relaIplt->getParent() == relaDyn->getParent())
|
||||||
|
size += in.relaIplt->getSize();
|
||||||
|
if (in.relaPlt->getParent() == relaDyn->getParent())
|
||||||
|
size += in.relaPlt->getSize();
|
||||||
|
return size;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// A Linker script may assign the RELA relocation sections to the same
|
// A Linker script may assign the RELA relocation sections to the same
|
||||||
// output section. When this occurs we cannot just use the OutputSection
|
// output section. When this occurs we cannot just use the OutputSection
|
||||||
// Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to
|
// Size. Moreover the [DT_JMPREL, DT_JMPREL + DT_PLTRELSZ) is permitted to
|
||||||
@ -1232,7 +1273,7 @@ static uint64_t addPltRelSz() {
|
|||||||
|
|
||||||
// Add remaining entries to complete .dynamic contents.
|
// Add remaining entries to complete .dynamic contents.
|
||||||
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
|
template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
|
||||||
elf::Partition &part = getPartition();
|
Partition &part = getPartition();
|
||||||
bool isMain = part.name.empty();
|
bool isMain = part.name.empty();
|
||||||
|
|
||||||
for (StringRef s : config->filterList)
|
for (StringRef s : config->filterList)
|
||||||
@ -1306,9 +1347,11 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
|
|||||||
if (OutputSection *sec = part.dynStrTab->getParent())
|
if (OutputSection *sec = part.dynStrTab->getParent())
|
||||||
this->link = sec->sectionIndex;
|
this->link = sec->sectionIndex;
|
||||||
|
|
||||||
if (part.relaDyn->isNeeded()) {
|
if (part.relaDyn->isNeeded() ||
|
||||||
|
(in.relaIplt->isNeeded() &&
|
||||||
|
part.relaDyn->getParent() == in.relaIplt->getParent())) {
|
||||||
addInSec(part.relaDyn->dynamicTag, part.relaDyn);
|
addInSec(part.relaDyn->dynamicTag, part.relaDyn);
|
||||||
addSize(part.relaDyn->sizeDynamicTag, part.relaDyn->getParent());
|
entries.push_back({part.relaDyn->sizeDynamicTag, addRelaSz(part.relaDyn)});
|
||||||
|
|
||||||
bool isRela = config->isRela;
|
bool isRela = config->isRela;
|
||||||
addInt(isRela ? DT_RELAENT : DT_RELENT,
|
addInt(isRela ? DT_RELAENT : DT_RELENT,
|
||||||
@ -1679,6 +1722,56 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
|
|||||||
relativeGroups.emplace_back(std::move(group));
|
relativeGroups.emplace_back(std::move(group));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For non-relative relocations, we would like to:
|
||||||
|
// 1. Have relocations with the same symbol offset to be consecutive, so
|
||||||
|
// that the runtime linker can speed-up symbol lookup by implementing an
|
||||||
|
// 1-entry cache.
|
||||||
|
// 2. Group relocations by r_info to reduce the size of the relocation
|
||||||
|
// section.
|
||||||
|
// Since the symbol offset is the high bits in r_info, sorting by r_info
|
||||||
|
// allows us to do both.
|
||||||
|
//
|
||||||
|
// For Rela, we also want to sort by r_addend when r_info is the same. This
|
||||||
|
// enables us to group by r_addend as well.
|
||||||
|
llvm::stable_sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
|
||||||
|
if (a.r_info != b.r_info)
|
||||||
|
return a.r_info < b.r_info;
|
||||||
|
if (config->isRela)
|
||||||
|
return a.r_addend < b.r_addend;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Group relocations with the same r_info. Note that each group emits a group
|
||||||
|
// header and that may make the relocation section larger. It is hard to
|
||||||
|
// estimate the size of a group header as the encoded size of that varies
|
||||||
|
// based on r_info. However, we can approximate this trade-off by the number
|
||||||
|
// of values encoded. Each group header contains 3 values, and each relocation
|
||||||
|
// in a group encodes one less value, as compared to when it is not grouped.
|
||||||
|
// Therefore, we only group relocations if there are 3 or more of them with
|
||||||
|
// the same r_info.
|
||||||
|
//
|
||||||
|
// For Rela, the addend for most non-relative relocations is zero, and thus we
|
||||||
|
// can usually get a smaller relocation section if we group relocations with 0
|
||||||
|
// addend as well.
|
||||||
|
std::vector<Elf_Rela> ungroupedNonRelatives;
|
||||||
|
std::vector<std::vector<Elf_Rela>> nonRelativeGroups;
|
||||||
|
for (auto i = nonRelatives.begin(), e = nonRelatives.end(); i != e;) {
|
||||||
|
auto j = i + 1;
|
||||||
|
while (j != e && i->r_info == j->r_info &&
|
||||||
|
(!config->isRela || i->r_addend == j->r_addend))
|
||||||
|
++j;
|
||||||
|
if (j - i < 3 || (config->isRela && i->r_addend != 0))
|
||||||
|
ungroupedNonRelatives.insert(ungroupedNonRelatives.end(), i, j);
|
||||||
|
else
|
||||||
|
nonRelativeGroups.emplace_back(i, j);
|
||||||
|
i = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort ungrouped relocations by offset to minimize the encoded length.
|
||||||
|
llvm::sort(ungroupedNonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
|
||||||
|
return a.r_offset < b.r_offset;
|
||||||
|
});
|
||||||
|
|
||||||
unsigned hasAddendIfRela =
|
unsigned hasAddendIfRela =
|
||||||
config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
|
config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0;
|
||||||
|
|
||||||
@ -1733,14 +1826,23 @@ bool AndroidPackedRelocationSection<ELFT>::updateAllocSize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally the non-relative relocations.
|
// Grouped non-relatives.
|
||||||
llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) {
|
for (ArrayRef<Elf_Rela> g : nonRelativeGroups) {
|
||||||
return a.r_offset < b.r_offset;
|
add(g.size());
|
||||||
});
|
add(RELOCATION_GROUPED_BY_INFO_FLAG);
|
||||||
if (!nonRelatives.empty()) {
|
add(g[0].r_info);
|
||||||
add(nonRelatives.size());
|
for (const Elf_Rela &r : g) {
|
||||||
|
add(r.r_offset - offset);
|
||||||
|
offset = r.r_offset;
|
||||||
|
}
|
||||||
|
addend = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally the ungrouped non-relative relocations.
|
||||||
|
if (!ungroupedNonRelatives.empty()) {
|
||||||
|
add(ungroupedNonRelatives.size());
|
||||||
add(hasAddendIfRela);
|
add(hasAddendIfRela);
|
||||||
for (Elf_Rela &r : nonRelatives) {
|
for (Elf_Rela &r : ungroupedNonRelatives) {
|
||||||
add(r.r_offset - offset);
|
add(r.r_offset - offset);
|
||||||
offset = r.r_offset;
|
offset = r.r_offset;
|
||||||
add(r.r_info);
|
add(r.r_info);
|
||||||
@ -1852,6 +1954,14 @@ template <class ELFT> bool RelrSection<ELFT>::updateAllocSize() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't allow the section to shrink; otherwise the size of the section can
|
||||||
|
// oscillate infinitely. Trailing 1s do not decode to more relocations.
|
||||||
|
if (relrRelocs.size() < oldSize) {
|
||||||
|
log(".relr.dyn needs " + Twine(oldSize - relrRelocs.size()) +
|
||||||
|
" padding word(s)");
|
||||||
|
relrRelocs.resize(oldSize, Elf_Relr(1));
|
||||||
|
}
|
||||||
|
|
||||||
return relrRelocs.size() != oldSize;
|
return relrRelocs.size() != oldSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2452,6 +2562,10 @@ readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
|
|||||||
|
|
||||||
uint32_t cuIdx = 0;
|
uint32_t cuIdx = 0;
|
||||||
for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) {
|
for (std::unique_ptr<DWARFUnit> &cu : dwarf.compile_units()) {
|
||||||
|
if (Error e = cu->tryExtractDIEsIfNeeded(false)) {
|
||||||
|
error(toString(sec) + ": " + toString(std::move(e)));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
Expected<DWARFAddressRangesVector> ranges = cu->collectAddressRanges();
|
Expected<DWARFAddressRangesVector> ranges = cu->collectAddressRanges();
|
||||||
if (!ranges) {
|
if (!ranges) {
|
||||||
error(toString(sec) + ": " + toString(ranges.takeError()));
|
error(toString(sec) + ": " + toString(ranges.takeError()));
|
||||||
@ -2481,9 +2595,9 @@ readAddressAreas(DWARFContext &dwarf, InputSection *sec) {
|
|||||||
template <class ELFT>
|
template <class ELFT>
|
||||||
static std::vector<GdbIndexSection::NameAttrEntry>
|
static std::vector<GdbIndexSection::NameAttrEntry>
|
||||||
readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
|
readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
|
||||||
const std::vector<GdbIndexSection::CuEntry> &cUs) {
|
const std::vector<GdbIndexSection::CuEntry> &cus) {
|
||||||
const DWARFSection &pubNames = obj.getGnuPubNamesSection();
|
const DWARFSection &pubNames = obj.getGnuPubnamesSection();
|
||||||
const DWARFSection &pubTypes = obj.getGnuPubTypesSection();
|
const DWARFSection &pubTypes = obj.getGnuPubtypesSection();
|
||||||
|
|
||||||
std::vector<GdbIndexSection::NameAttrEntry> ret;
|
std::vector<GdbIndexSection::NameAttrEntry> ret;
|
||||||
for (const DWARFSection *pub : {&pubNames, &pubTypes}) {
|
for (const DWARFSection *pub : {&pubNames, &pubTypes}) {
|
||||||
@ -2493,12 +2607,11 @@ readPubNamesAndTypes(const LLDDwarfObj<ELFT> &obj,
|
|||||||
// don't know how many compilation units precede this object to compute
|
// don't know how many compilation units precede this object to compute
|
||||||
// cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add
|
// cuIndex, we compute (kind << 24 | cuIndexInThisObject) instead, and add
|
||||||
// the number of preceding compilation units later.
|
// the number of preceding compilation units later.
|
||||||
uint32_t i =
|
uint32_t i = llvm::partition_point(cus,
|
||||||
lower_bound(cUs, set.Offset,
|
[&](GdbIndexSection::CuEntry cu) {
|
||||||
[](GdbIndexSection::CuEntry cu, uint32_t offset) {
|
return cu.cuOffset < set.Offset;
|
||||||
return cu.cuOffset < offset;
|
}) -
|
||||||
}) -
|
cus.begin();
|
||||||
cUs.begin();
|
|
||||||
for (const DWARFDebugPubTable::Entry &ent : set.Entries)
|
for (const DWARFDebugPubTable::Entry &ent : set.Entries)
|
||||||
ret.push_back({{ent.Name, computeGdbHash(ent.Name)},
|
ret.push_back({{ent.Name, computeGdbHash(ent.Name)},
|
||||||
(ent.Descriptor.toBits() << 24) | i});
|
(ent.Descriptor.toBits() << 24) | i});
|
||||||
@ -2603,7 +2716,7 @@ template <class ELFT> GdbIndexSection *GdbIndexSection::create() {
|
|||||||
|
|
||||||
parallelForEachN(0, sections.size(), [&](size_t i) {
|
parallelForEachN(0, sections.size(), [&](size_t i) {
|
||||||
ObjFile<ELFT> *file = sections[i]->getFile<ELFT>();
|
ObjFile<ELFT> *file = sections[i]->getFile<ELFT>();
|
||||||
DWARFContext dwarf(make_unique<LLDDwarfObj<ELFT>>(file));
|
DWARFContext dwarf(std::make_unique<LLDDwarfObj<ELFT>>(file));
|
||||||
|
|
||||||
chunks[i].sec = sections[i];
|
chunks[i].sec = sections[i];
|
||||||
chunks[i].compilationUnits = readCuList(dwarf);
|
chunks[i].compilationUnits = readCuList(dwarf);
|
||||||
@ -2750,7 +2863,7 @@ StringRef VersionDefinitionSection::getFileDefName() {
|
|||||||
|
|
||||||
void VersionDefinitionSection::finalizeContents() {
|
void VersionDefinitionSection::finalizeContents() {
|
||||||
fileDefNameOff = getPartition().dynStrTab->addString(getFileDefName());
|
fileDefNameOff = getPartition().dynStrTab->addString(getFileDefName());
|
||||||
for (VersionDefinition &v : config->versionDefinitions)
|
for (const VersionDefinition &v : namedVersionDefs())
|
||||||
verDefNameOffs.push_back(getPartition().dynStrTab->addString(v.name));
|
verDefNameOffs.push_back(getPartition().dynStrTab->addString(v.name));
|
||||||
|
|
||||||
if (OutputSection *sec = getPartition().dynStrTab->getParent())
|
if (OutputSection *sec = getPartition().dynStrTab->getParent())
|
||||||
@ -2784,7 +2897,7 @@ void VersionDefinitionSection::writeTo(uint8_t *buf) {
|
|||||||
writeOne(buf, 1, getFileDefName(), fileDefNameOff);
|
writeOne(buf, 1, getFileDefName(), fileDefNameOff);
|
||||||
|
|
||||||
auto nameOffIt = verDefNameOffs.begin();
|
auto nameOffIt = verDefNameOffs.begin();
|
||||||
for (VersionDefinition &v : config->versionDefinitions) {
|
for (const VersionDefinition &v : namedVersionDefs()) {
|
||||||
buf += EntrySize;
|
buf += EntrySize;
|
||||||
writeOne(buf, v.id, v.name, *nameOffIt++);
|
writeOne(buf, v.id, v.name, *nameOffIt++);
|
||||||
}
|
}
|
||||||
@ -2826,7 +2939,7 @@ bool VersionTableSection::isNeeded() const {
|
|||||||
return getPartition().verDef || getPartition().verNeed->isNeeded();
|
return getPartition().verDef || getPartition().verNeed->isNeeded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void elf::addVerneed(Symbol *ss) {
|
void addVerneed(Symbol *ss) {
|
||||||
auto &file = cast<SharedFile>(*ss->file);
|
auto &file = cast<SharedFile>(*ss->file);
|
||||||
if (ss->verdefIndex == VER_NDX_GLOBAL) {
|
if (ss->verdefIndex == VER_NDX_GLOBAL) {
|
||||||
ss->versionId = VER_NDX_GLOBAL;
|
ss->versionId = VER_NDX_GLOBAL;
|
||||||
@ -3009,17 +3122,16 @@ void MergeNoTailSection::finalizeContents() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static MergeSyntheticSection *createMergeSynthetic(StringRef name,
|
MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
|
||||||
uint32_t type,
|
uint64_t flags,
|
||||||
uint64_t flags,
|
uint32_t alignment) {
|
||||||
uint32_t alignment) {
|
|
||||||
bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2;
|
bool shouldTailMerge = (flags & SHF_STRINGS) && config->optimize >= 2;
|
||||||
if (shouldTailMerge)
|
if (shouldTailMerge)
|
||||||
return make<MergeTailSection>(name, type, flags, alignment);
|
return make<MergeTailSection>(name, type, flags, alignment);
|
||||||
return make<MergeNoTailSection>(name, type, flags, alignment);
|
return make<MergeNoTailSection>(name, type, flags, alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void elf::splitSections() {
|
template <class ELFT> void splitSections() {
|
||||||
// splitIntoPieces needs to be called on each MergeInputSection
|
// splitIntoPieces needs to be called on each MergeInputSection
|
||||||
// before calling finalizeContents().
|
// before calling finalizeContents().
|
||||||
parallelForEach(inputSections, [](InputSectionBase *sec) {
|
parallelForEach(inputSections, [](InputSectionBase *sec) {
|
||||||
@ -3030,63 +3142,6 @@ template <class ELFT> void elf::splitSections() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function scans over the inputsections to create mergeable
|
|
||||||
// synthetic sections.
|
|
||||||
//
|
|
||||||
// It removes MergeInputSections from the input section array and adds
|
|
||||||
// new synthetic sections at the location of the first input section
|
|
||||||
// that it replaces. It then finalizes each synthetic section in order
|
|
||||||
// to compute an output offset for each piece of each input section.
|
|
||||||
void elf::mergeSections() {
|
|
||||||
std::vector<MergeSyntheticSection *> mergeSections;
|
|
||||||
for (InputSectionBase *&s : inputSections) {
|
|
||||||
MergeInputSection *ms = dyn_cast<MergeInputSection>(s);
|
|
||||||
if (!ms)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// We do not want to handle sections that are not alive, so just remove
|
|
||||||
// them instead of trying to merge.
|
|
||||||
if (!ms->isLive()) {
|
|
||||||
s = nullptr;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringRef outsecName = getOutputSectionName(ms);
|
|
||||||
|
|
||||||
auto i = llvm::find_if(mergeSections, [=](MergeSyntheticSection *sec) {
|
|
||||||
// While we could create a single synthetic section for two different
|
|
||||||
// values of Entsize, it is better to take Entsize into consideration.
|
|
||||||
//
|
|
||||||
// With a single synthetic section no two pieces with different Entsize
|
|
||||||
// could be equal, so we may as well have two sections.
|
|
||||||
//
|
|
||||||
// Using Entsize in here also allows us to propagate it to the synthetic
|
|
||||||
// section.
|
|
||||||
//
|
|
||||||
// SHF_STRINGS section with different alignments should not be merged.
|
|
||||||
return sec->name == outsecName && sec->flags == ms->flags &&
|
|
||||||
sec->entsize == ms->entsize &&
|
|
||||||
(sec->alignment == ms->alignment || !(sec->flags & SHF_STRINGS));
|
|
||||||
});
|
|
||||||
if (i == mergeSections.end()) {
|
|
||||||
MergeSyntheticSection *syn =
|
|
||||||
createMergeSynthetic(outsecName, ms->type, ms->flags, ms->alignment);
|
|
||||||
mergeSections.push_back(syn);
|
|
||||||
i = std::prev(mergeSections.end());
|
|
||||||
s = syn;
|
|
||||||
syn->entsize = ms->entsize;
|
|
||||||
} else {
|
|
||||||
s = nullptr;
|
|
||||||
}
|
|
||||||
(*i)->addSection(ms);
|
|
||||||
}
|
|
||||||
for (auto *ms : mergeSections)
|
|
||||||
ms->finalizeContents();
|
|
||||||
|
|
||||||
std::vector<InputSectionBase *> &v = inputSections;
|
|
||||||
v.erase(std::remove(v.begin(), v.end(), nullptr), v.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
MipsRldMapSection::MipsRldMapSection()
|
MipsRldMapSection::MipsRldMapSection()
|
||||||
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
|
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, config->wordsize,
|
||||||
".rld_map") {}
|
".rld_map") {}
|
||||||
@ -3102,17 +3157,23 @@ static InputSection *findExidxSection(InputSection *isec) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isValidExidxSectionDep(InputSection *isec) {
|
||||||
|
return (isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) &&
|
||||||
|
isec->getSize() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool ARMExidxSyntheticSection::addSection(InputSection *isec) {
|
bool ARMExidxSyntheticSection::addSection(InputSection *isec) {
|
||||||
if (isec->type == SHT_ARM_EXIDX) {
|
if (isec->type == SHT_ARM_EXIDX) {
|
||||||
exidxSections.push_back(isec);
|
if (InputSection* dep = isec->getLinkOrderDep())
|
||||||
return true;
|
if (isValidExidxSectionDep(dep)) {
|
||||||
|
exidxSections.push_back(isec);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((isec->flags & SHF_ALLOC) && (isec->flags & SHF_EXECINSTR) &&
|
if (isValidExidxSectionDep(isec)) {
|
||||||
isec->getSize() > 0) {
|
|
||||||
executableSections.push_back(isec);
|
executableSections.push_back(isec);
|
||||||
if (empty && findExidxSection(isec))
|
|
||||||
empty = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3182,17 +3243,14 @@ static bool isDuplicateArmExidxSec(InputSection *prev, InputSection *cur) {
|
|||||||
// with the highest address and any InputSections that have mergeable
|
// with the highest address and any InputSections that have mergeable
|
||||||
// .ARM.exidx table entries are removed from it.
|
// .ARM.exidx table entries are removed from it.
|
||||||
void ARMExidxSyntheticSection::finalizeContents() {
|
void ARMExidxSyntheticSection::finalizeContents() {
|
||||||
if (script->hasSectionsCommand) {
|
// The executableSections and exidxSections that we use to derive the final
|
||||||
// The executableSections and exidxSections that we use to derive the
|
// contents of this SyntheticSection are populated before
|
||||||
// final contents of this SyntheticSection are populated before the
|
// processSectionCommands() and ICF. A /DISCARD/ entry in SECTIONS command or
|
||||||
// linker script assigns InputSections to OutputSections. The linker script
|
// ICF may remove executable InputSections and their dependent .ARM.exidx
|
||||||
// SECTIONS command may have a /DISCARD/ entry that removes executable
|
// section that we recorded earlier.
|
||||||
// InputSections and their dependent .ARM.exidx section that we recorded
|
auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); };
|
||||||
// earlier.
|
llvm::erase_if(executableSections, isDiscarded);
|
||||||
auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); };
|
llvm::erase_if(exidxSections, isDiscarded);
|
||||||
llvm::erase_if(executableSections, isDiscarded);
|
|
||||||
llvm::erase_if(exidxSections, isDiscarded);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the executable sections that may or may not have associated
|
// Sort the executable sections that may or may not have associated
|
||||||
// .ARM.exidx sections by order of ascending address. This requires the
|
// .ARM.exidx sections by order of ascending address. This requires the
|
||||||
@ -3282,6 +3340,12 @@ void ARMExidxSyntheticSection::writeTo(uint8_t *buf) {
|
|||||||
assert(size == offset + 8);
|
assert(size == offset + 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ARMExidxSyntheticSection::isNeeded() const {
|
||||||
|
return llvm::find_if(exidxSections, [](InputSection *isec) {
|
||||||
|
return isec->isLive();
|
||||||
|
}) != exidxSections.end();
|
||||||
|
}
|
||||||
|
|
||||||
bool ARMExidxSyntheticSection::classof(const SectionBase *d) {
|
bool ARMExidxSyntheticSection::classof(const SectionBase *d) {
|
||||||
return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX;
|
return d->kind() == InputSectionBase::Synthetic && d->type == SHT_ARM_EXIDX;
|
||||||
}
|
}
|
||||||
@ -3401,23 +3465,6 @@ bool PPC64LongBranchTargetSection::isNeeded() const {
|
|||||||
return !finalized || !entries.empty();
|
return !finalized || !entries.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
RISCVSdataSection::RISCVSdataSection()
|
|
||||||
: SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 1, ".sdata") {}
|
|
||||||
|
|
||||||
bool RISCVSdataSection::isNeeded() const {
|
|
||||||
if (!ElfSym::riscvGlobalPointer)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// __global_pointer$ is defined relative to .sdata . If the section does not
|
|
||||||
// exist, create a dummy one.
|
|
||||||
for (BaseCommand *base : getParent()->sectionCommands)
|
|
||||||
if (auto *isd = dyn_cast<InputSectionDescription>(base))
|
|
||||||
for (InputSection *isec : isd->sections)
|
|
||||||
if (isec != this)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t getAbiVersion() {
|
static uint8_t getAbiVersion() {
|
||||||
// MIPS non-PIC executable gets ABI version 1.
|
// MIPS non-PIC executable gets ABI version 1.
|
||||||
if (config->emachine == EM_MIPS) {
|
if (config->emachine == EM_MIPS) {
|
||||||
@ -3438,7 +3485,7 @@ static uint8_t getAbiVersion() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ELFT> void elf::writeEhdr(uint8_t *buf, Partition &part) {
|
template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part) {
|
||||||
// For executable segments, the trap instructions are written before writing
|
// For executable segments, the trap instructions are written before writing
|
||||||
// the header. Setting Elf header bytes to zero ensures that any unused bytes
|
// the header. Setting Elf header bytes to zero ensures that any unused bytes
|
||||||
// in header are zero-cleared, instead of having trap instructions.
|
// in header are zero-cleared, instead of having trap instructions.
|
||||||
@ -3464,7 +3511,7 @@ template <typename ELFT> void elf::writeEhdr(uint8_t *buf, Partition &part) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ELFT> void elf::writePhdrs(uint8_t *buf, Partition &part) {
|
template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part) {
|
||||||
// Write the program header table.
|
// Write the program header table.
|
||||||
auto *hBuf = reinterpret_cast<typename ELFT::Phdr *>(buf);
|
auto *hBuf = reinterpret_cast<typename ELFT::Phdr *>(buf);
|
||||||
for (PhdrEntry *p : part.phdrs) {
|
for (PhdrEntry *p : part.phdrs) {
|
||||||
@ -3539,92 +3586,90 @@ void PartitionIndexSection::writeTo(uint8_t *buf) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InStruct elf::in;
|
InStruct in;
|
||||||
|
|
||||||
std::vector<Partition> elf::partitions;
|
std::vector<Partition> partitions;
|
||||||
Partition *elf::mainPart;
|
Partition *mainPart;
|
||||||
|
|
||||||
template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
|
template GdbIndexSection *GdbIndexSection::create<ELF32LE>();
|
||||||
template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
|
template GdbIndexSection *GdbIndexSection::create<ELF32BE>();
|
||||||
template GdbIndexSection *GdbIndexSection::create<ELF64LE>();
|
template GdbIndexSection *GdbIndexSection::create<ELF64LE>();
|
||||||
template GdbIndexSection *GdbIndexSection::create<ELF64BE>();
|
template GdbIndexSection *GdbIndexSection::create<ELF64BE>();
|
||||||
|
|
||||||
template void elf::splitSections<ELF32LE>();
|
template void splitSections<ELF32LE>();
|
||||||
template void elf::splitSections<ELF32BE>();
|
template void splitSections<ELF32BE>();
|
||||||
template void elf::splitSections<ELF64LE>();
|
template void splitSections<ELF64LE>();
|
||||||
template void elf::splitSections<ELF64BE>();
|
template void splitSections<ELF64BE>();
|
||||||
|
|
||||||
template void EhFrameSection::addSection<ELF32LE>(InputSectionBase *);
|
|
||||||
template void EhFrameSection::addSection<ELF32BE>(InputSectionBase *);
|
|
||||||
template void EhFrameSection::addSection<ELF64LE>(InputSectionBase *);
|
|
||||||
template void EhFrameSection::addSection<ELF64BE>(InputSectionBase *);
|
|
||||||
|
|
||||||
template void PltSection::addEntry<ELF32LE>(Symbol &Sym);
|
template void PltSection::addEntry<ELF32LE>(Symbol &Sym);
|
||||||
template void PltSection::addEntry<ELF32BE>(Symbol &Sym);
|
template void PltSection::addEntry<ELF32BE>(Symbol &Sym);
|
||||||
template void PltSection::addEntry<ELF64LE>(Symbol &Sym);
|
template void PltSection::addEntry<ELF64LE>(Symbol &Sym);
|
||||||
template void PltSection::addEntry<ELF64BE>(Symbol &Sym);
|
template void PltSection::addEntry<ELF64BE>(Symbol &Sym);
|
||||||
|
|
||||||
template class elf::MipsAbiFlagsSection<ELF32LE>;
|
template class MipsAbiFlagsSection<ELF32LE>;
|
||||||
template class elf::MipsAbiFlagsSection<ELF32BE>;
|
template class MipsAbiFlagsSection<ELF32BE>;
|
||||||
template class elf::MipsAbiFlagsSection<ELF64LE>;
|
template class MipsAbiFlagsSection<ELF64LE>;
|
||||||
template class elf::MipsAbiFlagsSection<ELF64BE>;
|
template class MipsAbiFlagsSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::MipsOptionsSection<ELF32LE>;
|
template class MipsOptionsSection<ELF32LE>;
|
||||||
template class elf::MipsOptionsSection<ELF32BE>;
|
template class MipsOptionsSection<ELF32BE>;
|
||||||
template class elf::MipsOptionsSection<ELF64LE>;
|
template class MipsOptionsSection<ELF64LE>;
|
||||||
template class elf::MipsOptionsSection<ELF64BE>;
|
template class MipsOptionsSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::MipsReginfoSection<ELF32LE>;
|
template class MipsReginfoSection<ELF32LE>;
|
||||||
template class elf::MipsReginfoSection<ELF32BE>;
|
template class MipsReginfoSection<ELF32BE>;
|
||||||
template class elf::MipsReginfoSection<ELF64LE>;
|
template class MipsReginfoSection<ELF64LE>;
|
||||||
template class elf::MipsReginfoSection<ELF64BE>;
|
template class MipsReginfoSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::DynamicSection<ELF32LE>;
|
template class DynamicSection<ELF32LE>;
|
||||||
template class elf::DynamicSection<ELF32BE>;
|
template class DynamicSection<ELF32BE>;
|
||||||
template class elf::DynamicSection<ELF64LE>;
|
template class DynamicSection<ELF64LE>;
|
||||||
template class elf::DynamicSection<ELF64BE>;
|
template class DynamicSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::RelocationSection<ELF32LE>;
|
template class RelocationSection<ELF32LE>;
|
||||||
template class elf::RelocationSection<ELF32BE>;
|
template class RelocationSection<ELF32BE>;
|
||||||
template class elf::RelocationSection<ELF64LE>;
|
template class RelocationSection<ELF64LE>;
|
||||||
template class elf::RelocationSection<ELF64BE>;
|
template class RelocationSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::AndroidPackedRelocationSection<ELF32LE>;
|
template class AndroidPackedRelocationSection<ELF32LE>;
|
||||||
template class elf::AndroidPackedRelocationSection<ELF32BE>;
|
template class AndroidPackedRelocationSection<ELF32BE>;
|
||||||
template class elf::AndroidPackedRelocationSection<ELF64LE>;
|
template class AndroidPackedRelocationSection<ELF64LE>;
|
||||||
template class elf::AndroidPackedRelocationSection<ELF64BE>;
|
template class AndroidPackedRelocationSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::RelrSection<ELF32LE>;
|
template class RelrSection<ELF32LE>;
|
||||||
template class elf::RelrSection<ELF32BE>;
|
template class RelrSection<ELF32BE>;
|
||||||
template class elf::RelrSection<ELF64LE>;
|
template class RelrSection<ELF64LE>;
|
||||||
template class elf::RelrSection<ELF64BE>;
|
template class RelrSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::SymbolTableSection<ELF32LE>;
|
template class SymbolTableSection<ELF32LE>;
|
||||||
template class elf::SymbolTableSection<ELF32BE>;
|
template class SymbolTableSection<ELF32BE>;
|
||||||
template class elf::SymbolTableSection<ELF64LE>;
|
template class SymbolTableSection<ELF64LE>;
|
||||||
template class elf::SymbolTableSection<ELF64BE>;
|
template class SymbolTableSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::VersionNeedSection<ELF32LE>;
|
template class VersionNeedSection<ELF32LE>;
|
||||||
template class elf::VersionNeedSection<ELF32BE>;
|
template class VersionNeedSection<ELF32BE>;
|
||||||
template class elf::VersionNeedSection<ELF64LE>;
|
template class VersionNeedSection<ELF64LE>;
|
||||||
template class elf::VersionNeedSection<ELF64BE>;
|
template class VersionNeedSection<ELF64BE>;
|
||||||
|
|
||||||
template void elf::writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part);
|
template void writeEhdr<ELF32LE>(uint8_t *Buf, Partition &Part);
|
||||||
template void elf::writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part);
|
template void writeEhdr<ELF32BE>(uint8_t *Buf, Partition &Part);
|
||||||
template void elf::writeEhdr<ELF64LE>(uint8_t *Buf, Partition &Part);
|
template void writeEhdr<ELF64LE>(uint8_t *Buf, Partition &Part);
|
||||||
template void elf::writeEhdr<ELF64BE>(uint8_t *Buf, Partition &Part);
|
template void writeEhdr<ELF64BE>(uint8_t *Buf, Partition &Part);
|
||||||
|
|
||||||
template void elf::writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part);
|
template void writePhdrs<ELF32LE>(uint8_t *Buf, Partition &Part);
|
||||||
template void elf::writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
|
template void writePhdrs<ELF32BE>(uint8_t *Buf, Partition &Part);
|
||||||
template void elf::writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
|
template void writePhdrs<ELF64LE>(uint8_t *Buf, Partition &Part);
|
||||||
template void elf::writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
|
template void writePhdrs<ELF64BE>(uint8_t *Buf, Partition &Part);
|
||||||
|
|
||||||
template class elf::PartitionElfHeaderSection<ELF32LE>;
|
template class PartitionElfHeaderSection<ELF32LE>;
|
||||||
template class elf::PartitionElfHeaderSection<ELF32BE>;
|
template class PartitionElfHeaderSection<ELF32BE>;
|
||||||
template class elf::PartitionElfHeaderSection<ELF64LE>;
|
template class PartitionElfHeaderSection<ELF64LE>;
|
||||||
template class elf::PartitionElfHeaderSection<ELF64BE>;
|
template class PartitionElfHeaderSection<ELF64BE>;
|
||||||
|
|
||||||
template class elf::PartitionProgramHeadersSection<ELF32LE>;
|
template class PartitionProgramHeadersSection<ELF32LE>;
|
||||||
template class elf::PartitionProgramHeadersSection<ELF32BE>;
|
template class PartitionProgramHeadersSection<ELF32BE>;
|
||||||
template class elf::PartitionProgramHeadersSection<ELF64LE>;
|
template class PartitionProgramHeadersSection<ELF64LE>;
|
||||||
template class elf::PartitionProgramHeadersSection<ELF64BE>;
|
template class PartitionProgramHeadersSection<ELF64BE>;
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -76,7 +76,7 @@ class EhFrameSection final : public SyntheticSection {
|
|||||||
return SyntheticSection::classof(d) && d->name == ".eh_frame";
|
return SyntheticSection::classof(d) && d->name == ".eh_frame";
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void addSection(InputSectionBase *s);
|
void addSection(EhInputSection *sec);
|
||||||
|
|
||||||
std::vector<EhInputSection *> sections;
|
std::vector<EhInputSection *> sections;
|
||||||
size_t numFdes = 0;
|
size_t numFdes = 0;
|
||||||
@ -97,7 +97,9 @@ class EhFrameSection final : public SyntheticSection {
|
|||||||
uint64_t size = 0;
|
uint64_t size = 0;
|
||||||
|
|
||||||
template <class ELFT, class RelTy>
|
template <class ELFT, class RelTy>
|
||||||
void addSectionAux(EhInputSection *s, llvm::ArrayRef<RelTy> rels);
|
void addRecords(EhInputSection *s, llvm::ArrayRef<RelTy> rels);
|
||||||
|
template <class ELFT>
|
||||||
|
void addSectionAux(EhInputSection *s);
|
||||||
|
|
||||||
template <class ELFT, class RelTy>
|
template <class ELFT, class RelTy>
|
||||||
CieRecord *addCie(EhSectionPiece &piece, ArrayRef<RelTy> rels);
|
CieRecord *addCie(EhSectionPiece &piece, ArrayRef<RelTy> rels);
|
||||||
@ -992,7 +994,7 @@ class ARMExidxSyntheticSection : public SyntheticSection {
|
|||||||
|
|
||||||
size_t getSize() const override { return size; }
|
size_t getSize() const override { return size; }
|
||||||
void writeTo(uint8_t *buf) override;
|
void writeTo(uint8_t *buf) override;
|
||||||
bool isNeeded() const override { return !empty; }
|
bool isNeeded() const override;
|
||||||
// Sort and remove duplicate entries.
|
// Sort and remove duplicate entries.
|
||||||
void finalizeContents() override;
|
void finalizeContents() override;
|
||||||
InputSection *getLinkOrderDep() const;
|
InputSection *getLinkOrderDep() const;
|
||||||
@ -1006,9 +1008,6 @@ class ARMExidxSyntheticSection : public SyntheticSection {
|
|||||||
private:
|
private:
|
||||||
size_t size;
|
size_t size;
|
||||||
|
|
||||||
// Empty if ExecutableSections contains no dependent .ARM.exidx sections.
|
|
||||||
bool empty = true;
|
|
||||||
|
|
||||||
// Instead of storing pointers to the .ARM.exidx InputSections from
|
// Instead of storing pointers to the .ARM.exidx InputSections from
|
||||||
// InputObjects, we store pointers to the executable sections that need
|
// InputObjects, we store pointers to the executable sections that need
|
||||||
// .ARM.exidx sections. We can then use the dependentSections of these to
|
// .ARM.exidx sections. We can then use the dependentSections of these to
|
||||||
@ -1098,19 +1097,11 @@ class PartitionIndexSection : public SyntheticSection {
|
|||||||
void writeTo(uint8_t *buf) override;
|
void writeTo(uint8_t *buf) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a dummy .sdata for __global_pointer$ if .sdata does not exist.
|
|
||||||
class RISCVSdataSection final : public SyntheticSection {
|
|
||||||
public:
|
|
||||||
RISCVSdataSection();
|
|
||||||
size_t getSize() const override { return 0; }
|
|
||||||
bool isNeeded() const override;
|
|
||||||
void writeTo(uint8_t *buf) override {}
|
|
||||||
};
|
|
||||||
|
|
||||||
InputSection *createInterpSection();
|
InputSection *createInterpSection();
|
||||||
MergeInputSection *createCommentSection();
|
MergeInputSection *createCommentSection();
|
||||||
|
MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type,
|
||||||
|
uint64_t flags, uint32_t alignment);
|
||||||
template <class ELFT> void splitSections();
|
template <class ELFT> void splitSections();
|
||||||
void mergeSections();
|
|
||||||
|
|
||||||
template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part);
|
template <typename ELFT> void writeEhdr(uint8_t *buf, Partition &part);
|
||||||
template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part);
|
template <typename ELFT> void writePhdrs(uint8_t *buf, Partition &part);
|
||||||
@ -1171,7 +1162,6 @@ struct InStruct {
|
|||||||
PltSection *plt;
|
PltSection *plt;
|
||||||
PltSection *iplt;
|
PltSection *iplt;
|
||||||
PPC32Got2Section *ppc32Got2;
|
PPC32Got2Section *ppc32Got2;
|
||||||
RISCVSdataSection *riscvSdata;
|
|
||||||
RelocationBaseSection *relaPlt;
|
RelocationBaseSection *relaPlt;
|
||||||
RelocationBaseSection *relaIplt;
|
RelocationBaseSection *relaIplt;
|
||||||
StringTableSection *shStrTab;
|
StringTableSection *shStrTab;
|
||||||
|
@ -34,19 +34,19 @@
|
|||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace llvm::object;
|
using namespace llvm::object;
|
||||||
using namespace llvm::ELF;
|
using namespace llvm::ELF;
|
||||||
using namespace lld;
|
|
||||||
using namespace lld::elf;
|
|
||||||
|
|
||||||
const TargetInfo *elf::target;
|
namespace lld {
|
||||||
|
std::string toString(elf::RelType type) {
|
||||||
std::string lld::toString(RelType type) {
|
|
||||||
StringRef s = getELFRelocationTypeName(elf::config->emachine, type);
|
StringRef s = getELFRelocationTypeName(elf::config->emachine, type);
|
||||||
if (s == "Unknown")
|
if (s == "Unknown")
|
||||||
return ("Unknown (" + Twine(type) + ")").str();
|
return ("Unknown (" + Twine(type) + ")").str();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetInfo *elf::getTarget() {
|
namespace elf {
|
||||||
|
const TargetInfo *target;
|
||||||
|
|
||||||
|
TargetInfo *getTarget() {
|
||||||
switch (config->emachine) {
|
switch (config->emachine) {
|
||||||
case EM_386:
|
case EM_386:
|
||||||
case EM_IAMCU:
|
case EM_IAMCU:
|
||||||
@ -91,6 +91,9 @@ TargetInfo *elf::getTarget() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
|
template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
|
||||||
|
if (!Out::bufferStart)
|
||||||
|
return {};
|
||||||
|
|
||||||
for (InputSectionBase *d : inputSections) {
|
for (InputSectionBase *d : inputSections) {
|
||||||
auto *isec = cast<InputSection>(d);
|
auto *isec = cast<InputSection>(d);
|
||||||
if (!isec->getParent())
|
if (!isec->getParent())
|
||||||
@ -103,7 +106,7 @@ template <class ELFT> static ErrorPlace getErrPlace(const uint8_t *loc) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
|
ErrorPlace getErrorPlace(const uint8_t *loc) {
|
||||||
switch (config->ekind) {
|
switch (config->ekind) {
|
||||||
case ELF32LEKind:
|
case ELF32LEKind:
|
||||||
return getErrPlace<ELF32LE>(loc);
|
return getErrPlace<ELF32LE>(loc);
|
||||||
@ -179,3 +182,6 @@ uint64_t TargetInfo::getImageBase() const {
|
|||||||
return *config->imageBase;
|
return *config->imageBase;
|
||||||
return config->isPic ? 0 : defaultImageBase;
|
return config->isPic ? 0 : defaultImageBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "Writer.h"
|
#include "Writer.h"
|
||||||
#include "AArch64ErrataFix.h"
|
#include "AArch64ErrataFix.h"
|
||||||
|
#include "ARMErrataFix.h"
|
||||||
#include "CallGraphSort.h"
|
#include "CallGraphSort.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "LinkerScript.h"
|
#include "LinkerScript.h"
|
||||||
@ -35,9 +36,8 @@ using namespace llvm::object;
|
|||||||
using namespace llvm::support;
|
using namespace llvm::support;
|
||||||
using namespace llvm::support::endian;
|
using namespace llvm::support::endian;
|
||||||
|
|
||||||
using namespace lld;
|
namespace lld {
|
||||||
using namespace lld::elf;
|
namespace elf {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// The writer writes a SymbolTable result to a file.
|
// The writer writes a SymbolTable result to a file.
|
||||||
template <class ELFT> class Writer {
|
template <class ELFT> class Writer {
|
||||||
@ -62,7 +62,6 @@ template <class ELFT> class Writer {
|
|||||||
void setReservedSymbolSections();
|
void setReservedSymbolSections();
|
||||||
|
|
||||||
std::vector<PhdrEntry *> createPhdrs(Partition &part);
|
std::vector<PhdrEntry *> createPhdrs(Partition &part);
|
||||||
void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrEntry);
|
|
||||||
void addPhdrForSection(Partition &part, unsigned shType, unsigned pType,
|
void addPhdrForSection(Partition &part, unsigned shType, unsigned pType,
|
||||||
unsigned pFlags);
|
unsigned pFlags);
|
||||||
void assignFileOffsets();
|
void assignFileOffsets();
|
||||||
@ -92,7 +91,7 @@ static bool isSectionPrefix(StringRef prefix, StringRef name) {
|
|||||||
return name.startswith(prefix) || name == prefix.drop_back();
|
return name.startswith(prefix) || name == prefix.drop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
StringRef elf::getOutputSectionName(const InputSectionBase *s) {
|
StringRef getOutputSectionName(const InputSectionBase *s) {
|
||||||
if (config->relocatable)
|
if (config->relocatable)
|
||||||
return s->name;
|
return s->name;
|
||||||
|
|
||||||
@ -140,10 +139,9 @@ static bool needsInterpSection() {
|
|||||||
script->needsInterpSection();
|
script->needsInterpSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> void elf::writeResult() { Writer<ELFT>().run(); }
|
template <class ELFT> void writeResult() { Writer<ELFT>().run(); }
|
||||||
|
|
||||||
template <class ELFT>
|
static void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
|
||||||
void Writer<ELFT>::removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
|
|
||||||
llvm::erase_if(phdrs, [&](const PhdrEntry *p) {
|
llvm::erase_if(phdrs, [&](const PhdrEntry *p) {
|
||||||
if (p->p_type != PT_LOAD)
|
if (p->p_type != PT_LOAD)
|
||||||
return false;
|
return false;
|
||||||
@ -154,7 +152,7 @@ void Writer<ELFT>::removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> static void copySectionsIntoPartitions() {
|
void copySectionsIntoPartitions() {
|
||||||
std::vector<InputSectionBase *> newSections;
|
std::vector<InputSectionBase *> newSections;
|
||||||
for (unsigned part = 2; part != partitions.size() + 1; ++part) {
|
for (unsigned part = 2; part != partitions.size() + 1; ++part) {
|
||||||
for (InputSectionBase *s : inputSections) {
|
for (InputSectionBase *s : inputSections) {
|
||||||
@ -176,7 +174,7 @@ template <class ELFT> static void copySectionsIntoPartitions() {
|
|||||||
newSections.end());
|
newSections.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class ELFT> static void combineEhSections() {
|
void combineEhSections() {
|
||||||
for (InputSectionBase *&s : inputSections) {
|
for (InputSectionBase *&s : inputSections) {
|
||||||
// Ignore dead sections and the partition end marker (.part.end),
|
// Ignore dead sections and the partition end marker (.part.end),
|
||||||
// whose partition number is out of bounds.
|
// whose partition number is out of bounds.
|
||||||
@ -185,7 +183,7 @@ template <class ELFT> static void combineEhSections() {
|
|||||||
|
|
||||||
Partition &part = s->getPartition();
|
Partition &part = s->getPartition();
|
||||||
if (auto *es = dyn_cast<EhInputSection>(s)) {
|
if (auto *es = dyn_cast<EhInputSection>(s)) {
|
||||||
part.ehFrame->addSection<ELFT>(es);
|
part.ehFrame->addSection(es);
|
||||||
s = nullptr;
|
s = nullptr;
|
||||||
} else if (s->kind() == SectionBase::Regular && part.armExidx &&
|
} else if (s->kind() == SectionBase::Regular && part.armExidx &&
|
||||||
part.armExidx->addSection(cast<InputSection>(s))) {
|
part.armExidx->addSection(cast<InputSection>(s))) {
|
||||||
@ -217,7 +215,7 @@ static Defined *addAbsolute(StringRef name) {
|
|||||||
|
|
||||||
// The linker is expected to define some symbols depending on
|
// The linker is expected to define some symbols depending on
|
||||||
// the linking result. This function defines such symbols.
|
// the linking result. This function defines such symbols.
|
||||||
void elf::addReservedSymbols() {
|
void addReservedSymbols() {
|
||||||
if (config->emachine == EM_MIPS) {
|
if (config->emachine == EM_MIPS) {
|
||||||
// Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
|
// Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
|
||||||
// so that it points to an absolute address which by default is relative
|
// so that it points to an absolute address which by default is relative
|
||||||
@ -310,13 +308,23 @@ static OutputSection *findSection(StringRef name, unsigned partition = 1) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Out members.
|
template <class ELFT> void createSyntheticSections() {
|
||||||
template <class ELFT> static void createSyntheticSections() {
|
|
||||||
// Initialize all pointers with NULL. This is needed because
|
// Initialize all pointers with NULL. This is needed because
|
||||||
// you can call lld::elf::main more than once as a library.
|
// you can call lld::elf::main more than once as a library.
|
||||||
memset(&Out::first, 0, sizeof(Out));
|
memset(&Out::first, 0, sizeof(Out));
|
||||||
|
|
||||||
auto add = [](InputSectionBase *sec) { inputSections.push_back(sec); };
|
// Add the .interp section first because it is not a SyntheticSection.
|
||||||
|
// The removeUnusedSyntheticSections() function relies on the
|
||||||
|
// SyntheticSections coming last.
|
||||||
|
if (needsInterpSection()) {
|
||||||
|
for (size_t i = 1; i <= partitions.size(); ++i) {
|
||||||
|
InputSection *sec = createInterpSection();
|
||||||
|
sec->partition = i;
|
||||||
|
inputSections.push_back(sec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add = [](SyntheticSection *sec) { inputSections.push_back(sec); };
|
||||||
|
|
||||||
in.shStrTab = make<StringTableSection>(".shstrtab", false);
|
in.shStrTab = make<StringTableSection>(".shstrtab", false);
|
||||||
|
|
||||||
@ -355,8 +363,10 @@ template <class ELFT> static void createSyntheticSections() {
|
|||||||
add(sec);
|
add(sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringRef relaDynName = config->isRela ? ".rela.dyn" : ".rel.dyn";
|
||||||
|
|
||||||
for (Partition &part : partitions) {
|
for (Partition &part : partitions) {
|
||||||
auto add = [&](InputSectionBase *sec) {
|
auto add = [&](SyntheticSection *sec) {
|
||||||
sec->partition = part.getNumber();
|
sec->partition = part.getNumber();
|
||||||
inputSections.push_back(sec);
|
inputSections.push_back(sec);
|
||||||
};
|
};
|
||||||
@ -378,16 +388,11 @@ template <class ELFT> static void createSyntheticSections() {
|
|||||||
part.dynStrTab = make<StringTableSection>(".dynstr", true);
|
part.dynStrTab = make<StringTableSection>(".dynstr", true);
|
||||||
part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
|
part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
|
||||||
part.dynamic = make<DynamicSection<ELFT>>();
|
part.dynamic = make<DynamicSection<ELFT>>();
|
||||||
if (config->androidPackDynRelocs) {
|
if (config->androidPackDynRelocs)
|
||||||
part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>(
|
part.relaDyn = make<AndroidPackedRelocationSection<ELFT>>(relaDynName);
|
||||||
config->isRela ? ".rela.dyn" : ".rel.dyn");
|
else
|
||||||
} else {
|
part.relaDyn =
|
||||||
part.relaDyn = make<RelocationSection<ELFT>>(
|
make<RelocationSection<ELFT>>(relaDynName, config->zCombreloc);
|
||||||
config->isRela ? ".rela.dyn" : ".rel.dyn", config->zCombreloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsInterpSection())
|
|
||||||
add(createInterpSection());
|
|
||||||
|
|
||||||
if (config->hasDynSymTab) {
|
if (config->hasDynSymTab) {
|
||||||
part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
|
part.dynSymTab = make<SymbolTableSection<ELFT>>(*part.dynStrTab);
|
||||||
@ -396,7 +401,7 @@ template <class ELFT> static void createSyntheticSections() {
|
|||||||
part.verSym = make<VersionTableSection>();
|
part.verSym = make<VersionTableSection>();
|
||||||
add(part.verSym);
|
add(part.verSym);
|
||||||
|
|
||||||
if (!config->versionDefinitions.empty()) {
|
if (!namedVersionDefs().empty()) {
|
||||||
part.verDef = make<VersionDefinitionSection>();
|
part.verDef = make<VersionDefinitionSection>();
|
||||||
add(part.verDef);
|
add(part.verDef);
|
||||||
}
|
}
|
||||||
@ -476,11 +481,6 @@ template <class ELFT> static void createSyntheticSections() {
|
|||||||
add(in.ppc64LongBranchTarget);
|
add(in.ppc64LongBranchTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->emachine == EM_RISCV) {
|
|
||||||
in.riscvSdata = make<RISCVSdataSection>();
|
|
||||||
add(in.riscvSdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
in.gotPlt = make<GotPltSection>();
|
in.gotPlt = make<GotPltSection>();
|
||||||
add(in.gotPlt);
|
add(in.gotPlt);
|
||||||
in.igotPlt = make<IgotPltSection>();
|
in.igotPlt = make<IgotPltSection>();
|
||||||
@ -504,16 +504,14 @@ template <class ELFT> static void createSyntheticSections() {
|
|||||||
config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false);
|
config->isRela ? ".rela.plt" : ".rel.plt", /*sort=*/false);
|
||||||
add(in.relaPlt);
|
add(in.relaPlt);
|
||||||
|
|
||||||
// The relaIplt immediately follows .rel.plt (.rel.dyn for ARM) to ensure
|
// The relaIplt immediately follows .rel[a].dyn to ensure that the IRelative
|
||||||
// that the IRelative relocations are processed last by the dynamic loader.
|
// relocations are processed last by the dynamic loader. We cannot place the
|
||||||
// We cannot place the iplt section in .rel.dyn when Android relocation
|
// iplt section in .rel.dyn when Android relocation packing is enabled because
|
||||||
// packing is enabled because that would cause a section type mismatch.
|
// that would cause a section type mismatch. However, because the Android
|
||||||
// However, because the Android dynamic loader reads .rel.plt after .rel.dyn,
|
// dynamic loader reads .rel.plt after .rel.dyn, we can get the desired
|
||||||
// we can get the desired behaviour by placing the iplt section in .rel.plt.
|
// behaviour by placing the iplt section in .rel.plt.
|
||||||
in.relaIplt = make<RelocationSection<ELFT>>(
|
in.relaIplt = make<RelocationSection<ELFT>>(
|
||||||
(config->emachine == EM_ARM && !config->androidPackDynRelocs)
|
config->androidPackDynRelocs ? in.relaPlt->name : relaDynName,
|
||||||
? ".rel.dyn"
|
|
||||||
: in.relaPlt->name,
|
|
||||||
/*sort=*/false);
|
/*sort=*/false);
|
||||||
add(in.relaIplt);
|
add(in.relaIplt);
|
||||||
|
|
||||||
@ -544,29 +542,6 @@ template <class ELFT> static void createSyntheticSections() {
|
|||||||
|
|
||||||
// The main function of the writer.
|
// The main function of the writer.
|
||||||
template <class ELFT> void Writer<ELFT>::run() {
|
template <class ELFT> void Writer<ELFT>::run() {
|
||||||
// Make copies of any input sections that need to be copied into each
|
|
||||||
// partition.
|
|
||||||
copySectionsIntoPartitions<ELFT>();
|
|
||||||
|
|
||||||
// Create linker-synthesized sections such as .got or .plt.
|
|
||||||
// Such sections are of type input section.
|
|
||||||
createSyntheticSections<ELFT>();
|
|
||||||
|
|
||||||
// Some input sections that are used for exception handling need to be moved
|
|
||||||
// into synthetic sections. Do that now so that they aren't assigned to
|
|
||||||
// output sections in the usual way.
|
|
||||||
if (!config->relocatable)
|
|
||||||
combineEhSections<ELFT>();
|
|
||||||
|
|
||||||
// We want to process linker script commands. When SECTIONS command
|
|
||||||
// is given we let it create sections.
|
|
||||||
script->processSectionCommands();
|
|
||||||
|
|
||||||
// Linker scripts controls how input sections are assigned to output sections.
|
|
||||||
// Input sections that were not handled by scripts are called "orphans", and
|
|
||||||
// they are assigned to output sections by the default rule. Process that.
|
|
||||||
script->addOrphanSections();
|
|
||||||
|
|
||||||
if (config->discard != DiscardPolicy::All)
|
if (config->discard != DiscardPolicy::All)
|
||||||
copyLocalSymbols();
|
copyLocalSymbols();
|
||||||
|
|
||||||
@ -582,15 +557,14 @@ template <class ELFT> void Writer<ELFT>::run() {
|
|||||||
if (errorCount())
|
if (errorCount())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
script->assignAddresses();
|
|
||||||
|
|
||||||
// If -compressed-debug-sections is specified, we need to compress
|
// If -compressed-debug-sections is specified, we need to compress
|
||||||
// .debug_* sections. Do it right now because it changes the size of
|
// .debug_* sections. Do it right now because it changes the size of
|
||||||
// output sections.
|
// output sections.
|
||||||
for (OutputSection *sec : outputSections)
|
for (OutputSection *sec : outputSections)
|
||||||
sec->maybeCompress<ELFT>();
|
sec->maybeCompress<ELFT>();
|
||||||
|
|
||||||
script->allocateHeaders(mainPart->phdrs);
|
if (script->hasSectionsCommand)
|
||||||
|
script->allocateHeaders(mainPart->phdrs);
|
||||||
|
|
||||||
// Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
|
// Remove empty PT_LOAD to avoid causing the dynamic linker to try to mmap a
|
||||||
// 0 sized region. This has to be done late since only after assignAddresses
|
// 0 sized region. This has to be done late since only after assignAddresses
|
||||||
@ -622,7 +596,8 @@ template <class ELFT> void Writer<ELFT>::run() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (!config->oFormatBinary) {
|
if (!config->oFormatBinary) {
|
||||||
writeTrapInstr();
|
if (config->zSeparate != SeparateSegmentKind::None)
|
||||||
|
writeTrapInstr();
|
||||||
writeHeader();
|
writeHeader();
|
||||||
writeSections();
|
writeSections();
|
||||||
} else {
|
} else {
|
||||||
@ -738,7 +713,7 @@ template <class ELFT> void Writer<ELFT>::addSectionSymbols() {
|
|||||||
});
|
});
|
||||||
if (i == sec->sectionCommands.end())
|
if (i == sec->sectionCommands.end())
|
||||||
continue;
|
continue;
|
||||||
InputSection *isec = cast<InputSectionDescription>(*i)->sections[0];
|
InputSectionBase *isec = cast<InputSectionDescription>(*i)->sections[0];
|
||||||
|
|
||||||
// Relocations are not using REL[A] section symbols.
|
// Relocations are not using REL[A] section symbols.
|
||||||
if (isec->type == SHT_REL || isec->type == SHT_RELA)
|
if (isec->type == SHT_REL || isec->type == SHT_RELA)
|
||||||
@ -1070,7 +1045,7 @@ template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {
|
|||||||
ElfSym::globalOffsetTable->section = gotSection;
|
ElfSym::globalOffsetTable->section = gotSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
// .rela_iplt_{start,end} mark the start and the end of .rela.plt section.
|
// .rela_iplt_{start,end} mark the start and the end of in.relaIplt.
|
||||||
if (ElfSym::relaIpltStart && in.relaIplt->isNeeded()) {
|
if (ElfSym::relaIpltStart && in.relaIplt->isNeeded()) {
|
||||||
ElfSym::relaIpltStart->section = in.relaIplt;
|
ElfSym::relaIpltStart->section = in.relaIplt;
|
||||||
ElfSym::relaIpltEnd->section = in.relaIplt;
|
ElfSym::relaIpltEnd->section = in.relaIplt;
|
||||||
@ -1298,10 +1273,7 @@ sortISDBySectionOrder(InputSectionDescription *isd,
|
|||||||
}
|
}
|
||||||
orderedSections.push_back({isec, i->second});
|
orderedSections.push_back({isec, i->second});
|
||||||
}
|
}
|
||||||
llvm::sort(orderedSections, [&](std::pair<InputSection *, int> a,
|
llvm::sort(orderedSections, llvm::less_second());
|
||||||
std::pair<InputSection *, int> b) {
|
|
||||||
return a.second < b.second;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Find an insertion point for the ordered section list in the unordered
|
// Find an insertion point for the ordered section list in the unordered
|
||||||
// section list. On targets with limited-range branches, this is the mid-point
|
// section list. On targets with limited-range branches, this is the mid-point
|
||||||
@ -1536,6 +1508,12 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
|
|||||||
if (!(sec->flags & SHF_LINK_ORDER))
|
if (!(sec->flags & SHF_LINK_ORDER))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// The ARM.exidx section use SHF_LINK_ORDER, but we have consolidated
|
||||||
|
// this processing inside the ARMExidxsyntheticsection::finalizeContents().
|
||||||
|
if (!config->relocatable && config->emachine == EM_ARM &&
|
||||||
|
sec->type == SHT_ARM_EXIDX)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Link order may be distributed across several InputSectionDescriptions
|
// Link order may be distributed across several InputSectionDescriptions
|
||||||
// but sort must consider them all at once.
|
// but sort must consider them all at once.
|
||||||
std::vector<InputSection **> scriptSections;
|
std::vector<InputSection **> scriptSections;
|
||||||
@ -1545,14 +1523,16 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
|
|||||||
for (InputSection *&isec : isd->sections) {
|
for (InputSection *&isec : isd->sections) {
|
||||||
scriptSections.push_back(&isec);
|
scriptSections.push_back(&isec);
|
||||||
sections.push_back(isec);
|
sections.push_back(isec);
|
||||||
|
|
||||||
|
InputSection *link = isec->getLinkOrderDep();
|
||||||
|
if (!link->getParent())
|
||||||
|
error(toString(isec) + ": sh_link points to discarded section " +
|
||||||
|
toString(link));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The ARM.exidx section use SHF_LINK_ORDER, but we have consolidated
|
if (errorCount())
|
||||||
// this processing inside the ARMExidxsyntheticsection::finalizeContents().
|
|
||||||
if (!config->relocatable && config->emachine == EM_ARM &&
|
|
||||||
sec->type == SHT_ARM_EXIDX)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
llvm::stable_sort(sections, compareByFilePosition);
|
llvm::stable_sort(sections, compareByFilePosition);
|
||||||
@ -1569,21 +1549,30 @@ template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
|
|||||||
template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
|
template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
|
||||||
ThunkCreator tc;
|
ThunkCreator tc;
|
||||||
AArch64Err843419Patcher a64p;
|
AArch64Err843419Patcher a64p;
|
||||||
|
ARMErr657417Patcher a32p;
|
||||||
|
script->assignAddresses();
|
||||||
|
|
||||||
// For some targets, like x86, this loop iterates only once.
|
int assignPasses = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
bool changed = false;
|
bool changed = target->needsThunks && tc.createThunks(outputSections);
|
||||||
|
|
||||||
script->assignAddresses();
|
// With Thunk Size much smaller than branch range we expect to
|
||||||
|
// converge quickly; if we get to 10 something has gone wrong.
|
||||||
if (target->needsThunks)
|
if (changed && tc.pass >= 10) {
|
||||||
changed |= tc.createThunks(outputSections);
|
error("thunk creation not converged");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (config->fixCortexA53Errata843419) {
|
if (config->fixCortexA53Errata843419) {
|
||||||
if (changed)
|
if (changed)
|
||||||
script->assignAddresses();
|
script->assignAddresses();
|
||||||
changed |= a64p.createFixes();
|
changed |= a64p.createFixes();
|
||||||
}
|
}
|
||||||
|
if (config->fixCortexA8) {
|
||||||
|
if (changed)
|
||||||
|
script->assignAddresses();
|
||||||
|
changed |= a32p.createFixes();
|
||||||
|
}
|
||||||
|
|
||||||
if (in.mipsGot)
|
if (in.mipsGot)
|
||||||
in.mipsGot->updateAllocSize();
|
in.mipsGot->updateAllocSize();
|
||||||
@ -1594,8 +1583,19 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
|
|||||||
changed |= part.relrDyn->updateAllocSize();
|
changed |= part.relrDyn->updateAllocSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!changed)
|
const Defined *changedSym = script->assignAddresses();
|
||||||
return;
|
if (!changed) {
|
||||||
|
// Some symbols may be dependent on section addresses. When we break the
|
||||||
|
// loop, the symbol values are finalized because a previous
|
||||||
|
// assignAddresses() finalized section addresses.
|
||||||
|
if (!changedSym)
|
||||||
|
break;
|
||||||
|
if (++assignPasses == 5) {
|
||||||
|
errorOrWarn("assignment to symbol " + toString(*changedSym) +
|
||||||
|
" does not converge");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1655,13 +1655,13 @@ static bool computeIsPreemptible(const Symbol &b) {
|
|||||||
if (!b.isDefined())
|
if (!b.isDefined())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// If we have a dynamic list it specifies which local symbols are preemptible.
|
|
||||||
if (config->hasDynamicList)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!config->shared)
|
if (!config->shared)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// If the dynamic list is present, it specifies preemptable symbols in a DSO.
|
||||||
|
if (config->hasDynamicList)
|
||||||
|
return b.inDynamicList;
|
||||||
|
|
||||||
// -Bsymbolic means that definitions are not preempted.
|
// -Bsymbolic means that definitions are not preempted.
|
||||||
if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc()))
|
if (config->bsymbolic || (config->bsymbolicFunctions && b.isFunc()))
|
||||||
return false;
|
return false;
|
||||||
@ -1696,12 +1696,16 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
|
|||||||
// Define __rel[a]_iplt_{start,end} symbols if needed.
|
// Define __rel[a]_iplt_{start,end} symbols if needed.
|
||||||
addRelIpltSymbols();
|
addRelIpltSymbols();
|
||||||
|
|
||||||
// RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800 if not defined.
|
// RISC-V's gp can address +/- 2 KiB, set it to .sdata + 0x800. This symbol
|
||||||
// This symbol should only be defined in an executable.
|
// should only be defined in an executable. If .sdata does not exist, its
|
||||||
if (config->emachine == EM_RISCV && !config->shared)
|
// value/section does not matter but it has to be relative, so set its
|
||||||
|
// st_shndx arbitrarily to 1 (Out::elfHeader).
|
||||||
|
if (config->emachine == EM_RISCV && !config->shared) {
|
||||||
|
OutputSection *sec = findSection(".sdata");
|
||||||
ElfSym::riscvGlobalPointer =
|
ElfSym::riscvGlobalPointer =
|
||||||
addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800,
|
addOptionalRegular("__global_pointer$", sec ? sec : Out::elfHeader,
|
||||||
STV_DEFAULT, STB_GLOBAL);
|
0x800, STV_DEFAULT, STB_GLOBAL);
|
||||||
|
}
|
||||||
|
|
||||||
if (config->emachine == EM_X86_64) {
|
if (config->emachine == EM_X86_64) {
|
||||||
// On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
|
// On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
|
||||||
@ -1730,20 +1734,22 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
|
|||||||
for (Partition &part : partitions)
|
for (Partition &part : partitions)
|
||||||
finalizeSynthetic(part.ehFrame);
|
finalizeSynthetic(part.ehFrame);
|
||||||
|
|
||||||
symtab->forEachSymbol([](Symbol *s) {
|
symtab->forEachSymbol(
|
||||||
if (!s->isPreemptible)
|
[](Symbol *s) { s->isPreemptible = computeIsPreemptible(*s); });
|
||||||
s->isPreemptible = computeIsPreemptible(*s);
|
|
||||||
});
|
// Change values of linker-script-defined symbols from placeholders (assigned
|
||||||
|
// by declareSymbols) to actual definitions.
|
||||||
|
script->processSymbolAssignments();
|
||||||
|
|
||||||
// Scan relocations. This must be done after every symbol is declared so that
|
// Scan relocations. This must be done after every symbol is declared so that
|
||||||
// we can correctly decide if a dynamic relocation is needed.
|
// we can correctly decide if a dynamic relocation is needed. This is called
|
||||||
|
// after processSymbolAssignments() because it needs to know whether a
|
||||||
|
// linker-script-defined symbol is absolute.
|
||||||
if (!config->relocatable) {
|
if (!config->relocatable) {
|
||||||
forEachRelSec(scanRelocations<ELFT>);
|
forEachRelSec(scanRelocations<ELFT>);
|
||||||
reportUndefinedSymbols<ELFT>();
|
reportUndefinedSymbols<ELFT>();
|
||||||
}
|
}
|
||||||
|
|
||||||
addIRelativeRelocs();
|
|
||||||
|
|
||||||
if (in.plt && in.plt->isNeeded())
|
if (in.plt && in.plt->isNeeded())
|
||||||
in.plt->addSymbols();
|
in.plt->addSymbols();
|
||||||
if (in.iplt && in.iplt->isNeeded())
|
if (in.iplt && in.iplt->isNeeded())
|
||||||
@ -1880,7 +1886,6 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
|
|||||||
finalizeSynthetic(in.plt);
|
finalizeSynthetic(in.plt);
|
||||||
finalizeSynthetic(in.iplt);
|
finalizeSynthetic(in.iplt);
|
||||||
finalizeSynthetic(in.ppc32Got2);
|
finalizeSynthetic(in.ppc32Got2);
|
||||||
finalizeSynthetic(in.riscvSdata);
|
|
||||||
finalizeSynthetic(in.partIndex);
|
finalizeSynthetic(in.partIndex);
|
||||||
|
|
||||||
// Dynamic section must be the last one in this list and dynamic
|
// Dynamic section must be the last one in this list and dynamic
|
||||||
@ -1905,6 +1910,8 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
|
|||||||
// SHFLinkOrder processing must be processed after relative section placements are
|
// SHFLinkOrder processing must be processed after relative section placements are
|
||||||
// known but before addresses are allocated.
|
// known but before addresses are allocated.
|
||||||
resolveShfLinkOrder();
|
resolveShfLinkOrder();
|
||||||
|
if (errorCount())
|
||||||
|
return;
|
||||||
|
|
||||||
// This is used to:
|
// This is used to:
|
||||||
// 1) Create "thunks":
|
// 1) Create "thunks":
|
||||||
@ -2049,27 +2056,32 @@ std::vector<PhdrEntry *> Writer<ELFT>::createPhdrs(Partition &part) {
|
|||||||
unsigned partNo = part.getNumber();
|
unsigned partNo = part.getNumber();
|
||||||
bool isMain = partNo == 1;
|
bool isMain = partNo == 1;
|
||||||
|
|
||||||
// The first phdr entry is PT_PHDR which describes the program header itself.
|
|
||||||
if (isMain)
|
|
||||||
addHdr(PT_PHDR, PF_R)->add(Out::programHeaders);
|
|
||||||
else
|
|
||||||
addHdr(PT_PHDR, PF_R)->add(part.programHeaders->getParent());
|
|
||||||
|
|
||||||
// PT_INTERP must be the second entry if exists.
|
|
||||||
if (OutputSection *cmd = findSection(".interp", partNo))
|
|
||||||
addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd);
|
|
||||||
|
|
||||||
// Add the first PT_LOAD segment for regular output sections.
|
// Add the first PT_LOAD segment for regular output sections.
|
||||||
uint64_t flags = computeFlags(PF_R);
|
uint64_t flags = computeFlags(PF_R);
|
||||||
PhdrEntry *load = nullptr;
|
PhdrEntry *load = nullptr;
|
||||||
|
|
||||||
// Add the headers. We will remove them if they don't fit.
|
// nmagic or omagic output does not have PT_PHDR, PT_INTERP, or the readonly
|
||||||
// In the other partitions the headers are ordinary sections, so they don't
|
// PT_LOAD.
|
||||||
// need to be added here.
|
if (!config->nmagic && !config->omagic) {
|
||||||
if (isMain) {
|
// The first phdr entry is PT_PHDR which describes the program header
|
||||||
load = addHdr(PT_LOAD, flags);
|
// itself.
|
||||||
load->add(Out::elfHeader);
|
if (isMain)
|
||||||
load->add(Out::programHeaders);
|
addHdr(PT_PHDR, PF_R)->add(Out::programHeaders);
|
||||||
|
else
|
||||||
|
addHdr(PT_PHDR, PF_R)->add(part.programHeaders->getParent());
|
||||||
|
|
||||||
|
// PT_INTERP must be the second entry if exists.
|
||||||
|
if (OutputSection *cmd = findSection(".interp", partNo))
|
||||||
|
addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd);
|
||||||
|
|
||||||
|
// Add the headers. We will remove them if they don't fit.
|
||||||
|
// In the other partitions the headers are ordinary sections, so they don't
|
||||||
|
// need to be added here.
|
||||||
|
if (isMain) {
|
||||||
|
load = addHdr(PT_LOAD, flags);
|
||||||
|
load->add(Out::elfHeader);
|
||||||
|
load->add(Out::programHeaders);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PT_GNU_RELRO includes all sections that should be marked as
|
// PT_GNU_RELRO includes all sections that should be marked as
|
||||||
@ -2208,21 +2220,68 @@ void Writer<ELFT>::addPhdrForSection(Partition &part, unsigned shType,
|
|||||||
part.phdrs.push_back(entry);
|
part.phdrs.push_back(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first section of each PT_LOAD, the first section in PT_GNU_RELRO and the
|
// Place the first section of each PT_LOAD to a different page (of maxPageSize).
|
||||||
// first section after PT_GNU_RELRO have to be page aligned so that the dynamic
|
// This is achieved by assigning an alignment expression to addrExpr of each
|
||||||
// linker can set the permissions.
|
// such section.
|
||||||
template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
|
template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
|
||||||
auto pageAlign = [](OutputSection *cmd) {
|
const PhdrEntry *prev;
|
||||||
if (cmd && !cmd->addrExpr)
|
auto pageAlign = [&](const PhdrEntry *p) {
|
||||||
cmd->addrExpr = [=] {
|
OutputSection *cmd = p->firstSec;
|
||||||
return alignTo(script->getDot(), config->maxPageSize);
|
if (cmd && !cmd->addrExpr) {
|
||||||
};
|
// Prefer advancing to align(dot, maxPageSize) + dot%maxPageSize to avoid
|
||||||
|
// padding in the file contents.
|
||||||
|
//
|
||||||
|
// When -z separate-code is used we must not have any overlap in pages
|
||||||
|
// between an executable segment and a non-executable segment. We align to
|
||||||
|
// the next maximum page size boundary on transitions between executable
|
||||||
|
// and non-executable segments.
|
||||||
|
//
|
||||||
|
// SHT_LLVM_PART_EHDR marks the start of a partition. The partition
|
||||||
|
// sections will be extracted to a separate file. Align to the next
|
||||||
|
// maximum page size boundary so that we can find the ELF header at the
|
||||||
|
// start. We cannot benefit from overlapping p_offset ranges with the
|
||||||
|
// previous segment anyway.
|
||||||
|
if (config->zSeparate == SeparateSegmentKind::Loadable ||
|
||||||
|
(config->zSeparate == SeparateSegmentKind::Code && prev &&
|
||||||
|
(prev->p_flags & PF_X) != (p->p_flags & PF_X)) ||
|
||||||
|
cmd->type == SHT_LLVM_PART_EHDR)
|
||||||
|
cmd->addrExpr = [] {
|
||||||
|
return alignTo(script->getDot(), config->maxPageSize);
|
||||||
|
};
|
||||||
|
// PT_TLS is at the start of the first RW PT_LOAD. If `p` includes PT_TLS,
|
||||||
|
// it must be the RW. Align to p_align(PT_TLS) to make sure
|
||||||
|
// p_vaddr(PT_LOAD)%p_align(PT_LOAD) = 0. Otherwise, if
|
||||||
|
// sh_addralign(.tdata) < sh_addralign(.tbss), we will set p_align(PT_TLS)
|
||||||
|
// to sh_addralign(.tbss), while p_vaddr(PT_TLS)=p_vaddr(PT_LOAD) may not
|
||||||
|
// be congruent to 0 modulo p_align(PT_TLS).
|
||||||
|
//
|
||||||
|
// Technically this is not required, but as of 2019, some dynamic loaders
|
||||||
|
// don't handle p_vaddr%p_align != 0 correctly, e.g. glibc (i386 and
|
||||||
|
// x86-64) doesn't make runtime address congruent to p_vaddr modulo
|
||||||
|
// p_align for dynamic TLS blocks (PR/24606), FreeBSD rtld has the same
|
||||||
|
// bug, musl (TLS Variant 1 architectures) before 1.1.23 handled TLS
|
||||||
|
// blocks correctly. We need to keep the workaround for a while.
|
||||||
|
else if (Out::tlsPhdr && Out::tlsPhdr->firstSec == p->firstSec)
|
||||||
|
cmd->addrExpr = [] {
|
||||||
|
return alignTo(script->getDot(), config->maxPageSize) +
|
||||||
|
alignTo(script->getDot() % config->maxPageSize,
|
||||||
|
Out::tlsPhdr->p_align);
|
||||||
|
};
|
||||||
|
else
|
||||||
|
cmd->addrExpr = [] {
|
||||||
|
return alignTo(script->getDot(), config->maxPageSize) +
|
||||||
|
script->getDot() % config->maxPageSize;
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (Partition &part : partitions) {
|
for (Partition &part : partitions) {
|
||||||
|
prev = nullptr;
|
||||||
for (const PhdrEntry *p : part.phdrs)
|
for (const PhdrEntry *p : part.phdrs)
|
||||||
if (p->p_type == PT_LOAD && p->firstSec)
|
if (p->p_type == PT_LOAD && p->firstSec) {
|
||||||
pageAlign(p->firstSec);
|
pageAlign(p);
|
||||||
|
prev = p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2231,12 +2290,9 @@ template <class ELFT> void Writer<ELFT>::fixSectionAlignments() {
|
|||||||
// load executables without any address adjustment.
|
// load executables without any address adjustment.
|
||||||
static uint64_t computeFileOffset(OutputSection *os, uint64_t off) {
|
static uint64_t computeFileOffset(OutputSection *os, uint64_t off) {
|
||||||
// The first section in a PT_LOAD has to have congruent offset and address
|
// The first section in a PT_LOAD has to have congruent offset and address
|
||||||
// module the page size.
|
// modulo the maximum page size.
|
||||||
if (os->ptLoad && os->ptLoad->firstSec == os) {
|
if (os->ptLoad && os->ptLoad->firstSec == os)
|
||||||
uint64_t alignment =
|
return alignTo(off, os->ptLoad->p_align, os->addr);
|
||||||
std::max<uint64_t>(os->ptLoad->p_align, config->maxPageSize);
|
|
||||||
return alignTo(off, alignment, os->addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// File offsets are not significant for .bss sections other than the first one
|
// File offsets are not significant for .bss sections other than the first one
|
||||||
// in a PT_LOAD. By convention, we keep section offsets monotonically
|
// in a PT_LOAD. By convention, we keep section offsets monotonically
|
||||||
@ -2291,13 +2347,12 @@ template <class ELFT> void Writer<ELFT>::assignFileOffsets() {
|
|||||||
|
|
||||||
for (OutputSection *sec : outputSections) {
|
for (OutputSection *sec : outputSections) {
|
||||||
off = setFileOffset(sec, off);
|
off = setFileOffset(sec, off);
|
||||||
if (script->hasSectionsCommand)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If this is a last section of the last executable segment and that
|
// If this is a last section of the last executable segment and that
|
||||||
// segment is the last loadable segment, align the offset of the
|
// segment is the last loadable segment, align the offset of the
|
||||||
// following section to avoid loading non-segments parts of the file.
|
// following section to avoid loading non-segments parts of the file.
|
||||||
if (lastRX && lastRX->lastSec == sec)
|
if (config->zSeparate != SeparateSegmentKind::None && lastRX &&
|
||||||
|
lastRX->lastSec == sec)
|
||||||
off = alignTo(off, config->commonPageSize);
|
off = alignTo(off, config->commonPageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2348,14 +2403,13 @@ template <class ELFT> void Writer<ELFT>::setPhdrs(Partition &part) {
|
|||||||
p->p_paddr = first->getLMA();
|
p->p_paddr = first->getLMA();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->p_type == PT_LOAD) {
|
if (p->p_type == PT_GNU_RELRO) {
|
||||||
p->p_align = std::max<uint64_t>(p->p_align, config->maxPageSize);
|
|
||||||
} else if (p->p_type == PT_GNU_RELRO) {
|
|
||||||
p->p_align = 1;
|
p->p_align = 1;
|
||||||
// The glibc dynamic loader rounds the size down, so we need to round up
|
// musl/glibc ld.so rounds the size down, so we need to round up
|
||||||
// to protect the last page. This is a no-op on FreeBSD which always
|
// to protect the last page. This is a no-op on FreeBSD which always
|
||||||
// rounds up.
|
// rounds up.
|
||||||
p->p_memsz = alignTo(p->p_memsz, config->commonPageSize);
|
p->p_memsz = alignTo(p->p_offset + p->p_memsz, config->commonPageSize) -
|
||||||
|
p->p_offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2570,9 +2624,6 @@ static void fillTrap(uint8_t *i, uint8_t *end) {
|
|||||||
// We'll leave other pages in segments as-is because the rest will be
|
// We'll leave other pages in segments as-is because the rest will be
|
||||||
// overwritten by output sections.
|
// overwritten by output sections.
|
||||||
template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
|
template <class ELFT> void Writer<ELFT>::writeTrapInstr() {
|
||||||
if (script->hasSectionsCommand)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (Partition &part : partitions) {
|
for (Partition &part : partitions) {
|
||||||
// Fill the last page.
|
// Fill the last page.
|
||||||
for (PhdrEntry *p : part.phdrs)
|
for (PhdrEntry *p : part.phdrs)
|
||||||
@ -2685,7 +2736,15 @@ template <class ELFT> void Writer<ELFT>::writeBuildId() {
|
|||||||
part.buildId->writeBuildId(buildId);
|
part.buildId->writeBuildId(buildId);
|
||||||
}
|
}
|
||||||
|
|
||||||
template void elf::writeResult<ELF32LE>();
|
template void createSyntheticSections<ELF32LE>();
|
||||||
template void elf::writeResult<ELF32BE>();
|
template void createSyntheticSections<ELF32BE>();
|
||||||
template void elf::writeResult<ELF64LE>();
|
template void createSyntheticSections<ELF64LE>();
|
||||||
template void elf::writeResult<ELF64BE>();
|
template void createSyntheticSections<ELF64BE>();
|
||||||
|
|
||||||
|
template void writeResult<ELF32LE>();
|
||||||
|
template void writeResult<ELF32BE>();
|
||||||
|
template void writeResult<ELF64LE>();
|
||||||
|
template void writeResult<ELF64BE>();
|
||||||
|
|
||||||
|
} // namespace elf
|
||||||
|
} // namespace lld
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#ifndef LLD_ELF_WRITER_H
|
#ifndef LLD_ELF_WRITER_H
|
||||||
#define LLD_ELF_WRITER_H
|
#define LLD_ELF_WRITER_H
|
||||||
|
|
||||||
|
#include "Config.h"
|
||||||
#include "llvm/ADT/ArrayRef.h"
|
#include "llvm/ADT/ArrayRef.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -19,13 +20,18 @@ namespace elf {
|
|||||||
class InputFile;
|
class InputFile;
|
||||||
class OutputSection;
|
class OutputSection;
|
||||||
class InputSectionBase;
|
class InputSectionBase;
|
||||||
|
void copySectionsIntoPartitions();
|
||||||
|
template <class ELFT> void createSyntheticSections();
|
||||||
|
void combineEhSections();
|
||||||
template <class ELFT> void writeResult();
|
template <class ELFT> void writeResult();
|
||||||
|
|
||||||
// This describes a program header entry.
|
// This describes a program header entry.
|
||||||
// Each contains type, access flags and range of output sections that will be
|
// Each contains type, access flags and range of output sections that will be
|
||||||
// placed in it.
|
// placed in it.
|
||||||
struct PhdrEntry {
|
struct PhdrEntry {
|
||||||
PhdrEntry(unsigned type, unsigned flags) : p_type(type), p_flags(flags) {}
|
PhdrEntry(unsigned type, unsigned flags)
|
||||||
|
: p_align(type == llvm::ELF::PT_LOAD ? config->maxPageSize : 0),
|
||||||
|
p_type(type), p_flags(flags) {}
|
||||||
void add(OutputSection *sec);
|
void add(OutputSection *sec);
|
||||||
|
|
||||||
uint64_t p_paddr = 0;
|
uint64_t p_paddr = 0;
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
=======================
|
========================
|
||||||
lld 9.0.0 Release Notes
|
lld 10.0.0 Release Notes
|
||||||
=======================
|
========================
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
:local:
|
:local:
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
These are in-progress notes for the upcoming LLVM 10.0.0 release.
|
||||||
|
Release notes for previous releases can be found on
|
||||||
|
`the Download Page <https://releases.llvm.org/download.html>`_.
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
============
|
============
|
||||||
|
|
||||||
lld is a high-performance linker that supports ELF (Unix), COFF
|
This document contains the release notes for the lld linker, release 10.0.0.
|
||||||
(Windows), Mach-O (macOS), MinGW and WebAssembly. lld is
|
Here we describe the status of lld, including major improvements
|
||||||
command-line-compatible with GNU linkers and Microsoft link.exe and is
|
from the previous release. All lld releases may be downloaded
|
||||||
significantly faster than the system default linkers.
|
from the `LLVM releases web site <https://llvm.org/releases/>`_.
|
||||||
|
|
||||||
lld 9 has lots of feature improvements and bug fixes.
|
|
||||||
|
|
||||||
Non-comprehensive list of changes in this release
|
Non-comprehensive list of changes in this release
|
||||||
=================================================
|
=================================================
|
||||||
@ -21,211 +24,31 @@ Non-comprehensive list of changes in this release
|
|||||||
ELF Improvements
|
ELF Improvements
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* ld.lld now has typo suggestions for flags:
|
* Glob pattern, which you can use in linker scripts or version scripts,
|
||||||
``$ ld.lld --call-shared`` now prints
|
now supports `\` and `[!...]`. Except character classes
|
||||||
``unknown argument '--call-shared', did you mean '--call_shared'``.
|
(e.g. `[[:digit:]]`), lld's glob pattern should be fully compatible
|
||||||
(`r361518 <https://reviews.llvm.org/rL361518>`_)
|
with GNU now. (`r375051
|
||||||
|
<https://github.com/llvm/llvm-project/commit/48993d5ab9413f0e5b94dfa292a233ce55b09e3e>`_)
|
||||||
* ``--allow-shlib-undefined`` and ``--no-allow-shlib-undefined``
|
|
||||||
options are added. ``--no-allow-shlib-undefined`` is the default for
|
|
||||||
executables.
|
|
||||||
(`r352826 <https://reviews.llvm.org/rL352826>`_)
|
|
||||||
|
|
||||||
* ``-nmagic`` and ``-omagic`` options are fully supported.
|
|
||||||
(`r360593 <https://reviews.llvm.org/rL360593>`_)
|
|
||||||
|
|
||||||
* Segment layout has changed. PT_GNU_RELRO, which was previously
|
|
||||||
placed in the middle of readable/writable PT_LOAD segments, is now
|
|
||||||
placed at the beginning of them. This change permits lld-produced
|
|
||||||
ELF files to be read correctly by GNU strip older than 2.31, which
|
|
||||||
has a bug to discard a PT_GNU_RELRO in the former layout.
|
|
||||||
|
|
||||||
* ``-z common-page-size`` is supported.
|
|
||||||
(`r360593 <https://reviews.llvm.org/rL360593>`_)
|
|
||||||
|
|
||||||
* Diagnostics messages have improved. A new flag ``--vs-diagnostics``
|
|
||||||
alters the format of diagnostic output to enable source hyperlinks
|
|
||||||
in Microsoft Visual Studio IDE.
|
|
||||||
|
|
||||||
* Linker script compatibility with GNU BFD linker has generally improved.
|
|
||||||
|
|
||||||
* The clang ``--dependent-library`` form of autolinking is supported.
|
|
||||||
|
|
||||||
This feature is added to implement the Windows-style autolinking for
|
|
||||||
Unix. On Unix, in order to use a library, you usually have to
|
|
||||||
include a header file provided by the library and then explicitly
|
|
||||||
link the library with the linker ``-l`` option. On Windows, header
|
|
||||||
files usually contain pragmas that list needed libraries. Compilers
|
|
||||||
copy that information to object files, so that linkers can
|
|
||||||
automatically link needed libraries. ``--dependent-library`` is
|
|
||||||
added for implementing that Windows semantics on Unix.
|
|
||||||
(`r360984 <https://reviews.llvm.org/rL360984>`_)
|
|
||||||
|
|
||||||
* AArch64 BTI and PAC are supported.
|
|
||||||
(`r362793 <https://reviews.llvm.org/rL362793>`_)
|
|
||||||
|
|
||||||
* lld now supports replacing ``JAL`` with ``JALX`` instructions in case
|
|
||||||
of MIPS-microMIPS cross-mode jumps.
|
|
||||||
(`r354311 <https://reviews.llvm.org/rL354311>`_)
|
|
||||||
|
|
||||||
* lld now creates LA25 thunks for MIPS R6 code.
|
|
||||||
(`r354312 <https://reviews.llvm.org/rL354312>`_)
|
|
||||||
|
|
||||||
* Put MIPS-specific .reginfo, .MIPS.options, and .MIPS.abiflags sections
|
|
||||||
into corresponding PT_MIPS_REGINFO, PT_MIPS_OPTIONS, and PT_MIPS_ABIFLAGS
|
|
||||||
segments.
|
|
||||||
|
|
||||||
* The quality of RISC-V and PowerPC ports have greatly improved. Many
|
|
||||||
applications can now be linked by lld. PowerPC64 is now almost
|
|
||||||
production ready.
|
|
||||||
|
|
||||||
* The Linux kernel for arm32_7, arm64, ppc64le and x86_64 can now be
|
|
||||||
linked by lld.
|
|
||||||
|
|
||||||
* x86-64 TLSDESC is supported.
|
|
||||||
(`r361911 <https://reviews.llvm.org/rL361911>`_,
|
|
||||||
`r362078 <https://reviews.llvm.org/rL362078>`_)
|
|
||||||
|
|
||||||
* DF_STATIC_TLS flag is set for i386 and x86-64 when needed.
|
|
||||||
(`r353293 <https://reviews.llvm.org/rL353293>`_,
|
|
||||||
`r353378 <https://reviews.llvm.org/rL353378>`_)
|
|
||||||
|
|
||||||
* The experimental partitioning feature is added to allow a program to
|
|
||||||
be split into multiple pieces.
|
|
||||||
|
|
||||||
The feature allows you to semi-automatically split a single program
|
|
||||||
into multiple ELF files called "partitions". Since all partitions
|
|
||||||
share the same memory address space and don't use PLT/GOT, split
|
|
||||||
programs run as fast as regular programs.
|
|
||||||
|
|
||||||
With the mechanism, you can start a program only with a "main"
|
|
||||||
partition and load remaining partitions on-demand. For example, you
|
|
||||||
can split a web browser into a main partition and a PDF reader
|
|
||||||
sub-partition and load the PDF reader partition only when a user
|
|
||||||
tries to open a PDF file.
|
|
||||||
|
|
||||||
See `the documentation <Partitions.html>`_ for more information.
|
|
||||||
|
|
||||||
* If "-" is given as an output filename, lld writes the final result
|
|
||||||
to the standard output. Previously, it created a file "-" in the
|
|
||||||
current directory.
|
|
||||||
(`r351852 <https://reviews.llvm.org/rL351852>`_)
|
|
||||||
|
|
||||||
* ``-z ifunc-noplt`` option is added to reduce IFunc function call
|
|
||||||
overhead in a freestanding environment such as the OS kernel.
|
|
||||||
|
|
||||||
Functions resolved by the IFunc mechanism are usually dispatched via
|
|
||||||
PLT and thus slower than regular functions because of the cost of
|
|
||||||
indirection. With ``-z ifunc-noplt``, you can eliminate it by doing
|
|
||||||
text relocations at load-time. You need a special loader to utilize
|
|
||||||
this feature. This feature is added for the FreeBSD kernel but can
|
|
||||||
be used by any operating systems.
|
|
||||||
(`r360685 <https://reviews.llvm.org/rL360685>`_)
|
|
||||||
|
|
||||||
* ``--undefined-glob`` option is added. The new option is an extension
|
|
||||||
to ``--undefined`` to take a glob pattern instead of a single symbol
|
|
||||||
name.
|
|
||||||
(`r363396 <https://reviews.llvm.org/rL363396>`_)
|
|
||||||
|
|
||||||
|
|
||||||
COFF Improvements
|
COFF Improvements
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
* Like the ELF driver, lld-link now has typo suggestions for flags.
|
* ...
|
||||||
(`r361518 <https://reviews.llvm.org/rL361518>`_)
|
|
||||||
|
|
||||||
* lld-link now correctly reports duplicate symbol errors for object
|
|
||||||
files that were compiled with ``/Gy``.
|
|
||||||
(`r352590 <https://reviews.llvm.org/rL352590>`_)
|
|
||||||
|
|
||||||
* lld-link now correctly reports duplicate symbol errors when several
|
|
||||||
resource (.res) input files define resources with the same type,
|
|
||||||
name and language. This can be demoted to a warning using
|
|
||||||
``/force:multipleres``.
|
|
||||||
(`r359829 <https://reviews.llvm.org/rL359829>`_)
|
|
||||||
|
|
||||||
* lld-link now rejects more than one resource object input files,
|
|
||||||
matching link.exe. Previously, lld-link would silently ignore all
|
|
||||||
but one. If you hit this: Don't pass resource object files to the
|
|
||||||
linker, instead pass res files to the linker directly. Don't put
|
|
||||||
resource files in static libraries, pass them on the command line.
|
|
||||||
(`r359749 <https://reviews.llvm.org/rL359749>`_)
|
|
||||||
|
|
||||||
* Having more than two ``/natvis:`` now works correctly; it used to not
|
|
||||||
work for larger binaries before.
|
|
||||||
(`r359515 <https://reviews.llvm.org/rL359515>`_)
|
|
||||||
|
|
||||||
* Undefined symbols are now printed only in demangled form. Pass
|
|
||||||
``/demangle:no`` to see raw symbol names instead.
|
|
||||||
(`r355878 <https://reviews.llvm.org/rL355878>`_)
|
|
||||||
|
|
||||||
* Several speed and memory usage improvements.
|
|
||||||
|
|
||||||
* lld-link now supports resource object files created by GNU windres and
|
|
||||||
MS cvtres, not only llvm-cvtres.
|
|
||||||
|
|
||||||
* The generated thunks for delayimports now share the majority of code
|
|
||||||
among thunks, significantly reducing the overhead of using delayimport.
|
|
||||||
(`r365823 <https://reviews.llvm.org/rL365823>`_)
|
|
||||||
|
|
||||||
* ``IMAGE_REL_ARM{,64}_REL32`` relocations are supported.
|
|
||||||
(`r352325 <https://reviews.llvm.org/rL352325>`_)
|
|
||||||
|
|
||||||
* Range extension thunks for AArch64 are now supported, so lld can
|
|
||||||
create large executables for Windows/ARM64.
|
|
||||||
(`r352929 <https://reviews.llvm.org/rL352929>`_)
|
|
||||||
|
|
||||||
* The following flags have been added:
|
|
||||||
``/functionpadmin`` (`r354716 <https://reviews.llvm.org/rL354716>`_),
|
|
||||||
``/swaprun:`` (`r359192 <https://reviews.llvm.org/rL359192>`_),
|
|
||||||
``/threads:no`` (`r355029 <https://reviews.llvm.org/rL355029>`_),
|
|
||||||
``/filealign`` (`r361634 <https://reviews.llvm.org/rL361634>`_)
|
|
||||||
|
|
||||||
WebAssembly Improvements
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
* Imports from custom module names are supported.
|
|
||||||
(`r352828 <https://reviews.llvm.org/rL352828>`_)
|
|
||||||
|
|
||||||
* Symbols that are in llvm.used are now exported by default.
|
|
||||||
(`r353364 <https://reviews.llvm.org/rL353364>`_)
|
|
||||||
|
|
||||||
* Initial support for PIC and dynamic linking has landed.
|
|
||||||
(`r357022 <https://reviews.llvm.org/rL357022>`_)
|
|
||||||
|
|
||||||
* wasm-ld now add ``__start_``/``__stop_`` symbols for data sections.
|
|
||||||
(`r361236 <https://reviews.llvm.org/rL361236>`_)
|
|
||||||
|
|
||||||
* wasm-ld now doesn't report an error on archives without a symbol index.
|
|
||||||
(`r364338 <https://reviews.llvm.org/rL364338>`_)
|
|
||||||
|
|
||||||
* The following flags have been added:
|
|
||||||
``--emit-relocs`` (`r361635 <https://reviews.llvm.org/rL361635>`_),
|
|
||||||
``--wrap`` (`r361639 <https://reviews.llvm.org/rL361639>`_),
|
|
||||||
``--trace`` and ``--trace-symbol``
|
|
||||||
(`r353264 <https://reviews.llvm.org/rL353264>`_).
|
|
||||||
|
|
||||||
|
|
||||||
MinGW Improvements
|
MinGW Improvements
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* lld now correctly links crtend.o as the last object file, handling
|
* ...
|
||||||
terminators for the sections such as .eh_frame properly, fixing
|
|
||||||
DWARF exception handling with libgcc and gcc's crtend.o.
|
|
||||||
|
|
||||||
* lld now also handles DWARF unwind info generated by GCC, when linking
|
MachO Improvements
|
||||||
with libgcc.
|
------------------
|
||||||
|
|
||||||
* PDB output can be requested without manually specifying the PDB file
|
* Item 1.
|
||||||
name, with the new option ``-pdb=`` with an empty value to the option.
|
|
||||||
(The old existing syntax ``-pdb <filename>`` was more cumbersome to use
|
|
||||||
with an empty parameter value.)
|
|
||||||
|
|
||||||
* ``--no-insert-timestamp`` option is added as an alias to ``/timestamp:0``.
|
WebAssembly Improvements
|
||||||
(`r353145 <https://reviews.llvm.org/rL353145>`_)
|
------------------------
|
||||||
|
|
||||||
* Many more GNU ld options are now supported, which e.g. allows the lld
|
* `__data_end` and `__heap_base` are no longer exported by default,
|
||||||
MinGW frontend to be called by GCC.
|
as it's best to keep them internal when possible. They can be
|
||||||
|
explicitly exported with `--export=__data_end` and
|
||||||
* The following options are added: ``--exclude-all-symbols``,
|
`--export=__heap_base`, respectively.
|
||||||
``--appcontainer``, ``--undefined``
|
|
||||||
|
@ -109,7 +109,7 @@ trap at runtime (functions that contain only an ``unreachable`` instruction)
|
|||||||
and use these stub functions at the otherwise invalid call sites.
|
and use these stub functions at the otherwise invalid call sites.
|
||||||
|
|
||||||
The default behaviour is to generate these stub function and to produce
|
The default behaviour is to generate these stub function and to produce
|
||||||
a warning. The ``--falal-warnings`` flag can be used to disable this behaviour
|
a warning. The ``--fatal-warnings`` flag can be used to disable this behaviour
|
||||||
and error out if mismatched are found.
|
and error out if mismatched are found.
|
||||||
|
|
||||||
Imports and Exports
|
Imports and Exports
|
||||||
|
@ -48,9 +48,9 @@
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short version.
|
# The short version.
|
||||||
version = '9'
|
version = '10'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '9'
|
release = '10'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
.Nm ld.lld
|
.Nm ld.lld
|
||||||
.Op Ar options
|
.Op Ar options
|
||||||
.Ar objfile ...
|
.Ar objfile ...
|
||||||
|
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
A linker takes one or more object, archive, and library files, and combines
|
A linker takes one or more object, archive, and library files, and combines
|
||||||
them into an output file (an executable, a shared library, or another object
|
them into an output file (an executable, a shared library, or another object
|
||||||
@ -40,7 +39,6 @@ All these targets are always supported however
|
|||||||
was built, so you can always use
|
was built, so you can always use
|
||||||
.Nm
|
.Nm
|
||||||
as a native linker as well as a cross linker.
|
as a native linker as well as a cross linker.
|
||||||
|
|
||||||
.Sh OPTIONS
|
.Sh OPTIONS
|
||||||
Many options have both a single-letter and long form.
|
Many options have both a single-letter and long form.
|
||||||
When using the long form options other than those beginning with the
|
When using the long form options other than those beginning with the
|
||||||
@ -175,8 +173,8 @@ A value of zero indicates that there is no limit.
|
|||||||
.It Fl -error-unresolved-symbols
|
.It Fl -error-unresolved-symbols
|
||||||
Report unresolved symbols as errors.
|
Report unresolved symbols as errors.
|
||||||
.It Fl -execute-only
|
.It Fl -execute-only
|
||||||
Mark executable sections unreadable. This option is currently only
|
Mark executable sections unreadable.
|
||||||
supported on AArch64.
|
This option is currently only supported on AArch64.
|
||||||
.It Fl -exclude-libs Ns = Ns Ar value
|
.It Fl -exclude-libs Ns = Ns Ar value
|
||||||
Exclude static libraries from automatic export.
|
Exclude static libraries from automatic export.
|
||||||
.It Fl -export-dynamic , Fl E
|
.It Fl -export-dynamic , Fl E
|
||||||
@ -232,13 +230,16 @@ Enable safe identical code folding.
|
|||||||
Disable identical code folding.
|
Disable identical code folding.
|
||||||
.It Fl -ignore-data-address-equality
|
.It Fl -ignore-data-address-equality
|
||||||
Ignore address equality of data. C/C++ requires each data to have a unique
|
Ignore address equality of data. C/C++ requires each data to have a unique
|
||||||
address. This option allows lld to do unsafe optimization that breaks the
|
address.
|
||||||
|
This option allows lld to do unsafe optimization that breaks the
|
||||||
requirement: create copies of read-only data or merge two or more read-only data
|
requirement: create copies of read-only data or merge two or more read-only data
|
||||||
that happen to have the same value.
|
that happen to have the same value.
|
||||||
.It Fl -ignore-function-address-equality
|
.It Fl -ignore-function-address-equality
|
||||||
Ignore address equality of functions. This option allows non-PIC calls to a
|
Ignore address equality of functions.
|
||||||
function with non-default visibility in a shared object. The function may have
|
This option allows non-PIC calls to a function with non-default visibility in
|
||||||
different addresses within the executable and within the shared object.
|
a shared object.
|
||||||
|
The function may have different addresses within the executable and within the
|
||||||
|
shared object.
|
||||||
.It Fl -image-base Ns = Ns Ar value
|
.It Fl -image-base Ns = Ns Ar value
|
||||||
Set the base address to
|
Set the base address to
|
||||||
.Ar value .
|
.Ar value .
|
||||||
@ -344,7 +345,8 @@ is
|
|||||||
.Cm binary ,
|
.Cm binary ,
|
||||||
which produces output with no ELF header.
|
which produces output with no ELF header.
|
||||||
.It Fl -omagic , Fl N
|
.It Fl -omagic , Fl N
|
||||||
Set the text and data sections to be readable and writable, do not page align sections, link against static libraries.
|
Set the text and data sections to be readable and writable, do not page align
|
||||||
|
sections, link against static libraries.
|
||||||
.It Fl -opt-remarks-filename Ar file
|
.It Fl -opt-remarks-filename Ar file
|
||||||
Write optimization remarks in YAML format to
|
Write optimization remarks in YAML format to
|
||||||
.Ar file .
|
.Ar file .
|
||||||
@ -354,8 +356,8 @@ Filter optimization remarks by only allowing the passes matching
|
|||||||
.It Fl -opt-remarks-with-hotness
|
.It Fl -opt-remarks-with-hotness
|
||||||
Include hotness information in the optimization remarks file.
|
Include hotness information in the optimization remarks file.
|
||||||
.It Fl -orphan-handling Ns = Ns Ar mode
|
.It Fl -orphan-handling Ns = Ns Ar mode
|
||||||
Control how orphan sections are handled. An orphan section is one not
|
Control how orphan sections are handled.
|
||||||
specifically mentioned in a linker script.
|
An orphan section is one not specifically mentioned in a linker script.
|
||||||
.Ar mode
|
.Ar mode
|
||||||
may be:
|
may be:
|
||||||
.Pp
|
.Pp
|
||||||
@ -381,17 +383,21 @@ may be:
|
|||||||
.Pp
|
.Pp
|
||||||
.Bl -tag -width 2n -compact
|
.Bl -tag -width 2n -compact
|
||||||
.It Cm none
|
.It Cm none
|
||||||
Don't pack. Dynamic relocations are encoded in SHT_REL(A).
|
Do not pack.
|
||||||
|
Dynamic relocations are encoded in SHT_REL(A).
|
||||||
.It Cm android
|
.It Cm android
|
||||||
Pack dynamic relocations in SHT_ANDROID_REL(A).
|
Pack dynamic relocations in SHT_ANDROID_REL(A).
|
||||||
.It Cm relr
|
.It Cm relr
|
||||||
Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in SHT_REL(A).
|
Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in
|
||||||
|
SHT_REL(A).
|
||||||
.It Cm android+relr
|
.It Cm android+relr
|
||||||
Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in SHT_ANDROID_REL(A).
|
Pack relative relocations in SHT_RELR, and the rest of dynamic relocations in
|
||||||
|
SHT_ANDROID_REL(A).
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
.Cm none
|
.Cm none
|
||||||
is the default. If
|
is the default.
|
||||||
|
If
|
||||||
.Fl -use-android-relr-tags
|
.Fl -use-android-relr-tags
|
||||||
is specified, use SHT_ANDROID_RELR instead of SHT_RELR.
|
is specified, use SHT_ANDROID_RELR instead of SHT_RELR.
|
||||||
.Pp
|
.Pp
|
||||||
@ -418,8 +424,14 @@ Undo the effect of
|
|||||||
.Fl -push-state.
|
.Fl -push-state.
|
||||||
.It Fl -relocatable , Fl r
|
.It Fl -relocatable , Fl r
|
||||||
Create relocatable object file.
|
Create relocatable object file.
|
||||||
.It Fl -reproduce Ns = Ns Ar value
|
.It Fl -reproduce Ns = Ns Ar path
|
||||||
Dump linker invocation and input files for debugging.
|
Write a tar file to
|
||||||
|
.Ar path,
|
||||||
|
containing all the input files needed to reproduce the link, a text file called
|
||||||
|
response.txt containing the command line options and a text file called
|
||||||
|
version.txt containing the output of ld.lld --version.
|
||||||
|
The archive when
|
||||||
|
unpacked can be used to re-run the linker with the same options and input files.
|
||||||
.It Fl -retain-symbols-file Ns = Ns Ar file
|
.It Fl -retain-symbols-file Ns = Ns Ar file
|
||||||
Retain only the symbols listed in the file.
|
Retain only the symbols listed in the file.
|
||||||
.It Fl -rpath Ns = Ns Ar value , Fl R Ar value
|
.It Fl -rpath Ns = Ns Ar value , Fl R Ar value
|
||||||
@ -435,6 +447,8 @@ and
|
|||||||
.It Fl -script Ns = Ns Ar file , Fl T Ar file
|
.It Fl -script Ns = Ns Ar file , Fl T Ar file
|
||||||
Read linker script from
|
Read linker script from
|
||||||
.Ar file .
|
.Ar file .
|
||||||
|
If multiple linker scripts are given, they are processed as if they
|
||||||
|
were concatenated in the order they appeared on the command line.
|
||||||
.It Fl -section-start Ns = Ns Ar section Ns = Ns Ar address
|
.It Fl -section-start Ns = Ns Ar section Ns = Ns Ar address
|
||||||
Set address of section.
|
Set address of section.
|
||||||
.It Fl -shared , Fl -Bsharable
|
.It Fl -shared , Fl -Bsharable
|
||||||
@ -523,13 +537,15 @@ to include the object file in the output file.
|
|||||||
.It Fl -undefined-glob Ns = Ns Ar pattern
|
.It Fl -undefined-glob Ns = Ns Ar pattern
|
||||||
Synonym for
|
Synonym for
|
||||||
.Fl -undefined ,
|
.Fl -undefined ,
|
||||||
except that it takes a glob pattern. In a glob pattern,
|
except that it takes a glob pattern.
|
||||||
|
In a glob pattern,
|
||||||
.Cm *
|
.Cm *
|
||||||
matches zero or more characters,
|
matches zero or more characters,
|
||||||
.Cm ?
|
.Cm ?
|
||||||
matches any single character, and
|
matches any single character, and
|
||||||
.Cm [...]
|
.Cm [...]
|
||||||
matches the characters within brackets. All symbols that match
|
matches the characters within brackets.
|
||||||
|
All symbols that match
|
||||||
a given pattern are handled as if they were given as arguments of
|
a given pattern are handled as if they were given as arguments of
|
||||||
.Fl -undefined .
|
.Fl -undefined .
|
||||||
.It Fl -unresolved-symbols Ns = Ns Ar value
|
.It Fl -unresolved-symbols Ns = Ns Ar value
|
||||||
@ -556,13 +572,17 @@ Warn about duplicate common symbols.
|
|||||||
Warn about using ifunc symbols in conjunction with text relocations.
|
Warn about using ifunc symbols in conjunction with text relocations.
|
||||||
Older versions of glibc library (2.28 and earlier) has a bug that causes
|
Older versions of glibc library (2.28 and earlier) has a bug that causes
|
||||||
the segment that includes ifunc symbols to be marked as not executable when
|
the segment that includes ifunc symbols to be marked as not executable when
|
||||||
they are relocated. As a result, although the program compiles and links
|
they are relocated.
|
||||||
|
As a result, although the program compiles and links
|
||||||
successfully, it gives segmentation fault when the instruction pointer reaches
|
successfully, it gives segmentation fault when the instruction pointer reaches
|
||||||
an ifunc symbol. Use -warn-ifunc-textrel to let lld give a warning, if the
|
an ifunc symbol.
|
||||||
|
Use -warn-ifunc-textrel to let lld give a warning, if the
|
||||||
code may include ifunc symbols, may do text relocations and be linked with
|
code may include ifunc symbols, may do text relocations and be linked with
|
||||||
an older glibc version. Otherwise, there is no need to use it, as the default
|
an older glibc version.
|
||||||
value does not give a warning. This flag has been introduced in late 2018,
|
Otherwise, there is no need to use it, as the default value does not give a
|
||||||
has no counter part in ld and gold linkers, and may be removed in the future.
|
warning.
|
||||||
|
This flag has been introduced in late 2018, has no counter part in ld and gold
|
||||||
|
linkers, and may be removed in the future.
|
||||||
.It Fl -warn-unresolved-symbols
|
.It Fl -warn-unresolved-symbols
|
||||||
Report unresolved symbols as warnings.
|
Report unresolved symbols as warnings.
|
||||||
.It Fl -whole-archive
|
.It Fl -whole-archive
|
||||||
@ -571,18 +591,21 @@ Force load of all members in a static library.
|
|||||||
Use wrapper functions for symbol.
|
Use wrapper functions for symbol.
|
||||||
.It Fl z Ar option
|
.It Fl z Ar option
|
||||||
Linker option extensions.
|
Linker option extensions.
|
||||||
.Bl -tag -width indent
|
.Bl -tag -width indent -compact
|
||||||
|
.Pp
|
||||||
.It Cm execstack
|
.It Cm execstack
|
||||||
Make the main stack executable.
|
Make the main stack executable.
|
||||||
Stack permissions are recorded in the
|
Stack permissions are recorded in the
|
||||||
.Dv PT_GNU_STACK
|
.Dv PT_GNU_STACK
|
||||||
segment.
|
segment.
|
||||||
|
.Pp
|
||||||
.It Cm global
|
.It Cm global
|
||||||
Sets the
|
Sets the
|
||||||
.Dv DF_1_GLOBAL flag in the
|
.Dv DF_1_GLOBAL flag in the
|
||||||
.Dv DYNAMIC
|
.Dv DYNAMIC
|
||||||
section.
|
section.
|
||||||
Different loaders can decide how to handle this flag on their own.
|
Different loaders can decide how to handle this flag on their own.
|
||||||
|
.Pp
|
||||||
.It Cm ifunc-noplt
|
.It Cm ifunc-noplt
|
||||||
Do not emit PLT entries for ifunc symbols.
|
Do not emit PLT entries for ifunc symbols.
|
||||||
Instead, emit text relocations referencing the resolver.
|
Instead, emit text relocations referencing the resolver.
|
||||||
@ -591,64 +614,78 @@ environments where text relocations do not have the usual drawbacks.
|
|||||||
This option must be combined with the
|
This option must be combined with the
|
||||||
.Fl z Li notext
|
.Fl z Li notext
|
||||||
option.
|
option.
|
||||||
|
.Pp
|
||||||
.It Cm initfirst
|
.It Cm initfirst
|
||||||
Sets the
|
Sets the
|
||||||
.Dv DF_1_INITFIRST
|
.Dv DF_1_INITFIRST
|
||||||
flag to indicate the module should be initialized first.
|
flag to indicate the module should be initialized first.
|
||||||
|
.Pp
|
||||||
.It Cm interpose
|
.It Cm interpose
|
||||||
Set the
|
Set the
|
||||||
.Dv DF_1_INTERPOSE
|
.Dv DF_1_INTERPOSE
|
||||||
flag to indicate to the runtime linker that the object is an interposer.
|
flag to indicate to the runtime linker that the object is an interposer.
|
||||||
During symbol resolution interposers are searched after the application
|
During symbol resolution interposers are searched after the application
|
||||||
but before other dependencies.
|
but before other dependencies.
|
||||||
|
.Pp
|
||||||
.It Cm muldefs
|
.It Cm muldefs
|
||||||
Do not error if a symbol is defined multiple times.
|
Do not error if a symbol is defined multiple times.
|
||||||
The first definition will be used.
|
The first definition will be used.
|
||||||
This is a synonym for
|
This is a synonym for
|
||||||
.Fl -allow-multiple-definition.
|
.Fl -allow-multiple-definition.
|
||||||
|
.Pp
|
||||||
.It Cm nocombreloc
|
.It Cm nocombreloc
|
||||||
Disable combining and sorting multiple relocation sections.
|
Disable combining and sorting multiple relocation sections.
|
||||||
|
.Pp
|
||||||
.It Cm nocopyreloc
|
.It Cm nocopyreloc
|
||||||
Disable the creation of copy relocations.
|
Disable the creation of copy relocations.
|
||||||
|
.Pp
|
||||||
.It Cm nodefaultlib
|
.It Cm nodefaultlib
|
||||||
Set the
|
Set the
|
||||||
.Dv DF_1_NODEFLIB
|
.Dv DF_1_NODEFLIB
|
||||||
flag to indicate that default library search paths should be ignored.
|
flag to indicate that default library search paths should be ignored.
|
||||||
|
.Pp
|
||||||
.It Cm nodelete
|
.It Cm nodelete
|
||||||
Set the
|
Set the
|
||||||
.Dv DF_1_NODELETE
|
.Dv DF_1_NODELETE
|
||||||
flag to indicate that the object cannot be unloaded from a process.
|
flag to indicate that the object cannot be unloaded from a process.
|
||||||
|
.Pp
|
||||||
.It Cm nodlopen
|
.It Cm nodlopen
|
||||||
Set the
|
Set the
|
||||||
.Dv DF_1_NOOPEN
|
.Dv DF_1_NOOPEN
|
||||||
flag to indicate that the object may not be opened by
|
flag to indicate that the object may not be opened by
|
||||||
.Xr dlopen 3 .
|
.Xr dlopen 3 .
|
||||||
|
.Pp
|
||||||
.It Cm norelro
|
.It Cm norelro
|
||||||
Do not indicate that portions of the object shold be mapped read-only
|
Do not indicate that portions of the object shold be mapped read-only
|
||||||
after initial relocation processing.
|
after initial relocation processing.
|
||||||
The object will omit the
|
The object will omit the
|
||||||
.Dv PT_GNU_RELRO
|
.Dv PT_GNU_RELRO
|
||||||
segment.
|
segment.
|
||||||
|
.Pp
|
||||||
.It Cm notext
|
.It Cm notext
|
||||||
Allow relocations against read-only segments.
|
Allow relocations against read-only segments.
|
||||||
Sets the
|
Sets the
|
||||||
.Dv DT_TEXTREL flag in the
|
.Dv DT_TEXTREL flag in the
|
||||||
.Dv DYNAMIC
|
.Dv DYNAMIC
|
||||||
section.
|
section.
|
||||||
|
.Pp
|
||||||
.It Cm now
|
.It Cm now
|
||||||
Set the
|
Set the
|
||||||
.Dv DF_BIND_NOW
|
.Dv DF_BIND_NOW
|
||||||
flag to indicate that the run-time loader should perform all relocation
|
flag to indicate that the run-time loader should perform all relocation
|
||||||
processing as part of object initialization.
|
processing as part of object initialization.
|
||||||
By default relocations may be performed on demand.
|
By default relocations may be performed on demand.
|
||||||
|
.Pp
|
||||||
.It Cm origin
|
.It Cm origin
|
||||||
Set the
|
Set the
|
||||||
.Dv DF_ORIGIN
|
.Dv DF_ORIGIN
|
||||||
flag to indicate that the object requires
|
flag to indicate that the object requires
|
||||||
$ORIGIN
|
$ORIGIN
|
||||||
processing.
|
processing.
|
||||||
|
.Pp
|
||||||
.It Cm retpolineplt
|
.It Cm retpolineplt
|
||||||
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
|
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
|
||||||
|
.Pp
|
||||||
.It Cm rodynamic
|
.It Cm rodynamic
|
||||||
Make the
|
Make the
|
||||||
.Li .dynamic
|
.Li .dynamic
|
||||||
@ -656,6 +693,18 @@ section read-only.
|
|||||||
The
|
The
|
||||||
.Dv DT_DEBUG
|
.Dv DT_DEBUG
|
||||||
tag will not be emitted.
|
tag will not be emitted.
|
||||||
|
.Pp
|
||||||
|
.It Cm separate-loadable-segments
|
||||||
|
.It Cm separate-code
|
||||||
|
.It Cm noseparate-code
|
||||||
|
Specify whether two adjacent PT_LOAD segments are allowed to overlap in pages.
|
||||||
|
.Cm noseparate-code
|
||||||
|
(default) allows overlap.
|
||||||
|
.Cm separate-code
|
||||||
|
allows overlap between two executable segments, or two non-executable segments.
|
||||||
|
.Cm separate-loadable-segments
|
||||||
|
disallows overlap.
|
||||||
|
.Pp
|
||||||
.It Cm stack-size Ns = Ns Ar size
|
.It Cm stack-size Ns = Ns Ar size
|
||||||
Set the main thread's stack size to
|
Set the main thread's stack size to
|
||||||
.Ar size .
|
.Ar size .
|
||||||
@ -663,9 +712,11 @@ The stack size is recorded as the size of the
|
|||||||
.Ar size .
|
.Ar size .
|
||||||
.Dv PT_GNU_STACK
|
.Dv PT_GNU_STACK
|
||||||
program segment.
|
program segment.
|
||||||
|
.Pp
|
||||||
.It Cm text
|
.It Cm text
|
||||||
Do not allow relocations against read-only segments.
|
Do not allow relocations against read-only segments.
|
||||||
This is the default.
|
This is the default.
|
||||||
|
.Pp
|
||||||
.It Cm wxneeded
|
.It Cm wxneeded
|
||||||
Create a
|
Create a
|
||||||
.Dv PT_OPENBSD_WXNEEDED
|
.Dv PT_OPENBSD_WXNEEDED
|
||||||
|
47
contrib/llvm-project/lld/include/lld/Common/DWARF.h
Normal file
47
contrib/llvm-project/lld/include/lld/Common/DWARF.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
//===- DWARF.h --------------------------------------------------*- C++ -*-===//
|
||||||
|
//
|
||||||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef LLD_DWARF_H
|
||||||
|
#define LLD_DWARF_H
|
||||||
|
|
||||||
|
#include "lld/Common/LLVM.h"
|
||||||
|
#include "llvm/ADT/DenseMap.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||||
|
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace llvm {
|
||||||
|
struct DILineInfo;
|
||||||
|
} // namespace llvm
|
||||||
|
|
||||||
|
namespace lld {
|
||||||
|
|
||||||
|
class DWARFCache {
|
||||||
|
public:
|
||||||
|
DWARFCache(std::unique_ptr<llvm::DWARFContext> dwarf);
|
||||||
|
llvm::Optional<llvm::DILineInfo> getDILineInfo(uint64_t offset,
|
||||||
|
uint64_t sectionIndex);
|
||||||
|
llvm::Optional<std::pair<std::string, unsigned>>
|
||||||
|
getVariableLoc(StringRef name);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<llvm::DWARFContext> dwarf;
|
||||||
|
std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
|
||||||
|
struct VarLoc {
|
||||||
|
const llvm::DWARFDebugLine::LineTable *lt;
|
||||||
|
unsigned file;
|
||||||
|
unsigned line;
|
||||||
|
};
|
||||||
|
llvm::DenseMap<StringRef, VarLoc> variableLoc;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace lld
|
||||||
|
|
||||||
|
#endif
|
@ -87,7 +87,6 @@ class ErrorHandler {
|
|||||||
StringRef errorLimitExceededMsg = "too many errors emitted, stopping now";
|
StringRef errorLimitExceededMsg = "too many errors emitted, stopping now";
|
||||||
StringRef logName = "lld";
|
StringRef logName = "lld";
|
||||||
llvm::raw_ostream *errorOS = &llvm::errs();
|
llvm::raw_ostream *errorOS = &llvm::errs();
|
||||||
bool colorDiagnostics = llvm::errs().has_colors();
|
|
||||||
bool exitEarly = true;
|
bool exitEarly = true;
|
||||||
bool fatalWarnings = false;
|
bool fatalWarnings = false;
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
@ -102,12 +101,16 @@ class ErrorHandler {
|
|||||||
std::unique_ptr<llvm::FileOutputBuffer> outputBuffer;
|
std::unique_ptr<llvm::FileOutputBuffer> outputBuffer;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void printHeader(StringRef s, raw_ostream::Colors c, const Twine &msg);
|
using Colors = raw_ostream::Colors;
|
||||||
|
|
||||||
|
std::string getLocation(const Twine &msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns the default error handler.
|
/// Returns the default error handler.
|
||||||
ErrorHandler &errorHandler();
|
ErrorHandler &errorHandler();
|
||||||
|
|
||||||
|
void enableColors(bool enable);
|
||||||
|
|
||||||
inline void error(const Twine &msg) { errorHandler().error(msg); }
|
inline void error(const Twine &msg) { errorHandler().error(msg); }
|
||||||
inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg) {
|
inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &msg) {
|
||||||
errorHandler().fatal(msg);
|
errorHandler().fatal(msg);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
// This should be the only #include, force #includes of all the others on
|
// This should be the only #include, force #includes of all the others on
|
||||||
// clients.
|
// clients.
|
||||||
#include "llvm/ADT/Hashing.h"
|
#include "llvm/ADT/Hashing.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include "llvm/Support/Casting.h"
|
#include "llvm/Support/Casting.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
@ -18,9 +18,8 @@
|
|||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
// Returns a demangled C++ symbol name. If Name is not a mangled
|
// Returns a demangled C++ symbol name. If Name is not a mangled
|
||||||
// name, it returns Optional::None.
|
// name, it returns name.
|
||||||
llvm::Optional<std::string> demangleItanium(llvm::StringRef name);
|
std::string demangleItanium(llvm::StringRef name);
|
||||||
llvm::Optional<std::string> demangleMSVC(llvm::StringRef s);
|
|
||||||
|
|
||||||
std::vector<uint8_t> parseHex(llvm::StringRef s);
|
std::vector<uint8_t> parseHex(llvm::StringRef s);
|
||||||
bool isValidCIdentifier(llvm::StringRef s);
|
bool isValidCIdentifier(llvm::StringRef s);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
namespace lld {
|
namespace lld {
|
||||||
llvm::TargetOptions initTargetOptionsFromCodeGenFlags();
|
llvm::TargetOptions initTargetOptionsFromCodeGenFlags();
|
||||||
|
llvm::Optional<llvm::Reloc::Model> getRelocModelFromCMModel();
|
||||||
llvm::Optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
|
llvm::Optional<llvm::CodeModel::Model> getCodeModelFromCMModel();
|
||||||
std::string getCPUStr();
|
std::string getCPUStr();
|
||||||
std::vector<std::string> getMAttrs();
|
std::vector<std::string> getMAttrs();
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "llvm/ADT/Optional.h"
|
#include "llvm/ADT/Optional.h"
|
||||||
#include "llvm/ADT/STLExtras.h"
|
#include "llvm/ADT/STLExtras.h"
|
||||||
#include "llvm/ADT/Twine.h"
|
#include "llvm/ADT/Twine.h"
|
||||||
|
#include "llvm/Support/Allocator.h"
|
||||||
#include "llvm/Support/ErrorHandling.h"
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -101,7 +101,7 @@ class MachOLinkingContext : public LinkingContext {
|
|||||||
auto file = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
auto file = std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
auto *filePtr = file.get();
|
auto *filePtr = file.get();
|
||||||
auto *ctx = const_cast<MachOLinkingContext *>(this);
|
auto *ctx = const_cast<MachOLinkingContext *>(this);
|
||||||
ctx->getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
|
ctx->getNodes().push_back(std::make_unique<FileNode>(std::move(file)));
|
||||||
return filePtr;
|
return filePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ class DarwinLdOptTable : public llvm::opt::OptTable {
|
|||||||
static std::vector<std::unique_ptr<File>>
|
static std::vector<std::unique_ptr<File>>
|
||||||
makeErrorFile(StringRef path, std::error_code ec) {
|
makeErrorFile(StringRef path, std::error_code ec) {
|
||||||
std::vector<std::unique_ptr<File>> result;
|
std::vector<std::unique_ptr<File>> result;
|
||||||
result.push_back(llvm::make_unique<ErrorFile>(path, ec));
|
result.push_back(std::make_unique<ErrorFile>(path, ec));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ static void addFile(StringRef path, MachOLinkingContext &ctx,
|
|||||||
std::vector<std::unique_ptr<File>> files =
|
std::vector<std::unique_ptr<File>> files =
|
||||||
loadFile(ctx, path, loadWholeArchive, upwardDylib);
|
loadFile(ctx, path, loadWholeArchive, upwardDylib);
|
||||||
for (std::unique_ptr<File> &file : files)
|
for (std::unique_ptr<File> &file : files)
|
||||||
ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file)));
|
ctx.getNodes().push_back(std::make_unique<FileNode>(std::move(file)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export lists are one symbol per line. Blank lines are ignored.
|
// Export lists are one symbol per line. Blank lines are ignored.
|
||||||
@ -1138,7 +1138,7 @@ static void createFiles(MachOLinkingContext &ctx, bool Implicit) {
|
|||||||
ctx.createInternalFiles(Files);
|
ctx.createInternalFiles(Files);
|
||||||
for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) {
|
for (auto i = Files.rbegin(), e = Files.rend(); i != e; ++i) {
|
||||||
auto &members = ctx.getNodes();
|
auto &members = ctx.getNodes();
|
||||||
members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i)));
|
members.insert(members.begin(), std::make_unique<FileNode>(std::move(*i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1151,7 +1151,7 @@ bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
|
|||||||
"'-error-limit 0' to see all errors)";
|
"'-error-limit 0' to see all errors)";
|
||||||
errorHandler().errorOS = &Error;
|
errorHandler().errorOS = &Error;
|
||||||
errorHandler().exitEarly = CanExitEarly;
|
errorHandler().exitEarly = CanExitEarly;
|
||||||
errorHandler().colorDiagnostics = Error.has_colors();
|
enableColors(Error.has_colors());
|
||||||
|
|
||||||
MachOLinkingContext ctx;
|
MachOLinkingContext ctx;
|
||||||
if (!parse(args, ctx))
|
if (!parse(args, ctx))
|
||||||
@ -1185,7 +1185,7 @@ bool link(llvm::ArrayRef<const char *> args, bool CanExitEarly,
|
|||||||
merged = mergedFile.get();
|
merged = mergedFile.get();
|
||||||
auto &members = ctx.getNodes();
|
auto &members = ctx.getNodes();
|
||||||
members.insert(members.begin(),
|
members.insert(members.begin(),
|
||||||
llvm::make_unique<FileNode>(std::move(mergedFile)));
|
std::make_unique<FileNode>(std::move(mergedFile)));
|
||||||
}
|
}
|
||||||
resolveTask.end();
|
resolveTask.end();
|
||||||
|
|
||||||
|
@ -210,7 +210,7 @@ class ArchiveReader : public Reader {
|
|||||||
const Registry ®) const override {
|
const Registry ®) const override {
|
||||||
StringRef path = mb->getBufferIdentifier();
|
StringRef path = mb->getBufferIdentifier();
|
||||||
std::unique_ptr<File> ret =
|
std::unique_ptr<File> ret =
|
||||||
llvm::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading);
|
std::make_unique<FileArchive>(std::move(mb), reg, path, _logLoading);
|
||||||
return std::move(ret);
|
return std::move(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +181,20 @@ class ArchHandler_x86_64 : public ArchHandler {
|
|||||||
FindAddressForAtom addressForAtom,
|
FindAddressForAtom addressForAtom,
|
||||||
normalized::Relocations &relocs) override;
|
normalized::Relocations &relocs) override;
|
||||||
|
|
||||||
|
bool isDataInCodeTransition(Reference::KindValue refKind) override {
|
||||||
|
return refKind == modeCode || refKind == modeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reference::KindValue dataInCodeTransitionStart(
|
||||||
|
const MachODefinedAtom &atom) override {
|
||||||
|
return modeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reference::KindValue dataInCodeTransitionEnd(
|
||||||
|
const MachODefinedAtom &atom) override {
|
||||||
|
return modeCode;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const Registry::KindStrings _sKindStrings[];
|
static const Registry::KindStrings _sKindStrings[];
|
||||||
static const StubInfo _sStubInfo;
|
static const StubInfo _sStubInfo;
|
||||||
@ -188,6 +202,9 @@ class ArchHandler_x86_64 : public ArchHandler {
|
|||||||
enum X86_64Kind: Reference::KindValue {
|
enum X86_64Kind: Reference::KindValue {
|
||||||
invalid, /// for error condition
|
invalid, /// for error condition
|
||||||
|
|
||||||
|
modeCode, /// Content starting at this offset is code.
|
||||||
|
modeData, /// Content starting at this offset is data.
|
||||||
|
|
||||||
// Kinds found in mach-o .o files:
|
// Kinds found in mach-o .o files:
|
||||||
branch32, /// ex: call _foo
|
branch32, /// ex: call _foo
|
||||||
ripRel32, /// ex: movq _foo(%rip), %rax
|
ripRel32, /// ex: movq _foo(%rip), %rax
|
||||||
@ -242,24 +259,34 @@ class ArchHandler_x86_64 : public ArchHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
|
const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = {
|
||||||
LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch32),
|
LLD_KIND_STRING_ENTRY(invalid),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32), LLD_KIND_STRING_ENTRY(ripRel32Minus1),
|
LLD_KIND_STRING_ENTRY(modeCode),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32Minus2), LLD_KIND_STRING_ENTRY(ripRel32Minus4),
|
LLD_KIND_STRING_ENTRY(modeData),
|
||||||
|
LLD_KIND_STRING_ENTRY(branch32),
|
||||||
|
LLD_KIND_STRING_ENTRY(ripRel32),
|
||||||
|
LLD_KIND_STRING_ENTRY(ripRel32Minus1),
|
||||||
|
LLD_KIND_STRING_ENTRY(ripRel32Minus2),
|
||||||
|
LLD_KIND_STRING_ENTRY(ripRel32Minus4),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32Anon),
|
LLD_KIND_STRING_ENTRY(ripRel32Anon),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon),
|
LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon),
|
LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon),
|
LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32GotLoad),
|
LLD_KIND_STRING_ENTRY(ripRel32GotLoad),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea),
|
LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea),
|
||||||
LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(ripRel32Tlv),
|
LLD_KIND_STRING_ENTRY(ripRel32Got),
|
||||||
|
LLD_KIND_STRING_ENTRY(ripRel32Tlv),
|
||||||
LLD_KIND_STRING_ENTRY(lazyPointer),
|
LLD_KIND_STRING_ENTRY(lazyPointer),
|
||||||
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
|
LLD_KIND_STRING_ENTRY(lazyImmediateLocation),
|
||||||
LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon),
|
LLD_KIND_STRING_ENTRY(pointer64),
|
||||||
LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64),
|
LLD_KIND_STRING_ENTRY(pointer64Anon),
|
||||||
LLD_KIND_STRING_ENTRY(delta32Anon), LLD_KIND_STRING_ENTRY(delta64Anon),
|
LLD_KIND_STRING_ENTRY(delta32),
|
||||||
|
LLD_KIND_STRING_ENTRY(delta64),
|
||||||
|
LLD_KIND_STRING_ENTRY(delta32Anon),
|
||||||
|
LLD_KIND_STRING_ENTRY(delta64Anon),
|
||||||
LLD_KIND_STRING_ENTRY(negDelta64),
|
LLD_KIND_STRING_ENTRY(negDelta64),
|
||||||
LLD_KIND_STRING_ENTRY(negDelta32),
|
LLD_KIND_STRING_ENTRY(negDelta32),
|
||||||
LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot),
|
LLD_KIND_STRING_ENTRY(imageOffset),
|
||||||
|
LLD_KIND_STRING_ENTRY(imageOffsetGot),
|
||||||
LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
|
LLD_KIND_STRING_ENTRY(unwindFDEToFunction),
|
||||||
LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
|
LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame),
|
||||||
LLD_KIND_STRING_ENTRY(tlvInitSectionOffset),
|
LLD_KIND_STRING_ENTRY(tlvInitSectionOffset),
|
||||||
@ -601,6 +628,8 @@ void ArchHandler_x86_64::applyFixupFinal(
|
|||||||
case negDelta32:
|
case negDelta32:
|
||||||
*loc32 = fixupAddress - targetAddress + ref.addend();
|
*loc32 = fixupAddress - targetAddress + ref.addend();
|
||||||
return;
|
return;
|
||||||
|
case modeCode:
|
||||||
|
case modeData:
|
||||||
case lazyPointer:
|
case lazyPointer:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return;
|
return;
|
||||||
@ -720,6 +749,8 @@ void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,
|
|||||||
case unwindInfoToEhFrame:
|
case unwindInfoToEhFrame:
|
||||||
llvm_unreachable("fixup implies __unwind_info");
|
llvm_unreachable("fixup implies __unwind_info");
|
||||||
return;
|
return;
|
||||||
|
case modeCode:
|
||||||
|
case modeData:
|
||||||
case unwindFDEToFunction:
|
case unwindFDEToFunction:
|
||||||
// Do nothing for now
|
// Do nothing for now
|
||||||
return;
|
return;
|
||||||
@ -743,6 +774,9 @@ void ArchHandler_x86_64::appendSectionRelocations(
|
|||||||
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
assert(ref.kindArch() == Reference::KindArch::x86_64);
|
||||||
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
|
uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom();
|
||||||
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
switch (static_cast<X86_64Kind>(ref.kindValue())) {
|
||||||
|
case modeCode:
|
||||||
|
case modeData:
|
||||||
|
return;
|
||||||
case branch32:
|
case branch32:
|
||||||
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0,
|
||||||
X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4);
|
X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4);
|
||||||
|
@ -573,7 +573,7 @@ class CompactUnwindPass : public Pass {
|
|||||||
|
|
||||||
void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
void addCompactUnwindPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||||
assert(ctx.needsCompactUnwindPass());
|
assert(ctx.needsCompactUnwindPass());
|
||||||
pm.add(llvm::make_unique<CompactUnwindPass>(ctx));
|
pm.add(std::make_unique<CompactUnwindPass>(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namesapce mach_o
|
} // end namesapce mach_o
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "lld/Core/Atom.h"
|
#include "lld/Core/Atom.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "llvm/Support/Allocator.h"
|
||||||
#include "llvm/Support/Format.h"
|
#include "llvm/Support/Format.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ class GOTPass : public Pass {
|
|||||||
|
|
||||||
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
void addGOTPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||||
assert(ctx.needsGOTPass());
|
assert(ctx.needsGOTPass());
|
||||||
pm.add(llvm::make_unique<GOTPass>(ctx));
|
pm.add(std::make_unique<GOTPass>(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namesapce mach_o
|
} // end namesapce mach_o
|
||||||
|
@ -478,7 +478,7 @@ llvm::Error LayoutPass::perform(SimpleFile &mergedFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
void addLayoutPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||||
pm.add(llvm::make_unique<LayoutPass>(
|
pm.add(std::make_unique<LayoutPass>(
|
||||||
ctx.registry(), [&](const DefinedAtom * left, const DefinedAtom * right,
|
ctx.registry(), [&](const DefinedAtom * left, const DefinedAtom * right,
|
||||||
bool & leftBeforeRight) ->bool {
|
bool & leftBeforeRight) ->bool {
|
||||||
return ctx.customAtomOrderer(left, right, leftBeforeRight);
|
return ctx.customAtomOrderer(left, right, leftBeforeRight);
|
||||||
|
@ -802,9 +802,9 @@ void MachOLinkingContext::addSectCreateSection(
|
|||||||
std::unique_ptr<MemoryBuffer> content) {
|
std::unique_ptr<MemoryBuffer> content) {
|
||||||
|
|
||||||
if (!_sectCreateFile) {
|
if (!_sectCreateFile) {
|
||||||
auto sectCreateFile = llvm::make_unique<mach_o::SectCreateFile>();
|
auto sectCreateFile = std::make_unique<mach_o::SectCreateFile>();
|
||||||
_sectCreateFile = sectCreateFile.get();
|
_sectCreateFile = sectCreateFile.get();
|
||||||
getNodes().push_back(llvm::make_unique<FileNode>(std::move(sectCreateFile)));
|
getNodes().push_back(std::make_unique<FileNode>(std::move(sectCreateFile)));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(_sectCreateFile && "sectcreate file does not exist.");
|
assert(_sectCreateFile && "sectcreate file does not exist.");
|
||||||
@ -897,8 +897,8 @@ static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
|
|||||||
|
|
||||||
std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
|
std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
_dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(new
|
_dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(
|
||||||
llvm::raw_fd_ostream(path, ec, llvm::sys::fs::F_None));
|
new llvm::raw_fd_ostream(path, ec, llvm::sys::fs::OF_None));
|
||||||
if (ec) {
|
if (ec) {
|
||||||
_dependencyInfo.reset();
|
_dependencyInfo.reset();
|
||||||
return ec;
|
return ec;
|
||||||
@ -1019,7 +1019,7 @@ void MachOLinkingContext::finalizeInputFiles() {
|
|||||||
return !isLibrary(a) && isLibrary(b);
|
return !isLibrary(a) && isLibrary(b);
|
||||||
});
|
});
|
||||||
size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary);
|
size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary);
|
||||||
elements.push_back(llvm::make_unique<GroupEnd>(numLibs));
|
elements.push_back(std::make_unique<GroupEnd>(numLibs));
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Error MachOLinkingContext::handleLoadedFile(File &file) {
|
llvm::Error MachOLinkingContext::handleLoadedFile(File &file) {
|
||||||
|
@ -542,7 +542,7 @@ class MachOObjectReader : public Reader {
|
|||||||
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
||||||
const Registry ®istry) const override {
|
const Registry ®istry) const override {
|
||||||
std::unique_ptr<File> ret =
|
std::unique_ptr<File> ret =
|
||||||
llvm::make_unique<MachOFile>(std::move(mb), &_ctx);
|
std::make_unique<MachOFile>(std::move(mb), &_ctx);
|
||||||
return std::move(ret);
|
return std::move(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +568,7 @@ class MachODylibReader : public Reader {
|
|||||||
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
loadFile(std::unique_ptr<MemoryBuffer> mb,
|
||||||
const Registry ®istry) const override {
|
const Registry ®istry) const override {
|
||||||
std::unique_ptr<File> ret =
|
std::unique_ptr<File> ret =
|
||||||
llvm::make_unique<MachODylibFile>(std::move(mb), &_ctx);
|
std::make_unique<MachODylibFile>(std::move(mb), &_ctx);
|
||||||
return std::move(ret);
|
return std::move(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,17 +626,19 @@ llvm::Error MachOFileLayout::writeSingleSegmentLoadCommand(uint8_t *&lc) {
|
|||||||
+ _file.sections.size() * sizeof(typename T::section);
|
+ _file.sections.size() * sizeof(typename T::section);
|
||||||
uint8_t *next = lc + seg->cmdsize;
|
uint8_t *next = lc + seg->cmdsize;
|
||||||
memset(seg->segname, 0, 16);
|
memset(seg->segname, 0, 16);
|
||||||
|
seg->flags = 0;
|
||||||
seg->vmaddr = 0;
|
seg->vmaddr = 0;
|
||||||
seg->vmsize = _file.sections.back().address
|
|
||||||
+ _file.sections.back().content.size();
|
|
||||||
seg->fileoff = _endOfLoadCommands;
|
seg->fileoff = _endOfLoadCommands;
|
||||||
seg->filesize = _sectInfo[&_file.sections.back()].fileOffset +
|
|
||||||
_file.sections.back().content.size() -
|
|
||||||
_sectInfo[&_file.sections.front()].fileOffset;
|
|
||||||
seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
seg->maxprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
||||||
seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
seg->initprot = VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE;
|
||||||
seg->nsects = _file.sections.size();
|
seg->nsects = _file.sections.size();
|
||||||
seg->flags = 0;
|
if (seg->nsects) {
|
||||||
|
seg->vmsize = _file.sections.back().address
|
||||||
|
+ _file.sections.back().content.size();
|
||||||
|
seg->filesize = _sectInfo[&_file.sections.back()].fileOffset +
|
||||||
|
_file.sections.back().content.size() -
|
||||||
|
_sectInfo[&_file.sections.front()].fileOffset;
|
||||||
|
}
|
||||||
if (_swap)
|
if (_swap)
|
||||||
swapStruct(*seg);
|
swapStruct(*seg);
|
||||||
typename T::section *sout = reinterpret_cast<typename T::section*>
|
typename T::section *sout = reinterpret_cast<typename T::section*>
|
||||||
|
@ -717,7 +717,7 @@ llvm::Error parseStabs(MachOFile &file,
|
|||||||
// FIXME: Kill this off when we can move to sane yaml parsing.
|
// FIXME: Kill this off when we can move to sane yaml parsing.
|
||||||
std::unique_ptr<BumpPtrAllocator> allocator;
|
std::unique_ptr<BumpPtrAllocator> allocator;
|
||||||
if (copyRefs)
|
if (copyRefs)
|
||||||
allocator = llvm::make_unique<BumpPtrAllocator>();
|
allocator = std::make_unique<BumpPtrAllocator>();
|
||||||
|
|
||||||
enum { start, inBeginEnd } state = start;
|
enum { start, inBeginEnd } state = start;
|
||||||
|
|
||||||
@ -812,7 +812,7 @@ llvm::Error parseStabs(MachOFile &file,
|
|||||||
stabsList.push_back(stab);
|
stabsList.push_back(stab);
|
||||||
}
|
}
|
||||||
|
|
||||||
file.setDebugInfo(llvm::make_unique<StabsDebugInfo>(std::move(stabsList)));
|
file.setDebugInfo(std::make_unique<StabsDebugInfo>(std::move(stabsList)));
|
||||||
|
|
||||||
// FIXME: Kill this off when we fix YAML memory ownership.
|
// FIXME: Kill this off when we fix YAML memory ownership.
|
||||||
file.debugInfo()->setAllocator(std::move(allocator));
|
file.debugInfo()->setAllocator(std::move(allocator));
|
||||||
@ -832,10 +832,10 @@ dataExtractorFromSection(const NormalizedFile &normalizedFile,
|
|||||||
|
|
||||||
// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
|
// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
|
||||||
// inspection" code if possible.
|
// inspection" code if possible.
|
||||||
static uint32_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
|
static uint64_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
|
||||||
uint64_t abbrCode) {
|
uint64_t abbrCode) {
|
||||||
uint64_t curCode;
|
uint64_t curCode;
|
||||||
uint32_t offset = 0;
|
uint64_t offset = 0;
|
||||||
while ((curCode = abbrevData.getULEB128(&offset)) != abbrCode) {
|
while ((curCode = abbrevData.getULEB128(&offset)) != abbrCode) {
|
||||||
// Tag
|
// Tag
|
||||||
abbrevData.getULEB128(&offset);
|
abbrevData.getULEB128(&offset);
|
||||||
@ -853,13 +853,13 @@ static uint32_t getCUAbbrevOffset(llvm::DataExtractor abbrevData,
|
|||||||
static Expected<const char *>
|
static Expected<const char *>
|
||||||
getIndexedString(const NormalizedFile &normalizedFile,
|
getIndexedString(const NormalizedFile &normalizedFile,
|
||||||
llvm::dwarf::Form form, llvm::DataExtractor infoData,
|
llvm::dwarf::Form form, llvm::DataExtractor infoData,
|
||||||
uint32_t &infoOffset, const Section &stringsSection) {
|
uint64_t &infoOffset, const Section &stringsSection) {
|
||||||
if (form == llvm::dwarf::DW_FORM_string)
|
if (form == llvm::dwarf::DW_FORM_string)
|
||||||
return infoData.getCStr(&infoOffset);
|
return infoData.getCStr(&infoOffset);
|
||||||
if (form != llvm::dwarf::DW_FORM_strp)
|
if (form != llvm::dwarf::DW_FORM_strp)
|
||||||
return llvm::make_error<GenericError>(
|
return llvm::make_error<GenericError>(
|
||||||
"string field encoded without DW_FORM_strp");
|
"string field encoded without DW_FORM_strp");
|
||||||
uint32_t stringOffset = infoData.getU32(&infoOffset);
|
uint64_t stringOffset = infoData.getU32(&infoOffset);
|
||||||
llvm::DataExtractor stringsData =
|
llvm::DataExtractor stringsData =
|
||||||
dataExtractorFromSection(normalizedFile, stringsSection);
|
dataExtractorFromSection(normalizedFile, stringsSection);
|
||||||
return stringsData.getCStr(&stringOffset);
|
return stringsData.getCStr(&stringOffset);
|
||||||
@ -875,7 +875,7 @@ readCompUnit(const NormalizedFile &normalizedFile,
|
|||||||
StringRef path) {
|
StringRef path) {
|
||||||
// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
|
// FIXME: Cribbed from llvm-dwp -- should share "lightweight CU DIE
|
||||||
// inspection" code if possible.
|
// inspection" code if possible.
|
||||||
uint32_t offset = 0;
|
uint64_t offset = 0;
|
||||||
llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32;
|
llvm::dwarf::DwarfFormat Format = llvm::dwarf::DwarfFormat::DWARF32;
|
||||||
auto infoData = dataExtractorFromSection(normalizedFile, info);
|
auto infoData = dataExtractorFromSection(normalizedFile, info);
|
||||||
uint32_t length = infoData.getU32(&offset);
|
uint32_t length = infoData.getU32(&offset);
|
||||||
@ -897,7 +897,7 @@ readCompUnit(const NormalizedFile &normalizedFile,
|
|||||||
|
|
||||||
uint32_t abbrCode = infoData.getULEB128(&offset);
|
uint32_t abbrCode = infoData.getULEB128(&offset);
|
||||||
auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev);
|
auto abbrevData = dataExtractorFromSection(normalizedFile, abbrev);
|
||||||
uint32_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode);
|
uint64_t abbrevOffset = getCUAbbrevOffset(abbrevData, abbrCode);
|
||||||
uint64_t tag = abbrevData.getULEB128(&abbrevOffset);
|
uint64_t tag = abbrevData.getULEB128(&abbrevOffset);
|
||||||
if (tag != llvm::dwarf::DW_TAG_compile_unit)
|
if (tag != llvm::dwarf::DW_TAG_compile_unit)
|
||||||
return llvm::make_error<GenericError>("top level DIE is not a compile unit");
|
return llvm::make_error<GenericError>("top level DIE is not a compile unit");
|
||||||
@ -974,11 +974,11 @@ llvm::Error parseDebugInfo(MachOFile &file,
|
|||||||
// memory ownership.
|
// memory ownership.
|
||||||
std::unique_ptr<BumpPtrAllocator> allocator;
|
std::unique_ptr<BumpPtrAllocator> allocator;
|
||||||
if (copyRefs) {
|
if (copyRefs) {
|
||||||
allocator = llvm::make_unique<BumpPtrAllocator>();
|
allocator = std::make_unique<BumpPtrAllocator>();
|
||||||
tuOrErr->name = copyDebugString(tuOrErr->name, *allocator);
|
tuOrErr->name = copyDebugString(tuOrErr->name, *allocator);
|
||||||
tuOrErr->path = copyDebugString(tuOrErr->path, *allocator);
|
tuOrErr->path = copyDebugString(tuOrErr->path, *allocator);
|
||||||
}
|
}
|
||||||
file.setDebugInfo(llvm::make_unique<DwarfDebugInfo>(std::move(*tuOrErr)));
|
file.setDebugInfo(std::make_unique<DwarfDebugInfo>(std::move(*tuOrErr)));
|
||||||
if (copyRefs)
|
if (copyRefs)
|
||||||
file.debugInfo()->setAllocator(std::move(allocator));
|
file.debugInfo()->setAllocator(std::move(allocator));
|
||||||
} else
|
} else
|
||||||
|
@ -124,7 +124,7 @@ class ObjCPass : public Pass {
|
|||||||
|
|
||||||
|
|
||||||
void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
void addObjCPass(PassManager &pm, const MachOLinkingContext &ctx) {
|
||||||
pm.add(llvm::make_unique<ObjCPass>(ctx));
|
pm.add(std::make_unique<ObjCPass>(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace mach_o
|
} // end namespace mach_o
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user