mirror of
https://github.com/opnsense/src.git
synced 2026-06-04 22:32:43 -04:00
Vendor import of lld trunk r300422:
https://llvm.org/svn/llvm-project/lld/trunk@300422
This commit is contained in:
parent
16787c9ce0
commit
d2d3ebb819
369 changed files with 12798 additions and 7941 deletions
|
|
@ -11,8 +11,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
|||
message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND "${LLVM_CONFIG_PATH}" "--obj-root" "--includedir"
|
||||
execute_process(COMMAND "${LLVM_CONFIG_PATH}"
|
||||
"--obj-root"
|
||||
"--includedir"
|
||||
"--cmakedir"
|
||||
"--src-root"
|
||||
RESULT_VARIABLE HAD_ERROR
|
||||
OUTPUT_VARIABLE LLVM_CONFIG_OUTPUT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
|
@ -25,9 +28,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
|||
list(GET LLVM_CONFIG_OUTPUT 0 OBJ_ROOT)
|
||||
list(GET LLVM_CONFIG_OUTPUT 1 MAIN_INCLUDE_DIR)
|
||||
list(GET LLVM_CONFIG_OUTPUT 2 LLVM_CMAKE_PATH)
|
||||
list(GET LLVM_CONFIG_OUTPUT 3 MAIN_SRC_DIR)
|
||||
|
||||
set(LLVM_OBJ_ROOT ${OBJ_ROOT} CACHE PATH "path to LLVM build tree")
|
||||
set(LLVM_MAIN_INCLUDE_DIR ${MAIN_INCLUDE_DIR} CACHE PATH "path to llvm/include")
|
||||
set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree")
|
||||
|
||||
file(TO_CMAKE_PATH ${LLVM_OBJ_ROOT} LLVM_BINARY_DIR)
|
||||
|
||||
|
|
@ -49,6 +54,67 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
|||
include(AddLLVM)
|
||||
include(TableGen)
|
||||
include(HandleLLVMOptions)
|
||||
|
||||
if(LLVM_INCLUDE_TESTS)
|
||||
set(Python_ADDITIONAL_VERSIONS 2.7)
|
||||
include(FindPythonInterp)
|
||||
if(NOT PYTHONINTERP_FOUND)
|
||||
message(FATAL_ERROR
|
||||
"Unable to find Python interpreter, required for testing.
|
||||
|
||||
Please install Python or specify the PYTHON_EXECUTABLE CMake variable.")
|
||||
endif()
|
||||
|
||||
if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7)
|
||||
message(FATAL_ERROR "Python 2.7 or newer is required")
|
||||
endif()
|
||||
|
||||
# Check prebuilt llvm/utils.
|
||||
if(EXISTS ${LLVM_TOOLS_BINARY_DIR}/FileCheck${CMAKE_EXECUTABLE_SUFFIX}
|
||||
AND EXISTS ${LLVM_TOOLS_BINARY_DIR}/not${CMAKE_EXECUTABLE_SUFFIX})
|
||||
set(LLVM_UTILS_PROVIDED ON)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
# Note: path not really used, except for checking if lit was found
|
||||
set(LLVM_LIT ${LLVM_MAIN_SRC_DIR}/utils/lit/lit.py)
|
||||
if(NOT LLVM_UTILS_PROVIDED)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/FileCheck utils/FileCheck)
|
||||
add_subdirectory(${LLVM_MAIN_SRC_DIR}/utils/not utils/not)
|
||||
set(LLVM_UTILS_PROVIDED ON)
|
||||
set(LLD_TEST_DEPS FileCheck not)
|
||||
endif()
|
||||
set(UNITTEST_DIR ${LLVM_MAIN_SRC_DIR}/utils/unittest)
|
||||
if(EXISTS ${UNITTEST_DIR}/googletest/include/gtest/gtest.h
|
||||
AND NOT EXISTS ${LLVM_LIBRARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}gtest${CMAKE_STATIC_LIBRARY_SUFFIX}
|
||||
AND EXISTS ${UNITTEST_DIR}/CMakeLists.txt)
|
||||
add_subdirectory(${UNITTEST_DIR} utils/unittest)
|
||||
endif()
|
||||
else()
|
||||
# Seek installed Lit.
|
||||
find_program(LLVM_LIT
|
||||
NAMES llvm-lit lit.py lit
|
||||
PATHS "${LLVM_MAIN_SRC_DIR}/utils/lit"
|
||||
DOC "Path to lit.py")
|
||||
endif()
|
||||
|
||||
if(LLVM_LIT)
|
||||
# Define the default arguments to use with 'lit', and an option for the user
|
||||
# to override.
|
||||
set(LIT_ARGS_DEFAULT "-sv")
|
||||
if (MSVC OR XCODE)
|
||||
set(LIT_ARGS_DEFAULT "${LIT_ARGS_DEFAULT} --no-progress-bar")
|
||||
endif()
|
||||
set(LLVM_LIT_ARGS "${LIT_ARGS_DEFAULT}" CACHE STRING "Default options for lit")
|
||||
|
||||
# On Win32 hosts, provide an option to specify the path to the GnuWin32 tools.
|
||||
if(WIN32 AND NOT CYGWIN)
|
||||
set(LLVM_LIT_TOOLS_DIR "" CACHE PATH "Path to GnuWin32 tools")
|
||||
endif()
|
||||
else()
|
||||
set(LLVM_INCLUDE_TESTS OFF)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(LLD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ add_lld_library(lldCOFF
|
|||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
Librarian.cpp
|
||||
LTO.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
ModuleDef.cpp
|
||||
PDB.cpp
|
||||
|
|
@ -25,6 +27,7 @@ add_lld_library(lldCOFF
|
|||
|
||||
LINK_COMPONENTS
|
||||
${LLVM_TARGETS_TO_BUILD}
|
||||
BitReader
|
||||
Core
|
||||
DebugInfoCodeView
|
||||
DebugInfoMSF
|
||||
|
|
@ -40,7 +43,7 @@ add_lld_library(lldCOFF
|
|||
|
||||
LINK_LIBS
|
||||
lldCore
|
||||
${PTHREAD_LIB}
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
COFFOptionsTableGen
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/COFF.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
|
|
@ -61,7 +62,7 @@ void SectionChunk::applyRelX64(uint8_t *Off, uint16_t Type, Defined *Sym,
|
|||
case IMAGE_REL_AMD64_SECTION: add16(Off, Sym->getSectionIndex()); break;
|
||||
case IMAGE_REL_AMD64_SECREL: add32(Off, Sym->getSecrel()); break;
|
||||
default:
|
||||
fatal("unsupported relocation type");
|
||||
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +77,7 @@ void SectionChunk::applyRelX86(uint8_t *Off, uint16_t Type, Defined *Sym,
|
|||
case IMAGE_REL_I386_SECTION: add16(Off, Sym->getSectionIndex()); break;
|
||||
case IMAGE_REL_I386_SECREL: add32(Off, Sym->getSecrel()); break;
|
||||
default:
|
||||
fatal("unsupported relocation type");
|
||||
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +137,7 @@ void SectionChunk::applyRelARM(uint8_t *Off, uint16_t Type, Defined *Sym,
|
|||
case IMAGE_REL_ARM_BLX23T: applyBranch24T(Off, S - P - 4); break;
|
||||
case IMAGE_REL_ARM_SECREL: add32(Off, Sym->getSecrel()); break;
|
||||
default:
|
||||
fatal("unsupported relocation type");
|
||||
fatal("unsupported relocation type 0x" + Twine::utohexstr(Type));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +227,7 @@ void SectionChunk::printDiscardedMessage() const {
|
|||
// Removed by dead-stripping. If it's removed by ICF, ICF already
|
||||
// printed out the name, so don't repeat that here.
|
||||
if (Sym && this == Repl)
|
||||
outs() << "Discarded " << Sym->getName() << "\n";
|
||||
message("Discarded " + Sym->getName());
|
||||
}
|
||||
|
||||
StringRef SectionChunk::getDebugName() {
|
||||
|
|
|
|||
|
|
@ -187,10 +187,10 @@ public:
|
|||
|
||||
const coff_section *Header;
|
||||
|
||||
private:
|
||||
// A file this chunk was created from.
|
||||
// The file that this chunk was created from.
|
||||
ObjectFile *File;
|
||||
|
||||
private:
|
||||
StringRef SectionName;
|
||||
std::vector<SectionChunk *> AssocChildren;
|
||||
llvm::iterator_range<const coff_relocation *> Relocs;
|
||||
|
|
|
|||
|
|
@ -80,14 +80,16 @@ struct Configuration {
|
|||
SymbolBody *Entry = nullptr;
|
||||
bool NoEntry = false;
|
||||
std::string OutputFile;
|
||||
bool ColorDiagnostics;
|
||||
bool DoGC = true;
|
||||
bool DoICF = true;
|
||||
uint64_t ErrorLimit = 20;
|
||||
bool Relocatable = true;
|
||||
bool Force = false;
|
||||
bool Debug = false;
|
||||
bool WriteSymtab = true;
|
||||
unsigned DebugTypes = static_cast<unsigned>(DebugType::None);
|
||||
StringRef PDBPath;
|
||||
llvm::SmallString<128> PDBPath;
|
||||
|
||||
// Symbols in this set are considered as live by the garbage collector.
|
||||
std::set<SymbolBody *> GCRoot;
|
||||
|
|
@ -103,6 +105,8 @@ struct Configuration {
|
|||
std::map<std::string, int> DLLOrder;
|
||||
SymbolBody *DelayLoadHelper = nullptr;
|
||||
|
||||
bool SaveTemps = false;
|
||||
|
||||
// Used for SafeSEH.
|
||||
Symbol *SEHTable = nullptr;
|
||||
Symbol *SEHCount = nullptr;
|
||||
|
|
@ -111,7 +115,9 @@ struct Configuration {
|
|||
unsigned LTOOptLevel = 2;
|
||||
|
||||
// Used for /opt:lldltojobs=N
|
||||
unsigned LTOJobs = 1;
|
||||
unsigned LTOJobs = 0;
|
||||
// Used for /opt:lldltopartitions=N
|
||||
unsigned LTOPartitions = 1;
|
||||
|
||||
// Used for /merge:from=to (e.g. /merge:.rdata=.text)
|
||||
std::map<StringRef, StringRef> Merge;
|
||||
|
|
@ -135,6 +141,9 @@ struct Configuration {
|
|||
// Used for /alternatename.
|
||||
std::map<StringRef, StringRef> AlternateNames;
|
||||
|
||||
// Used for /lldmap.
|
||||
std::string MapFile;
|
||||
|
||||
uint64_t ImageBase = -1;
|
||||
uint64_t StackReserve = 1024 * 1024;
|
||||
uint64_t StackCommit = 4096;
|
||||
|
|
@ -151,6 +160,7 @@ struct Configuration {
|
|||
bool TerminalServerAware = true;
|
||||
bool LargeAddressAware = false;
|
||||
bool HighEntropyVA = false;
|
||||
bool AppContainer = false;
|
||||
|
||||
// This is for debugging.
|
||||
bool DebugPdb = false;
|
||||
|
|
|
|||
287
COFF/Driver.cpp
287
COFF/Driver.cpp
|
|
@ -19,6 +19,7 @@
|
|||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/LibDriver/LibDriver.h"
|
||||
#include "llvm/Object/ArchiveWriter.h"
|
||||
#include "llvm/Option/Arg.h"
|
||||
#include "llvm/Option/ArgList.h"
|
||||
#include "llvm/Option/Option.h"
|
||||
|
|
@ -31,17 +32,11 @@
|
|||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// <future> depends on <eh.h> for __uncaught_exception.
|
||||
#include <eh.h>
|
||||
#endif
|
||||
|
||||
#include <future>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::COFF;
|
||||
using llvm::sys::Process;
|
||||
using llvm::sys::fs::OpenFlags;
|
||||
using llvm::sys::fs::file_magic;
|
||||
using llvm::sys::fs::identify_magic;
|
||||
|
||||
|
|
@ -55,11 +50,16 @@ BumpPtrAllocator BAlloc;
|
|||
StringSaver Saver{BAlloc};
|
||||
std::vector<SpecificAllocBase *> SpecificAllocBase::Instances;
|
||||
|
||||
bool link(ArrayRef<const char *> Args) {
|
||||
bool link(ArrayRef<const char *> Args, raw_ostream &Diag) {
|
||||
ErrorCount = 0;
|
||||
ErrorOS = &Diag;
|
||||
Argv0 = Args[0];
|
||||
Config = make<Configuration>();
|
||||
Config->ColorDiagnostics =
|
||||
(ErrorOS == &llvm::errs() && Process::StandardErrHasColors());
|
||||
Driver = make<LinkerDriver>();
|
||||
Driver->link(Args);
|
||||
return true;
|
||||
return !ErrorCount;
|
||||
}
|
||||
|
||||
// Drop directory components and replace extension with ".exe" or ".dll".
|
||||
|
|
@ -121,10 +121,12 @@ void LinkerDriver::addBuffer(std::unique_ptr<MemoryBuffer> MB) {
|
|||
return Symtab.addFile(make<ArchiveFile>(MBRef));
|
||||
if (Magic == file_magic::bitcode)
|
||||
return Symtab.addFile(make<BitcodeFile>(MBRef));
|
||||
|
||||
if (Magic == file_magic::coff_cl_gl_object)
|
||||
fatal(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
|
||||
error(MBRef.getBufferIdentifier() + ": is not a native COFF file. "
|
||||
"Recompile without /GL");
|
||||
Symtab.addFile(make<ObjectFile>(MBRef));
|
||||
else
|
||||
Symtab.addFile(make<ObjectFile>(MBRef));
|
||||
}
|
||||
|
||||
void LinkerDriver::enqueuePath(StringRef Path) {
|
||||
|
|
@ -134,12 +136,10 @@ void LinkerDriver::enqueuePath(StringRef Path) {
|
|||
enqueueTask([=]() {
|
||||
auto MBOrErr = Future->get();
|
||||
if (MBOrErr.second)
|
||||
fatal(MBOrErr.second, "could not open " + PathStr);
|
||||
Driver->addBuffer(std::move(MBOrErr.first));
|
||||
error("could not open " + PathStr + ": " + MBOrErr.second.message());
|
||||
else
|
||||
Driver->addBuffer(std::move(MBOrErr.first));
|
||||
});
|
||||
|
||||
if (Config->OutputFile == "")
|
||||
Config->OutputFile = getOutputPath(Path);
|
||||
}
|
||||
|
||||
void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
|
||||
|
|
@ -151,17 +151,18 @@ void LinkerDriver::addArchiveBuffer(MemoryBufferRef MB, StringRef SymName,
|
|||
}
|
||||
|
||||
InputFile *Obj;
|
||||
if (Magic == file_magic::coff_object)
|
||||
if (Magic == file_magic::coff_object) {
|
||||
Obj = make<ObjectFile>(MB);
|
||||
else if (Magic == file_magic::bitcode)
|
||||
} else if (Magic == file_magic::bitcode) {
|
||||
Obj = make<BitcodeFile>(MB);
|
||||
else
|
||||
fatal("unknown file type: " + MB.getBufferIdentifier());
|
||||
} else {
|
||||
error("unknown file type: " + MB.getBufferIdentifier());
|
||||
return;
|
||||
}
|
||||
|
||||
Obj->ParentName = ParentName;
|
||||
Symtab.addFile(Obj);
|
||||
if (Config->Verbose)
|
||||
outs() << "Loaded " << toString(Obj) << " for " << SymName << "\n";
|
||||
log("Loaded " + toString(Obj) + " for " + SymName);
|
||||
}
|
||||
|
||||
void LinkerDriver::enqueueArchiveMember(const Archive::Child &C,
|
||||
|
|
@ -234,7 +235,7 @@ void LinkerDriver::parseDirectives(StringRef S) {
|
|||
case OPT_throwingnew:
|
||||
break;
|
||||
default:
|
||||
fatal(Arg->getSpelling() + " is not allowed in .drectve");
|
||||
error(Arg->getSpelling() + " is not allowed in .drectve");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -402,7 +403,8 @@ static unsigned parseDebugType(StringRef Arg) {
|
|||
DebugTypes |= StringSwitch<unsigned>(Type.lower())
|
||||
.Case("cv", static_cast<unsigned>(DebugType::CV))
|
||||
.Case("pdata", static_cast<unsigned>(DebugType::PData))
|
||||
.Case("fixup", static_cast<unsigned>(DebugType::Fixup));
|
||||
.Case("fixup", static_cast<unsigned>(DebugType::Fixup))
|
||||
.Default(0);
|
||||
return DebugTypes;
|
||||
}
|
||||
|
||||
|
|
@ -418,6 +420,132 @@ static std::string getMapFile(const opt::InputArgList &Args) {
|
|||
return (OutFile.substr(0, OutFile.rfind('.')) + ".map").str();
|
||||
}
|
||||
|
||||
std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
|
||||
std::vector<MemoryBufferRef> V;
|
||||
Error Err = Error::success();
|
||||
for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
|
||||
Archive::Child C =
|
||||
check(COrErr,
|
||||
File->getFileName() + ": could not get the child of the archive");
|
||||
MemoryBufferRef MBRef =
|
||||
check(C.getMemoryBufferRef(),
|
||||
File->getFileName() +
|
||||
": could not get the buffer for a child of the archive");
|
||||
V.push_back(MBRef);
|
||||
}
|
||||
if (Err)
|
||||
fatal(File->getFileName() +
|
||||
": Archive::children failed: " + toString(std::move(Err)));
|
||||
return V;
|
||||
}
|
||||
|
||||
// A helper function for filterBitcodeFiles.
|
||||
static bool needsRebuilding(MemoryBufferRef MB) {
|
||||
// The MSVC linker doesn't support thin archives, so if it's a thin
|
||||
// archive, we always need to rebuild it.
|
||||
std::unique_ptr<Archive> File =
|
||||
check(Archive::create(MB), "Failed to read " + MB.getBufferIdentifier());
|
||||
if (File->isThin())
|
||||
return true;
|
||||
|
||||
// Returns true if the archive contains at least one bitcode file.
|
||||
for (MemoryBufferRef Member : getArchiveMembers(File.get()))
|
||||
if (identify_magic(Member.getBuffer()) == file_magic::bitcode)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Opens a given path as an archive file and removes bitcode files
|
||||
// from them if exists. This function is to appease the MSVC linker as
|
||||
// their linker doesn't like archive files containing non-native
|
||||
// object files.
|
||||
//
|
||||
// If a given archive doesn't contain bitcode files, the archive path
|
||||
// is returned as-is. Otherwise, a new temporary file is created and
|
||||
// its path is returned.
|
||||
static Optional<std::string>
|
||||
filterBitcodeFiles(StringRef Path, std::vector<std::string> &TemporaryFiles) {
|
||||
std::unique_ptr<MemoryBuffer> MB = check(
|
||||
MemoryBuffer::getFile(Path, -1, false, true), "could not open " + Path);
|
||||
MemoryBufferRef MBRef = MB->getMemBufferRef();
|
||||
file_magic Magic = identify_magic(MBRef.getBuffer());
|
||||
|
||||
if (Magic == file_magic::bitcode)
|
||||
return None;
|
||||
if (Magic != file_magic::archive)
|
||||
return Path.str();
|
||||
if (!needsRebuilding(MBRef))
|
||||
return Path.str();
|
||||
|
||||
std::unique_ptr<Archive> File =
|
||||
check(Archive::create(MBRef),
|
||||
MBRef.getBufferIdentifier() + ": failed to parse archive");
|
||||
|
||||
std::vector<NewArchiveMember> New;
|
||||
for (MemoryBufferRef Member : getArchiveMembers(File.get()))
|
||||
if (identify_magic(Member.getBuffer()) != file_magic::bitcode)
|
||||
New.emplace_back(Member);
|
||||
|
||||
if (New.empty())
|
||||
return None;
|
||||
|
||||
log("Creating a temporary archive for " + Path + " to remove bitcode files");
|
||||
|
||||
SmallString<128> S;
|
||||
if (auto EC = sys::fs::createTemporaryFile("lld-" + sys::path::stem(Path),
|
||||
".lib", S))
|
||||
fatal(EC, "cannot create a temporary file");
|
||||
std::string Temp = S.str();
|
||||
TemporaryFiles.push_back(Temp);
|
||||
|
||||
std::pair<StringRef, std::error_code> Ret =
|
||||
llvm::writeArchive(Temp, New, /*WriteSymtab=*/true, Archive::Kind::K_GNU,
|
||||
/*Deterministics=*/true,
|
||||
/*Thin=*/false);
|
||||
if (Ret.second)
|
||||
error("failed to create a new archive " + S.str() + ": " + Ret.first);
|
||||
return Temp;
|
||||
}
|
||||
|
||||
// Create response file contents and invoke the MSVC linker.
|
||||
void LinkerDriver::invokeMSVC(opt::InputArgList &Args) {
|
||||
std::string Rsp = "/nologo ";
|
||||
std::vector<std::string> Temps;
|
||||
|
||||
for (auto *Arg : Args) {
|
||||
switch (Arg->getOption().getID()) {
|
||||
case OPT_linkrepro:
|
||||
case OPT_lldmap:
|
||||
case OPT_lldmap_file:
|
||||
case OPT_lldsavetemps:
|
||||
case OPT_msvclto:
|
||||
// LLD-specific options are stripped.
|
||||
break;
|
||||
case OPT_opt:
|
||||
if (!StringRef(Arg->getValue()).startswith("lld"))
|
||||
Rsp += toString(Arg) + " ";
|
||||
break;
|
||||
case OPT_INPUT: {
|
||||
if (Optional<StringRef> Path = doFindFile(Arg->getValue())) {
|
||||
if (Optional<std::string> S = filterBitcodeFiles(*Path, Temps))
|
||||
Rsp += quote(*S) + " ";
|
||||
continue;
|
||||
}
|
||||
Rsp += quote(Arg->getValue()) + " ";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Rsp += toString(Arg) + " ";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<StringRef> ObjectFiles = Symtab.compileBitcodeFiles();
|
||||
runMSVCLinker(Rsp, ObjectFiles);
|
||||
|
||||
for (StringRef Path : Temps)
|
||||
sys::fs::remove(Path);
|
||||
}
|
||||
|
||||
void LinkerDriver::enqueueTask(std::function<void()> Task) {
|
||||
TaskQueue.push_back(std::move(Task));
|
||||
}
|
||||
|
|
@ -451,6 +579,22 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
// Parse command line options.
|
||||
opt::InputArgList Args = Parser.parseLINK(ArgsArr.slice(1));
|
||||
|
||||
// Parse and evaluate -mllvm options.
|
||||
std::vector<const char *> V;
|
||||
V.push_back("lld-link (LLVM option parsing)");
|
||||
for (auto *Arg : Args.filtered(OPT_mllvm))
|
||||
V.push_back(Arg->getValue());
|
||||
cl::ParseCommandLineOptions(V.size(), V.data());
|
||||
|
||||
// Handle /errorlimit early, because error() depends on it.
|
||||
if (auto *Arg = Args.getLastArg(OPT_errorlimit)) {
|
||||
int N = 20;
|
||||
StringRef S = Arg->getValue();
|
||||
if (S.getAsInteger(10, N))
|
||||
error(Arg->getSpelling() + " number expected, but got " + S);
|
||||
Config->ErrorLimit = N;
|
||||
}
|
||||
|
||||
// Handle /help
|
||||
if (Args.hasArg(OPT_help)) {
|
||||
printHelp(ArgsArr[0]);
|
||||
|
|
@ -467,12 +611,12 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
if (ErrOrWriter) {
|
||||
Tar = std::move(*ErrOrWriter);
|
||||
} else {
|
||||
errs() << "/linkrepro: failed to open " << Path << ": "
|
||||
<< toString(ErrOrWriter.takeError()) << '\n';
|
||||
error("/linkrepro: failed to open " + Path + ": " +
|
||||
toString(ErrOrWriter.takeError()));
|
||||
}
|
||||
}
|
||||
|
||||
if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end())
|
||||
if (!Args.hasArgNoClaim(OPT_INPUT))
|
||||
fatal("no input files");
|
||||
|
||||
// Construct search path list.
|
||||
|
|
@ -508,9 +652,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
|
||||
// Handle /noentry
|
||||
if (Args.hasArg(OPT_noentry)) {
|
||||
if (!Args.hasArg(OPT_dll))
|
||||
fatal("/noentry must be specified with /dll");
|
||||
Config->NoEntry = true;
|
||||
if (Args.hasArg(OPT_dll))
|
||||
Config->NoEntry = true;
|
||||
else
|
||||
error("/noentry must be specified with /dll");
|
||||
}
|
||||
|
||||
// Handle /dll
|
||||
|
|
@ -521,12 +666,17 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
|
||||
// Handle /fixed
|
||||
if (Args.hasArg(OPT_fixed)) {
|
||||
if (Args.hasArg(OPT_dynamicbase))
|
||||
fatal("/fixed must not be specified with /dynamicbase");
|
||||
Config->Relocatable = false;
|
||||
Config->DynamicBase = false;
|
||||
if (Args.hasArg(OPT_dynamicbase)) {
|
||||
error("/fixed must not be specified with /dynamicbase");
|
||||
} else {
|
||||
Config->Relocatable = false;
|
||||
Config->DynamicBase = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Args.hasArg(OPT_appcontainer))
|
||||
Config->AppContainer = true;
|
||||
|
||||
// Handle /machine
|
||||
if (auto *Arg = Args.getLastArg(OPT_machine))
|
||||
Config->Machine = getMachineType(Arg->getValue());
|
||||
|
|
@ -596,20 +746,31 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
StringRef OptLevel = StringRef(S).substr(7);
|
||||
if (OptLevel.getAsInteger(10, Config->LTOOptLevel) ||
|
||||
Config->LTOOptLevel > 3)
|
||||
fatal("/opt:lldlto: invalid optimization level: " + OptLevel);
|
||||
error("/opt:lldlto: invalid optimization level: " + OptLevel);
|
||||
continue;
|
||||
}
|
||||
if (StringRef(S).startswith("lldltojobs=")) {
|
||||
StringRef Jobs = StringRef(S).substr(11);
|
||||
if (Jobs.getAsInteger(10, Config->LTOJobs) || Config->LTOJobs == 0)
|
||||
fatal("/opt:lldltojobs: invalid job count: " + Jobs);
|
||||
error("/opt:lldltojobs: invalid job count: " + Jobs);
|
||||
continue;
|
||||
}
|
||||
if (StringRef(S).startswith("lldltopartitions=")) {
|
||||
StringRef N = StringRef(S).substr(17);
|
||||
if (N.getAsInteger(10, Config->LTOPartitions) ||
|
||||
Config->LTOPartitions == 0)
|
||||
error("/opt:lldltopartitions: invalid partition count: " + N);
|
||||
continue;
|
||||
}
|
||||
if (S != "ref" && S != "lbr" && S != "nolbr")
|
||||
fatal("/opt: unknown option: " + S);
|
||||
error("/opt: unknown option: " + S);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle /lldsavetemps
|
||||
if (Args.hasArg(OPT_lldsavetemps))
|
||||
Config->SaveTemps = true;
|
||||
|
||||
// Handle /failifmismatch
|
||||
for (auto *Arg : Args.filtered(OPT_failifmismatch))
|
||||
checkFailIfMismatch(Arg->getValue());
|
||||
|
|
@ -658,6 +819,11 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
Config->DumpPdb = Args.hasArg(OPT_dumppdb);
|
||||
Config->DebugPdb = Args.hasArg(OPT_debugpdb);
|
||||
|
||||
Config->MapFile = getMapFile(Args);
|
||||
|
||||
if (ErrorCount)
|
||||
return;
|
||||
|
||||
// Create a list of input files. Files can be given as arguments
|
||||
// for /defaultlib option.
|
||||
std::vector<MemoryBufferRef> MBs;
|
||||
|
|
@ -678,7 +844,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
// We should have inferred a machine type by now from the input files, but if
|
||||
// not we assume x64.
|
||||
if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
|
||||
errs() << "warning: /machine is not specified. x64 is assumed.\n";
|
||||
warn("/machine is not specified. x64 is assumed");
|
||||
Config->Machine = AMD64;
|
||||
}
|
||||
|
||||
|
|
@ -715,8 +881,7 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
if (S.empty())
|
||||
fatal("entry point must be defined");
|
||||
Config->Entry = addUndefined(S);
|
||||
if (Config->Verbose)
|
||||
outs() << "Entry name inferred: " << S << "\n";
|
||||
log("Entry name inferred: " + S);
|
||||
}
|
||||
|
||||
// Handle /export
|
||||
|
|
@ -749,6 +914,22 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
}
|
||||
}
|
||||
|
||||
// Set default image name if neither /out or /def set it.
|
||||
if (Config->OutputFile.empty()) {
|
||||
Config->OutputFile =
|
||||
getOutputPath((*Args.filtered(OPT_INPUT).begin())->getValue());
|
||||
}
|
||||
|
||||
// Put the PDB next to the image if no /pdb flag was passed.
|
||||
if (Config->Debug && Config->PDBPath.empty()) {
|
||||
Config->PDBPath = Config->OutputFile;
|
||||
sys::path::replace_extension(Config->PDBPath, ".pdb");
|
||||
}
|
||||
|
||||
// Disable PDB generation if the user requested it.
|
||||
if (Args.hasArg(OPT_nopdb))
|
||||
Config->PDBPath = "";
|
||||
|
||||
// Set default image base if /base is not given.
|
||||
if (Config->ImageBase == uint64_t(-1))
|
||||
Config->ImageBase = getDefaultImageBase();
|
||||
|
|
@ -801,6 +982,16 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
addUndefined(mangle("_load_config_used"));
|
||||
} while (run());
|
||||
|
||||
if (ErrorCount)
|
||||
return;
|
||||
|
||||
// If /msvclto is given, we use the MSVC linker to link LTO output files.
|
||||
// This is useful because MSVC link.exe can generate complete PDBs.
|
||||
if (Args.hasArg(OPT_msvclto)) {
|
||||
invokeMSVC(Args);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Do LTO by compiling bitcode input files to a set of native COFF files then
|
||||
// link those files.
|
||||
Symtab.addCombinedLTOObjects();
|
||||
|
|
@ -818,10 +1009,13 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
}
|
||||
|
||||
// Handle /safeseh.
|
||||
if (Args.hasArg(OPT_safeseh))
|
||||
if (Args.hasArg(OPT_safeseh)) {
|
||||
for (ObjectFile *File : Symtab.ObjectFiles)
|
||||
if (!File->SEHCompat)
|
||||
fatal("/safeseh: " + File->getName() + " is not compatible with SEH");
|
||||
error("/safeseh: " + File->getName() + " is not compatible with SEH");
|
||||
if (ErrorCount)
|
||||
return;
|
||||
}
|
||||
|
||||
// Windows specific -- when we are creating a .dll file, we also
|
||||
// need to create a .lib file.
|
||||
|
|
@ -846,17 +1040,6 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
|
|||
// Write the result.
|
||||
writeResult(&Symtab);
|
||||
|
||||
// Create a symbol map file containing symbol VAs and their names
|
||||
// to help debugging.
|
||||
std::string MapFile = getMapFile(Args);
|
||||
if (!MapFile.empty()) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream Out(MapFile, EC, OpenFlags::F_Text);
|
||||
if (EC)
|
||||
fatal(EC, "could not create the symbol map " + MapFile);
|
||||
Symtab.printMap(Out);
|
||||
}
|
||||
|
||||
// Call exit to avoid calling destructors.
|
||||
exit(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@ private:
|
|||
StringRef findDefaultEntry();
|
||||
WindowsSubsystem inferSubsystem();
|
||||
|
||||
void invokeMSVC(llvm::opt::InputArgList &Args);
|
||||
|
||||
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> MB);
|
||||
void addBuffer(std::unique_ptr<MemoryBuffer> MB);
|
||||
void addArchiveBuffer(MemoryBufferRef MBRef, StringRef SymName,
|
||||
|
|
@ -178,6 +180,8 @@ void checkFailIfMismatch(StringRef Arg);
|
|||
std::unique_ptr<MemoryBuffer>
|
||||
convertResToCOFF(const std::vector<MemoryBufferRef> &MBs);
|
||||
|
||||
void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects);
|
||||
|
||||
// Create enum with OPT_xxx values for each option in Options.td
|
||||
enum {
|
||||
OPT_INVALID = 0,
|
||||
|
|
|
|||
|
|
@ -44,31 +44,33 @@ namespace {
|
|||
class Executor {
|
||||
public:
|
||||
explicit Executor(StringRef S) : Saver(Alloc), Prog(Saver.save(S)) {}
|
||||
void add(StringRef S) { Args.push_back(Saver.save(S).data()); }
|
||||
void add(std::string &S) { Args.push_back(Saver.save(S).data()); }
|
||||
void add(Twine S) { Args.push_back(Saver.save(S).data()); }
|
||||
void add(const char *S) { Args.push_back(Saver.save(S).data()); }
|
||||
void add(StringRef S) { Args.push_back(Saver.save(S)); }
|
||||
void add(std::string &S) { Args.push_back(Saver.save(S)); }
|
||||
void add(Twine S) { Args.push_back(Saver.save(S)); }
|
||||
void add(const char *S) { Args.push_back(Saver.save(S)); }
|
||||
|
||||
void run() {
|
||||
ErrorOr<std::string> ExeOrErr = sys::findProgramByName(Prog);
|
||||
if (auto EC = ExeOrErr.getError())
|
||||
fatal(EC, "unable to find " + Prog + " in PATH: ");
|
||||
const char *Exe = Saver.save(*ExeOrErr).data();
|
||||
StringRef Exe = Saver.save(*ExeOrErr);
|
||||
Args.insert(Args.begin(), Exe);
|
||||
Args.push_back(nullptr);
|
||||
if (sys::ExecuteAndWait(Args[0], Args.data()) != 0) {
|
||||
for (const char *S : Args)
|
||||
if (S)
|
||||
errs() << S << " ";
|
||||
fatal("ExecuteAndWait failed");
|
||||
}
|
||||
|
||||
std::vector<const char *> Vec;
|
||||
for (StringRef S : Args)
|
||||
Vec.push_back(S.data());
|
||||
Vec.push_back(nullptr);
|
||||
|
||||
if (sys::ExecuteAndWait(Args[0], Vec.data()) != 0)
|
||||
fatal("ExecuteAndWait failed: " +
|
||||
llvm::join(Args.begin(), Args.end(), " "));
|
||||
}
|
||||
|
||||
private:
|
||||
BumpPtrAllocator Alloc;
|
||||
StringSaver Saver;
|
||||
StringRef Prog;
|
||||
std::vector<const char *> Args;
|
||||
std::vector<StringRef> Args;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
|
@ -167,8 +169,7 @@ void parseMerge(StringRef S) {
|
|||
if (!Inserted) {
|
||||
StringRef Existing = Pair.first->second;
|
||||
if (Existing != To)
|
||||
errs() << "warning: " << S << ": already merged into " << Existing
|
||||
<< "\n";
|
||||
warn(S + ": already merged into " + Existing);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,11 +283,19 @@ static void quoteAndPrint(raw_ostream &Out, StringRef S) {
|
|||
namespace {
|
||||
class TemporaryFile {
|
||||
public:
|
||||
TemporaryFile(StringRef Prefix, StringRef Extn) {
|
||||
TemporaryFile(StringRef Prefix, StringRef Extn, StringRef Contents = "") {
|
||||
SmallString<128> S;
|
||||
if (auto EC = sys::fs::createTemporaryFile("lld-" + Prefix, Extn, S))
|
||||
fatal(EC, "cannot create a temporary file");
|
||||
Path = S.str();
|
||||
|
||||
if (!Contents.empty()) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Path, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
fatal(EC, "failed to open " + Path);
|
||||
OS << Contents;
|
||||
}
|
||||
}
|
||||
|
||||
TemporaryFile(TemporaryFile &&Obj) {
|
||||
|
|
@ -542,7 +551,7 @@ void fixupExports() {
|
|||
Export *Existing = Pair.first->second;
|
||||
if (E == *Existing || E.Name != Existing->Name)
|
||||
continue;
|
||||
errs() << "warning: duplicate /export option: " << E.Name << "\n";
|
||||
warn("duplicate /export option: " + E.Name);
|
||||
}
|
||||
Config->Exports = std::move(V);
|
||||
|
||||
|
|
@ -617,6 +626,26 @@ convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) {
|
|||
return File.getMemoryBuffer();
|
||||
}
|
||||
|
||||
// Run MSVC link.exe for given in-memory object files.
|
||||
// Command line options are copied from those given to LLD.
|
||||
// This is for the /msvclto option.
|
||||
void runMSVCLinker(std::string Rsp, ArrayRef<StringRef> Objects) {
|
||||
// Write the in-memory object files to disk.
|
||||
std::vector<TemporaryFile> Temps;
|
||||
for (StringRef S : Objects) {
|
||||
Temps.emplace_back("lto", "obj", S);
|
||||
Rsp += quote(Temps.back().Path) + " ";
|
||||
}
|
||||
|
||||
log("link.exe " + Rsp);
|
||||
|
||||
// Run MSVC link.exe.
|
||||
Temps.emplace_back("lto", "rsp", Rsp);
|
||||
Executor E("link.exe");
|
||||
E.add(Twine("@" + Temps.back().Path));
|
||||
E.run();
|
||||
}
|
||||
|
||||
// Create OptTable
|
||||
|
||||
// Create prefix string literals used in Options.td
|
||||
|
|
@ -653,16 +682,16 @@ opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) {
|
|||
|
||||
// Print the real command line if response files are expanded.
|
||||
if (Args.hasArg(OPT_verbose) && ArgsArr.size() != Argv.size()) {
|
||||
outs() << "Command line:";
|
||||
std::string Msg = "Command line:";
|
||||
for (const char *S : Argv)
|
||||
outs() << " " << S;
|
||||
outs() << "\n";
|
||||
Msg += " " + std::string(S);
|
||||
message(Msg);
|
||||
}
|
||||
|
||||
if (MissingCount)
|
||||
fatal(Twine(Args.getArgString(MissingIndex)) + ": missing argument");
|
||||
for (auto *Arg : Args.filtered(OPT_UNKNOWN))
|
||||
errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n";
|
||||
warn("ignoring unknown argument: " + Arg->getSpelling());
|
||||
return Args;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Error.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/Process.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <mutex>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
|
|
@ -21,10 +24,68 @@
|
|||
using namespace llvm;
|
||||
|
||||
namespace lld {
|
||||
// The functions defined in this file can be called from multiple threads,
|
||||
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
||||
static std::mutex Mu;
|
||||
|
||||
namespace coff {
|
||||
StringRef Argv0;
|
||||
uint64_t ErrorCount;
|
||||
raw_ostream *ErrorOS;
|
||||
|
||||
static LLVM_ATTRIBUTE_NORETURN void exitLld(int Val) {
|
||||
// Dealloc/destroy ManagedStatic variables before calling
|
||||
// _exit(). In a non-LTO build, this is a nop. In an LTO
|
||||
// build allows us to get the output of -time-passes.
|
||||
llvm_shutdown();
|
||||
|
||||
outs().flush();
|
||||
errs().flush();
|
||||
_exit(Val);
|
||||
}
|
||||
|
||||
static void print(StringRef S, raw_ostream::Colors C) {
|
||||
*ErrorOS << Argv0 + ": ";
|
||||
if (Config->ColorDiagnostics) {
|
||||
ErrorOS->changeColor(C, true);
|
||||
*ErrorOS << S;
|
||||
ErrorOS->resetColor();
|
||||
} else {
|
||||
*ErrorOS << S;
|
||||
}
|
||||
}
|
||||
|
||||
void log(const Twine &Msg) {
|
||||
if (Config->Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Argv0 << ": " << Msg << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void message(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
void error(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
|
||||
if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << Msg << "\n";
|
||||
} else if (ErrorCount == Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << "too many errors emitted, stopping now"
|
||||
<< " (use /ERRORLIMIT:0 to see all errors)\n";
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
++ErrorCount;
|
||||
}
|
||||
|
||||
void fatal(const Twine &Msg) {
|
||||
if (sys::Process::StandardErrHasColors()) {
|
||||
if (Config->ColorDiagnostics) {
|
||||
errs().changeColor(raw_ostream::RED, /*bold=*/true);
|
||||
errs() << "error: ";
|
||||
errs().resetColor();
|
||||
|
|
@ -32,10 +93,7 @@ void fatal(const Twine &Msg) {
|
|||
errs() << "error: ";
|
||||
}
|
||||
errs() << Msg << "\n";
|
||||
|
||||
outs().flush();
|
||||
errs().flush();
|
||||
_exit(1);
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
void fatal(std::error_code EC, const Twine &Msg) {
|
||||
|
|
@ -46,5 +104,11 @@ void fatal(llvm::Error &Err, const Twine &Msg) {
|
|||
fatal(errorToErrorCode(std::move(Err)), Msg);
|
||||
}
|
||||
|
||||
void warn(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
print("warning: ", raw_ostream::MAGENTA);
|
||||
*ErrorOS << Msg << "\n";
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
} // namespace lld
|
||||
|
|
|
|||
10
COFF/Error.h
10
COFF/Error.h
|
|
@ -16,11 +16,19 @@
|
|||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
extern uint64_t ErrorCount;
|
||||
extern llvm::raw_ostream *ErrorOS;
|
||||
extern llvm::StringRef Argv0;
|
||||
|
||||
void log(const Twine &Msg);
|
||||
void message(const Twine &Msg);
|
||||
void warn(const Twine &Msg);
|
||||
void error(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(llvm::Error &Err, const Twine &Prefix);
|
||||
|
||||
template <class T> T check(ErrorOr<T> &&V, const Twine &Prefix) {
|
||||
template <class T> T check(ErrorOr<T> V, const Twine &Prefix) {
|
||||
if (auto EC = V.getError())
|
||||
fatal(EC, Prefix);
|
||||
return std::move(*V);
|
||||
|
|
|
|||
|
|
@ -231,19 +231,16 @@ void ICF::run(const std::vector<Chunk *> &Vec) {
|
|||
++Cnt;
|
||||
} while (Repeat);
|
||||
|
||||
if (Config->Verbose)
|
||||
outs() << "\nICF needed " << Cnt << " iterations\n";
|
||||
log("ICF needed " + Twine(Cnt) + " iterations");
|
||||
|
||||
// Merge sections in the same colors.
|
||||
forEachColor([&](size_t Begin, size_t End) {
|
||||
if (End - Begin == 1)
|
||||
return;
|
||||
|
||||
if (Config->Verbose)
|
||||
outs() << "Selected " << Chunks[Begin]->getDebugName() << "\n";
|
||||
log("Selected " + Chunks[Begin]->getDebugName());
|
||||
for (size_t I = Begin + 1; I < End; ++I) {
|
||||
if (Config->Verbose)
|
||||
outs() << " Removed " << Chunks[I]->getDebugName() << "\n";
|
||||
log(" Removed " + Chunks[I]->getDebugName());
|
||||
Chunks[Begin]->replace(Chunks[I]);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@
|
|||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/LTO/legacy/LTOModule.h"
|
||||
#include "llvm/Object/Binary.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/COFF.h"
|
||||
|
|
@ -41,13 +39,23 @@ using namespace llvm::support::endian;
|
|||
|
||||
using llvm::Triple;
|
||||
using llvm::support::ulittle32_t;
|
||||
using llvm::sys::fs::file_magic;
|
||||
using llvm::sys::fs::identify_magic;
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
LLVMContext BitcodeFile::Context;
|
||||
/// Checks that Source is compatible with being a weak alias to Target.
|
||||
/// If Source is Undefined and has no weak alias set, makes it a weak
|
||||
/// alias to Target.
|
||||
static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
|
||||
SymbolBody *Source, SymbolBody *Target) {
|
||||
auto *U = dyn_cast<Undefined>(Source);
|
||||
if (!U)
|
||||
return;
|
||||
else if (!U->WeakAlias)
|
||||
U->WeakAlias = Target;
|
||||
else if (U->WeakAlias != Target)
|
||||
Symtab->reportDuplicate(Source->symbol(), F);
|
||||
}
|
||||
|
||||
ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
|
||||
|
||||
|
|
@ -177,15 +185,9 @@ void ObjectFile::initializeSymbols() {
|
|||
I += Sym.getNumberOfAuxSymbols();
|
||||
LastSectionNumber = Sym.getSectionNumber();
|
||||
}
|
||||
for (auto WeakAlias : WeakAliases) {
|
||||
auto *U = dyn_cast<Undefined>(WeakAlias.first);
|
||||
if (!U)
|
||||
continue;
|
||||
// Report an error if two undefined symbols have different weak aliases.
|
||||
if (U->WeakAlias && U->WeakAlias != SparseSymbolBodies[WeakAlias.second])
|
||||
Symtab->reportDuplicate(U->symbol(), this);
|
||||
U->WeakAlias = SparseSymbolBodies[WeakAlias.second];
|
||||
}
|
||||
for (auto WeakAlias : WeakAliases)
|
||||
checkAndSetWeakAlias(Symtab, this, WeakAlias.first,
|
||||
SparseSymbolBodies[WeakAlias.second]);
|
||||
}
|
||||
|
||||
SymbolBody *ObjectFile::createUndefined(COFFSymbolRef Sym) {
|
||||
|
|
@ -200,7 +202,10 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
|||
if (Sym.isCommon()) {
|
||||
auto *C = new (Alloc) CommonChunk(Sym);
|
||||
Chunks.push_back(C);
|
||||
return Symtab->addCommon(this, Sym, C)->body();
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
Symbol *S =
|
||||
Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C);
|
||||
return S->body();
|
||||
}
|
||||
if (Sym.isAbsolute()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
|
|
@ -247,10 +252,14 @@ SymbolBody *ObjectFile::createDefined(COFFSymbolRef Sym, const void *AuxP,
|
|||
}
|
||||
|
||||
DefinedRegular *B;
|
||||
if (Sym.isExternal())
|
||||
B = cast<DefinedRegular>(Symtab->addRegular(this, Sym, SC)->body());
|
||||
else
|
||||
B = new (Alloc) DefinedRegular(this, Sym, SC);
|
||||
if (Sym.isExternal()) {
|
||||
COFFObj->getSymbolName(Sym, Name);
|
||||
Symbol *S =
|
||||
Symtab->addRegular(this, Name, SC->isCOMDAT(), Sym.getGeneric(), SC);
|
||||
B = cast<DefinedRegular>(S->body());
|
||||
} else
|
||||
B = new (Alloc) DefinedRegular(this, /*Name*/ "", SC->isCOMDAT(),
|
||||
/*IsExternal*/ false, Sym.getGeneric(), SC);
|
||||
if (SC->isCOMDAT() && Sym.getValue() == 0 && !AuxP)
|
||||
SC->setSymbol(B);
|
||||
|
||||
|
|
@ -329,39 +338,32 @@ void ImportFile::parse() {
|
|||
}
|
||||
|
||||
void BitcodeFile::parse() {
|
||||
Context.enableDebugTypeODRUniquing();
|
||||
ErrorOr<std::unique_ptr<LTOModule>> ModOrErr = LTOModule::createFromBuffer(
|
||||
Context, MB.getBufferStart(), MB.getBufferSize(), llvm::TargetOptions());
|
||||
M = check(std::move(ModOrErr), "could not create LTO module");
|
||||
|
||||
StringSaver Saver(Alloc);
|
||||
for (unsigned I = 0, E = M->getSymbolCount(); I != E; ++I) {
|
||||
lto_symbol_attributes Attrs = M->getSymbolAttributes(I);
|
||||
if ((Attrs & LTO_SYMBOL_SCOPE_MASK) == LTO_SYMBOL_SCOPE_INTERNAL)
|
||||
continue;
|
||||
|
||||
StringRef SymName = Saver.save(M->getSymbolName(I));
|
||||
int SymbolDef = Attrs & LTO_SYMBOL_DEFINITION_MASK;
|
||||
if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) {
|
||||
SymbolBodies.push_back(Symtab->addUndefined(SymName, this, false)->body());
|
||||
Obj = check(lto::InputFile::create(MemoryBufferRef(
|
||||
MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
|
||||
StringRef SymName = Saver.save(ObjSym.getName());
|
||||
Symbol *Sym;
|
||||
if (ObjSym.isUndefined()) {
|
||||
Sym = Symtab->addUndefined(SymName, this, false);
|
||||
} else if (ObjSym.isCommon()) {
|
||||
Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize());
|
||||
} else if (ObjSym.isWeak() && ObjSym.isIndirect()) {
|
||||
// Weak external.
|
||||
Sym = Symtab->addUndefined(SymName, this, true);
|
||||
std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
|
||||
SymbolBody *Alias = Symtab->addUndefined(Saver.save(Fallback));
|
||||
checkAndSetWeakAlias(Symtab, this, Sym->body(), Alias);
|
||||
} else {
|
||||
bool Replaceable =
|
||||
(SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common
|
||||
(Attrs & LTO_SYMBOL_COMDAT) || // comdat
|
||||
(SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external
|
||||
(Attrs & LTO_SYMBOL_ALIAS)));
|
||||
SymbolBodies.push_back(
|
||||
Symtab->addBitcode(this, SymName, Replaceable)->body());
|
||||
bool IsCOMDAT = ObjSym.getComdatIndex() != -1;
|
||||
Sym = Symtab->addRegular(this, SymName, IsCOMDAT);
|
||||
}
|
||||
SymbolBodies.push_back(Sym->body());
|
||||
}
|
||||
|
||||
Directives = M->getLinkerOpts();
|
||||
Directives = Obj->getCOFFLinkerOpts();
|
||||
}
|
||||
|
||||
MachineTypes BitcodeFile::getMachineType() {
|
||||
if (!M)
|
||||
return IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
switch (Triple(M->getTargetTriple()).getArch()) {
|
||||
switch (Triple(Obj->getTargetTriple()).getArch()) {
|
||||
case Triple::x86_64:
|
||||
return AMD64;
|
||||
case Triple::x86:
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/LTO/legacy/LTOModule.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/Archive.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/StringSaver.h"
|
||||
|
|
@ -25,7 +24,6 @@
|
|||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
using llvm::LTOModule;
|
||||
using llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN;
|
||||
using llvm::COFF::MachineTypes;
|
||||
using llvm::object::Archive;
|
||||
|
|
@ -174,7 +172,6 @@ public:
|
|||
private:
|
||||
void parse() override;
|
||||
|
||||
llvm::BumpPtrAllocator Alloc;
|
||||
llvm::BumpPtrAllocator StringAllocAux;
|
||||
llvm::StringSaver StringAlloc;
|
||||
|
||||
|
|
@ -191,16 +188,12 @@ public:
|
|||
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
|
||||
std::vector<SymbolBody *> &getSymbols() { return SymbolBodies; }
|
||||
MachineTypes getMachineType() override;
|
||||
std::unique_ptr<LTOModule> takeModule() { return std::move(M); }
|
||||
|
||||
static llvm::LLVMContext Context;
|
||||
std::unique_ptr<llvm::lto::InputFile> Obj;
|
||||
|
||||
private:
|
||||
void parse() override;
|
||||
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
llvm::BumpPtrAllocator Alloc;
|
||||
std::unique_ptr<LTOModule> M;
|
||||
};
|
||||
} // namespace coff
|
||||
|
||||
|
|
|
|||
140
COFF/LTO.cpp
Normal file
140
COFF/LTO.cpp
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
//===- LTO.cpp ------------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LTO.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/IR/DiagnosticPrinter.h"
|
||||
#include "llvm/LTO/Config.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/SymbolicFile.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
static void diagnosticHandler(const DiagnosticInfo &DI) {
|
||||
SmallString<128> ErrStorage;
|
||||
raw_svector_ostream OS(ErrStorage);
|
||||
DiagnosticPrinterRawOStream DP(OS);
|
||||
DI.print(DP);
|
||||
warn(ErrStorage);
|
||||
}
|
||||
|
||||
static void checkError(Error E) {
|
||||
handleAllErrors(std::move(E), [&](ErrorInfoBase &EIB) -> Error {
|
||||
error(EIB.message());
|
||||
return Error::success();
|
||||
});
|
||||
}
|
||||
|
||||
static void saveBuffer(StringRef Buffer, const Twine &Path) {
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
|
||||
if (EC)
|
||||
error("cannot create " + Path + ": " + EC.message());
|
||||
OS << Buffer;
|
||||
}
|
||||
|
||||
static std::unique_ptr<lto::LTO> createLTO() {
|
||||
lto::Config Conf;
|
||||
Conf.Options = InitTargetOptionsFromCodeGenFlags();
|
||||
Conf.RelocModel = Reloc::PIC_;
|
||||
Conf.DisableVerify = true;
|
||||
Conf.DiagHandler = diagnosticHandler;
|
||||
Conf.OptLevel = Config->LTOOptLevel;
|
||||
if (Config->SaveTemps)
|
||||
checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
|
||||
/*UseInputModulePath*/ true));
|
||||
lto::ThinBackend Backend;
|
||||
if (Config->LTOJobs != 0)
|
||||
Backend = lto::createInProcessThinBackend(Config->LTOJobs);
|
||||
return llvm::make_unique<lto::LTO>(std::move(Conf), Backend,
|
||||
Config->LTOPartitions);
|
||||
}
|
||||
|
||||
BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
|
||||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined>(S, S->body()->getName());
|
||||
}
|
||||
|
||||
void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
lto::InputFile &Obj = *F.Obj;
|
||||
unsigned SymNum = 0;
|
||||
std::vector<SymbolBody *> SymBodies = F.getSymbols();
|
||||
std::vector<lto::SymbolResolution> Resols(SymBodies.size());
|
||||
|
||||
// Provide a resolution to the LTO API for each symbol.
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj.symbols()) {
|
||||
SymbolBody *B = SymBodies[SymNum];
|
||||
Symbol *Sym = B->symbol();
|
||||
lto::SymbolResolution &R = Resols[SymNum];
|
||||
++SymNum;
|
||||
|
||||
// Ideally we shouldn't check for SF_Undefined but currently IRObjectFile
|
||||
// reports two symbols for module ASM defined. Without this check, lld
|
||||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
R.Prevailing = !ObjSym.isUndefined() && B->getFile() == &F;
|
||||
R.VisibleToRegularObj = Sym->IsUsedInRegularObj;
|
||||
if (R.Prevailing)
|
||||
undefine(Sym);
|
||||
}
|
||||
checkError(LTOObj->add(std::move(F.Obj), Resols));
|
||||
}
|
||||
|
||||
// Merge all the bitcode files we have seen, codegen the result
|
||||
// and return the resulting objects.
|
||||
std::vector<StringRef> BitcodeCompiler::compile() {
|
||||
unsigned MaxTasks = LTOObj->getMaxTasks();
|
||||
Buff.resize(MaxTasks);
|
||||
|
||||
checkError(LTOObj->run([&](size_t Task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
|
||||
}));
|
||||
|
||||
std::vector<StringRef> Ret;
|
||||
for (unsigned I = 0; I != MaxTasks; ++I) {
|
||||
if (Buff[I].empty())
|
||||
continue;
|
||||
if (Config->SaveTemps) {
|
||||
if (I == 0)
|
||||
saveBuffer(Buff[I], Config->OutputFile + ".lto.obj");
|
||||
else
|
||||
saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.obj");
|
||||
}
|
||||
Ret.emplace_back(Buff[I].data(), Buff[I].size());
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
56
COFF/LTO.h
Normal file
56
COFF/LTO.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//===- LTO.h ----------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides a way to combine bitcode files into one COFF
|
||||
// file by compiling them using LLVM.
|
||||
//
|
||||
// If LTO is in use, your input files are not in regular COFF files
|
||||
// but instead LLVM bitcode files. In that case, the linker has to
|
||||
// convert bitcode files into the native format so that we can create
|
||||
// a COFF file that contains native code. This file provides that
|
||||
// functionality.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_LTO_H
|
||||
#define LLD_COFF_LTO_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
namespace lto {
|
||||
class LTO;
|
||||
}
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
class BitcodeFile;
|
||||
class InputFile;
|
||||
|
||||
class BitcodeCompiler {
|
||||
public:
|
||||
BitcodeCompiler();
|
||||
~BitcodeCompiler();
|
||||
|
||||
void add(BitcodeFile &F);
|
||||
std::vector<StringRef> compile();
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::lto::LTO> LTOObj;
|
||||
std::vector<SmallString<0>> Buff;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -104,7 +104,18 @@ static ImportNameType getNameType(StringRef Sym, StringRef ExtName) {
|
|||
|
||||
static std::string replace(StringRef S, StringRef From, StringRef To) {
|
||||
size_t Pos = S.find(From);
|
||||
assert(Pos != StringRef::npos);
|
||||
|
||||
// From and To may be mangled, but substrings in S may not.
|
||||
if (Pos == StringRef::npos && From.startswith("_") && To.startswith("_")) {
|
||||
From = From.substr(1);
|
||||
To = To.substr(1);
|
||||
Pos = S.find(From);
|
||||
}
|
||||
|
||||
if (Pos == StringRef::npos) {
|
||||
error(S + ": replacing '" + From + "' with '" + To + "' failed");
|
||||
return "";
|
||||
}
|
||||
return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str();
|
||||
}
|
||||
|
||||
|
|
|
|||
114
COFF/MapFile.cpp
Normal file
114
COFF/MapFile.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
//===- MapFile.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the /lldmap option. It shows lists in order and
|
||||
// hierarchically the output sections, input sections, input files and
|
||||
// symbol:
|
||||
//
|
||||
// Address Size Align Out In File Symbol
|
||||
// =================================================================
|
||||
// 00201000 00000015 4 .text
|
||||
// 00201000 0000000e 4 .text
|
||||
// 00201000 0000000e 4 test.o
|
||||
// 0020100e 00000000 0 local
|
||||
// 00201005 00000000 0 f(int)
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MapFile.h"
|
||||
#include "Error.h"
|
||||
#include "Symbols.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
static void writeOutSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
|
||||
uint64_t Align, StringRef Name) {
|
||||
OS << format("%08llx %08llx %5lld ", Address, Size, Align)
|
||||
<< left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeInSecLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
|
||||
uint64_t Align, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeOutSecLine(OS, Address, Size, Align, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeFileLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
|
||||
uint64_t Align, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeInSecLine(OS, Address, Size, Align, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeSymbolLine(raw_fd_ostream &OS, uint64_t Address, uint64_t Size,
|
||||
StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeFileLine(OS, Address, Size, 0, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeSectionChunk(raw_fd_ostream &OS, const SectionChunk *SC,
|
||||
StringRef &PrevName) {
|
||||
StringRef Name = SC->getSectionName();
|
||||
if (Name != PrevName) {
|
||||
writeInSecLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(), Name);
|
||||
OS << '\n';
|
||||
PrevName = Name;
|
||||
}
|
||||
coff::ObjectFile *File = SC->File;
|
||||
if (!File)
|
||||
return;
|
||||
writeFileLine(OS, SC->getRVA(), SC->getSize(), SC->getAlign(),
|
||||
toString(File));
|
||||
OS << '\n';
|
||||
ArrayRef<SymbolBody *> Syms = File->getSymbols();
|
||||
for (SymbolBody *Sym : Syms) {
|
||||
auto *DR = dyn_cast<DefinedRegular>(Sym);
|
||||
if (!DR || DR->getChunk() != SC ||
|
||||
DR->getCOFFSymbol().isSectionDefinition())
|
||||
continue;
|
||||
writeSymbolLine(OS, DR->getRVA(), SC->getSize(), toString(*Sym));
|
||||
OS << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
static void writeMapFile2(raw_fd_ostream &OS,
|
||||
ArrayRef<OutputSection *> OutputSections) {
|
||||
OS << "Address Size Align Out In File Symbol\n";
|
||||
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
uint32_t VA = Sec->getRVA();
|
||||
writeOutSecLine(OS, VA, Sec->getVirtualSize(), /*Align=*/PageSize,
|
||||
Sec->getName());
|
||||
OS << '\n';
|
||||
StringRef PrevName = "";
|
||||
for (Chunk *C : Sec->getChunks())
|
||||
if (const auto *SC = dyn_cast<SectionChunk>(C))
|
||||
writeSectionChunk(OS, SC, PrevName);
|
||||
}
|
||||
}
|
||||
|
||||
void coff::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
|
||||
if (Config->MapFile.empty())
|
||||
return;
|
||||
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
fatal("cannot open " + Config->MapFile + ": " + EC.message());
|
||||
writeMapFile2(OS, OutputSections);
|
||||
}
|
||||
22
COFF/MapFile.h
Normal file
22
COFF/MapFile.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//===- MapFile.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_COFF_MAPFILE_H
|
||||
#define LLD_COFF_MAPFILE_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
class OutputSection;
|
||||
void writeMapFile(llvm::ArrayRef<OutputSection *> OutputSections);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -163,17 +163,25 @@ private:
|
|||
case KwHeapsize:
|
||||
parseNumbers(&Config->HeapReserve, &Config->HeapCommit);
|
||||
return;
|
||||
case KwLibrary:
|
||||
parseName(&Config->OutputFile, &Config->ImageBase);
|
||||
if (!StringRef(Config->OutputFile).endswith_lower(".dll"))
|
||||
Config->OutputFile += ".dll";
|
||||
return;
|
||||
case KwStacksize:
|
||||
parseNumbers(&Config->StackReserve, &Config->StackCommit);
|
||||
return;
|
||||
case KwName:
|
||||
parseName(&Config->OutputFile, &Config->ImageBase);
|
||||
case KwLibrary:
|
||||
case KwName: {
|
||||
bool IsDll = Tok.K == KwLibrary; // Check before parseName.
|
||||
std::string Name;
|
||||
parseName(&Name, &Config->ImageBase);
|
||||
|
||||
// Append the appropriate file extension if not already present.
|
||||
StringRef Ext = IsDll ? ".dll" : ".exe";
|
||||
if (!StringRef(Name).endswith_lower(Ext))
|
||||
Name += Ext;
|
||||
|
||||
// Set the output file, but don't override /out if it was already passed.
|
||||
if (Config->OutputFile.empty())
|
||||
Config->OutputFile = Name;
|
||||
return;
|
||||
}
|
||||
case KwVersion:
|
||||
parseVersion(&Config->MajorImageVersion, &Config->MinorImageVersion);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ def base : P<"base", "Base address of the program">;
|
|||
def defaultlib : P<"defaultlib", "Add the library to the list of input files">;
|
||||
def delayload : P<"delayload", "Delay loaded DLL name">;
|
||||
def entry : P<"entry", "Name of entry point symbol">;
|
||||
def errorlimit : P<"errorlimit",
|
||||
"Maximum number of errors to emit before stopping (0 = no limit)">;
|
||||
def export : P<"export", "Export a function">;
|
||||
// No help text because /failifmismatch is not intended to be used by the user.
|
||||
def failifmismatch : P<"failifmismatch", "">;
|
||||
|
|
@ -28,6 +30,8 @@ def heap : P<"heap", "Size of the heap">;
|
|||
def implib : P<"implib", "Import library name">;
|
||||
def libpath : P<"libpath", "Additional library search path">;
|
||||
def linkrepro : P<"linkrepro", "Dump linker invocation and input files for debugging">;
|
||||
def lldsavetemps : F<"lldsavetemps">,
|
||||
HelpText<"Save temporary files instead of deleting them">;
|
||||
def machine : P<"machine", "Specify target platform">;
|
||||
def merge : P<"merge", "Combine sections">;
|
||||
def mllvm : P<"mllvm", "Options to pass to LLVM">;
|
||||
|
|
@ -78,6 +82,8 @@ def force_unresolved : F<"force:unresolved">;
|
|||
|
||||
defm allowbind: B<"allowbind", "Disable DLL binding">;
|
||||
defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">;
|
||||
defm appcontainer : B<"appcontainer",
|
||||
"Image can only be run in an app container">;
|
||||
defm dynamicbase : B<"dynamicbase",
|
||||
"Disable address space layout randomization">;
|
||||
defm fixed : B<"fixed", "Enable base relocations">;
|
||||
|
|
@ -91,7 +97,9 @@ def help : F<"help">;
|
|||
def help_q : Flag<["/?", "-?"], "">, Alias<help>;
|
||||
|
||||
// LLD extensions
|
||||
def nopdb : F<"nopdb">, HelpText<"Disable PDB generation for DWARF users">;
|
||||
def nosymtab : F<"nosymtab">;
|
||||
def msvclto : F<"msvclto">;
|
||||
|
||||
// Flags for debugging
|
||||
def debugpdb : F<"debugpdb">;
|
||||
|
|
|
|||
109
COFF/PDB.cpp
109
COFF/PDB.cpp
|
|
@ -20,20 +20,23 @@
|
|||
#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
|
||||
#include "llvm/DebugInfo/CodeView/TypeTableBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/ByteStream.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
|
||||
#include "llvm/DebugInfo/MSF/MSFCommon.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/DbiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/DbiStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/InfoStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/InfoStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/PDBFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/PDBFileBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Raw/TpiStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBFileBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/PDBTypeServerHandler.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/StringTableBuilder.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
|
||||
#include "llvm/DebugInfo/PDB/Native/TpiStreamBuilder.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include "llvm/Support/BinaryByteStream.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
#include "llvm/Support/ScopedPrinter.h"
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -79,32 +82,49 @@ static ArrayRef<uint8_t> getDebugSection(ObjectFile *File, StringRef SecName) {
|
|||
return Data.slice(4);
|
||||
}
|
||||
|
||||
// Merge .debug$T sections and returns it.
|
||||
static std::vector<uint8_t> mergeDebugT(SymbolTable *Symtab) {
|
||||
ScopedPrinter W(outs());
|
||||
static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
|
||||
codeview::TypeTableBuilder &TypeTable) {
|
||||
// Start the TPI or IPI stream header.
|
||||
TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
|
||||
|
||||
// Flatten the in memory type table.
|
||||
TypeTable.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
|
||||
// FIXME: Hash types.
|
||||
TpiBuilder.addTypeRecord(Rec, None);
|
||||
});
|
||||
}
|
||||
|
||||
// Merge .debug$T sections into IpiData and TpiData.
|
||||
static void mergeDebugT(SymbolTable *Symtab, pdb::PDBFileBuilder &Builder,
|
||||
codeview::TypeTableBuilder &TypeTable,
|
||||
codeview::TypeTableBuilder &IDTable) {
|
||||
// Visit all .debug$T sections to add them to Builder.
|
||||
codeview::TypeTableBuilder Builder(BAlloc);
|
||||
for (ObjectFile *File : Symtab->ObjectFiles) {
|
||||
ArrayRef<uint8_t> Data = getDebugSection(File, ".debug$T");
|
||||
if (Data.empty())
|
||||
continue;
|
||||
|
||||
msf::ByteStream Stream(Data);
|
||||
BinaryByteStream Stream(Data, support::little);
|
||||
codeview::CVTypeArray Types;
|
||||
msf::StreamReader Reader(Stream);
|
||||
BinaryStreamReader Reader(Stream);
|
||||
// Follow type servers. If the same type server is encountered more than
|
||||
// once for this instance of `PDBTypeServerHandler` (for example if many
|
||||
// object files reference the same TypeServer), the types from the
|
||||
// TypeServer will only be visited once.
|
||||
pdb::PDBTypeServerHandler Handler;
|
||||
Handler.addSearchPath(llvm::sys::path::parent_path(File->getName()));
|
||||
if (auto EC = Reader.readArray(Types, Reader.getLength()))
|
||||
fatal(EC, "Reader::readArray failed");
|
||||
if (!codeview::mergeTypeStreams(Builder, Types))
|
||||
fatal("codeview::mergeTypeStreams failed");
|
||||
if (auto Err =
|
||||
codeview::mergeTypeStreams(IDTable, TypeTable, &Handler, Types))
|
||||
fatal(Err, "codeview::mergeTypeStreams failed");
|
||||
}
|
||||
|
||||
// Construct section contents.
|
||||
std::vector<uint8_t> V;
|
||||
Builder.ForEachRecord([&](TypeIndex TI, ArrayRef<uint8_t> Rec) {
|
||||
V.insert(V.end(), Rec.begin(), Rec.end());
|
||||
});
|
||||
return V;
|
||||
// Construct TPI stream contents.
|
||||
addTypeInfo(Builder.getTpiBuilder(), TypeTable);
|
||||
|
||||
// Construct IPI stream contents.
|
||||
addTypeInfo(Builder.getIpiBuilder(), IDTable);
|
||||
}
|
||||
|
||||
static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) {
|
||||
|
|
@ -115,6 +135,8 @@ static void dumpDebugT(ScopedPrinter &W, ObjectFile *File) {
|
|||
|
||||
TypeDatabase TDB;
|
||||
TypeDumpVisitor TDV(TDB, &W, false);
|
||||
// Use a default implementation that does not follow type servers and instead
|
||||
// just dumps the contents of the TypeServer2 record.
|
||||
CVTypeDumper TypeDumper(TDB);
|
||||
if (auto EC = TypeDumper.dump(Data, TDV))
|
||||
fatal(EC, "CVTypeDumper::dump failed");
|
||||
|
|
@ -126,9 +148,9 @@ static void dumpDebugS(ScopedPrinter &W, ObjectFile *File) {
|
|||
if (Data.empty())
|
||||
return;
|
||||
|
||||
msf::ByteStream Stream(Data);
|
||||
BinaryByteStream Stream(Data, llvm::support::little);
|
||||
CVSymbolArray Symbols;
|
||||
msf::StreamReader Reader(Stream);
|
||||
BinaryStreamReader Reader(Stream);
|
||||
if (auto EC = Reader.readArray(Symbols, Reader.getLength()))
|
||||
fatal(EC, "StreamReader.readArray<CVSymbolArray> failed");
|
||||
|
||||
|
|
@ -148,17 +170,6 @@ static void dumpCodeView(SymbolTable *Symtab) {
|
|||
}
|
||||
}
|
||||
|
||||
static void addTypeInfo(pdb::TpiStreamBuilder &TpiBuilder,
|
||||
ArrayRef<uint8_t> Data) {
|
||||
msf::ByteStream Stream(Data);
|
||||
codeview::CVTypeArray Records;
|
||||
msf::StreamReader Reader(Stream);
|
||||
if (auto EC = Reader.readArray(Records, Reader.getLength()))
|
||||
fatal(EC, "Reader.readArray failed");
|
||||
for (const codeview::CVType &Rec : Records)
|
||||
TpiBuilder.addTypeRecord(Rec);
|
||||
}
|
||||
|
||||
// Creates a PDB file.
|
||||
void coff::createPDB(StringRef Path, SymbolTable *Symtab,
|
||||
ArrayRef<uint8_t> SectionTable,
|
||||
|
|
@ -177,9 +188,12 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab,
|
|||
|
||||
// Add an Info stream.
|
||||
auto &InfoBuilder = Builder.getInfoBuilder();
|
||||
InfoBuilder.setAge(DI->PDB70.Age);
|
||||
InfoBuilder.setGuid(
|
||||
*reinterpret_cast<const pdb::PDB_UniqueId *>(&DI->PDB70.Signature));
|
||||
InfoBuilder.setAge(DI ? DI->PDB70.Age : 0);
|
||||
|
||||
pdb::PDB_UniqueId uuid{};
|
||||
if (DI)
|
||||
memcpy(&uuid, &DI->PDB70.Signature, sizeof(uuid));
|
||||
InfoBuilder.setGuid(uuid);
|
||||
// Should be the current time, but set 0 for reproducibilty.
|
||||
InfoBuilder.setSignature(0);
|
||||
InfoBuilder.setVersion(pdb::PdbRaw_ImplVer::PdbImplVC70);
|
||||
|
|
@ -188,18 +202,9 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab,
|
|||
auto &DbiBuilder = Builder.getDbiBuilder();
|
||||
DbiBuilder.setVersionHeader(pdb::PdbDbiV110);
|
||||
|
||||
// Add an empty TPI stream.
|
||||
auto &TpiBuilder = Builder.getTpiBuilder();
|
||||
TpiBuilder.setVersionHeader(pdb::PdbTpiV80);
|
||||
std::vector<uint8_t> TpiData;
|
||||
if (Config->DebugPdb) {
|
||||
TpiData = mergeDebugT(Symtab);
|
||||
addTypeInfo(TpiBuilder, TpiData);
|
||||
}
|
||||
|
||||
// Add an empty IPI stream.
|
||||
auto &IpiBuilder = Builder.getIpiBuilder();
|
||||
IpiBuilder.setVersionHeader(pdb::PdbTpiV80);
|
||||
codeview::TypeTableBuilder TypeTable(BAlloc);
|
||||
codeview::TypeTableBuilder IDTable(BAlloc);
|
||||
mergeDebugT(Symtab, Builder, TypeTable, IDTable);
|
||||
|
||||
// Add Section Contributions.
|
||||
std::vector<pdb::SectionContrib> Contribs =
|
||||
|
|
@ -214,7 +219,7 @@ void coff::createPDB(StringRef Path, SymbolTable *Symtab,
|
|||
pdb::DbiStreamBuilder::createSectionMap(Sections);
|
||||
DbiBuilder.setSectionMap(SectionMap);
|
||||
|
||||
ExitOnErr(DbiBuilder.addModuleInfo("", "* Linker *"));
|
||||
ExitOnErr(DbiBuilder.addModuleInfo("* Linker *"));
|
||||
|
||||
// Add COFF section header stream.
|
||||
ExitOnErr(
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@
|
|||
#include "Config.h"
|
||||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "LTO.h"
|
||||
#include "Memory.h"
|
||||
#include "Symbols.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/LTO/legacy/LTOCodeGenerator.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <utility>
|
||||
|
|
@ -24,11 +24,40 @@ using namespace llvm;
|
|||
namespace lld {
|
||||
namespace coff {
|
||||
|
||||
enum SymbolPreference {
|
||||
SP_EXISTING = -1,
|
||||
SP_CONFLICT = 0,
|
||||
SP_NEW = 1,
|
||||
};
|
||||
|
||||
/// Checks if an existing symbol S should be kept or replaced by a new symbol.
|
||||
/// Returns SP_EXISTING when S should be kept, SP_NEW when the new symbol
|
||||
/// should be kept, and SP_CONFLICT if no valid resolution exists.
|
||||
static SymbolPreference compareDefined(Symbol *S, bool WasInserted,
|
||||
bool NewIsCOMDAT) {
|
||||
// If the symbol wasn't previously known, the new symbol wins by default.
|
||||
if (WasInserted || !isa<Defined>(S->body()))
|
||||
return SP_NEW;
|
||||
|
||||
// If the existing symbol is a DefinedRegular, both it and the new symbol
|
||||
// must be comdats. In that case, we have no reason to prefer one symbol
|
||||
// over the other, and we keep the existing one. If one of the symbols
|
||||
// is not a comdat, we report a conflict.
|
||||
if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
|
||||
if (NewIsCOMDAT && R->isCOMDAT())
|
||||
return SP_EXISTING;
|
||||
else
|
||||
return SP_CONFLICT;
|
||||
}
|
||||
|
||||
// Existing symbol is not a DefinedRegular; new symbol wins.
|
||||
return SP_NEW;
|
||||
}
|
||||
|
||||
SymbolTable *Symtab;
|
||||
|
||||
void SymbolTable::addFile(InputFile *File) {
|
||||
if (Config->Verbose)
|
||||
outs() << "Reading " << toString(File) << "\n";
|
||||
log("Reading " + toString(File));
|
||||
File->parse();
|
||||
|
||||
MachineTypes MT = File->getMachineType();
|
||||
|
|
@ -51,8 +80,7 @@ void SymbolTable::addFile(InputFile *File) {
|
|||
if (S.empty())
|
||||
return;
|
||||
|
||||
if (Config->Verbose)
|
||||
outs() << "Directives: " << toString(File) << ": " << S << "\n";
|
||||
log("Directives: " + toString(File) + ": " + S);
|
||||
Driver->parseDirectives(S);
|
||||
}
|
||||
|
||||
|
|
@ -106,12 +134,11 @@ void SymbolTable::reportRemainingUndefines() {
|
|||
return;
|
||||
for (SymbolBody *B : Config->GCRoot)
|
||||
if (Undefs.count(B))
|
||||
errs() << "<root>: undefined symbol: " << B->getName() << "\n";
|
||||
warn("<root>: undefined symbol: " + B->getName());
|
||||
for (ObjectFile *File : ObjectFiles)
|
||||
for (SymbolBody *Sym : File->getSymbols())
|
||||
if (Undefs.count(Sym))
|
||||
errs() << toString(File) << ": undefined symbol: " << Sym->getName()
|
||||
<< "\n";
|
||||
warn(toString(File) + ": undefined symbol: " + Sym->getName());
|
||||
if (!Config->Force)
|
||||
fatal("link failed");
|
||||
}
|
||||
|
|
@ -163,7 +190,7 @@ void SymbolTable::addLazy(ArchiveFile *F, const Archive::Symbol Sym) {
|
|||
}
|
||||
|
||||
void SymbolTable::reportDuplicate(Symbol *Existing, InputFile *NewFile) {
|
||||
fatal("duplicate symbol: " + toString(*Existing->body()) + " in " +
|
||||
error("duplicate symbol: " + toString(*Existing->body()) + " in " +
|
||||
toString(Existing->body()->getFile()) + " and in " +
|
||||
(NewFile ? toString(NewFile) : "(internal)"));
|
||||
}
|
||||
|
|
@ -204,59 +231,35 @@ Symbol *SymbolTable::addRelative(StringRef N, uint64_t VA) {
|
|||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addRegular(ObjectFile *F, COFFSymbolRef Sym,
|
||||
Symbol *SymbolTable::addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
const coff_symbol_generic *Sym,
|
||||
SectionChunk *C) {
|
||||
StringRef Name;
|
||||
F->getCOFFObj()->getSymbolName(Sym, Name);
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body()))
|
||||
replaceBody<DefinedRegular>(S, F, Sym, C);
|
||||
else if (auto *R = dyn_cast<DefinedRegular>(S->body())) {
|
||||
if (!C->isCOMDAT() || !R->isCOMDAT())
|
||||
reportDuplicate(S, F);
|
||||
} else if (auto *B = dyn_cast<DefinedBitcode>(S->body())) {
|
||||
if (B->IsReplaceable)
|
||||
replaceBody<DefinedRegular>(S, F, Sym, C);
|
||||
else if (!C->isCOMDAT())
|
||||
reportDuplicate(S, F);
|
||||
} else
|
||||
replaceBody<DefinedRegular>(S, F, Sym, C);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (WasInserted || isa<Undefined>(S->body()) || isa<Lazy>(S->body())) {
|
||||
replaceBody<DefinedBitcode>(S, F, N, IsReplaceable);
|
||||
return S;
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
SymbolPreference SP = compareDefined(S, WasInserted, IsCOMDAT);
|
||||
if (SP == SP_CONFLICT) {
|
||||
reportDuplicate(S, F);
|
||||
} else if (SP == SP_NEW) {
|
||||
replaceBody<DefinedRegular>(S, F, N, IsCOMDAT, /*IsExternal*/ true, Sym, C);
|
||||
}
|
||||
if (isa<DefinedCommon>(S->body()))
|
||||
return S;
|
||||
if (IsReplaceable)
|
||||
if (isa<DefinedRegular>(S->body()) || isa<DefinedBitcode>(S->body()))
|
||||
return S;
|
||||
reportDuplicate(S, F);
|
||||
return S;
|
||||
}
|
||||
|
||||
Symbol *SymbolTable::addCommon(ObjectFile *F, COFFSymbolRef Sym,
|
||||
CommonChunk *C) {
|
||||
StringRef Name;
|
||||
F->getCOFFObj()->getSymbolName(Sym, Name);
|
||||
Symbol *SymbolTable::addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const coff_symbol_generic *Sym, CommonChunk *C) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name);
|
||||
S->IsUsedInRegularObj = true;
|
||||
std::tie(S, WasInserted) = insert(N);
|
||||
if (!isa<BitcodeFile>(F))
|
||||
S->IsUsedInRegularObj = true;
|
||||
if (WasInserted || !isa<DefinedCOFF>(S->body()))
|
||||
replaceBody<DefinedCommon>(S, F, Sym, C);
|
||||
replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
else if (auto *DC = dyn_cast<DefinedCommon>(S->body()))
|
||||
if (Sym.getValue() > DC->getSize())
|
||||
replaceBody<DefinedCommon>(S, F, Sym, C);
|
||||
if (Size > DC->getSize())
|
||||
replaceBody<DefinedCommon>(S, F, N, Size, Sym, C);
|
||||
return S;
|
||||
}
|
||||
|
||||
|
|
@ -345,75 +348,21 @@ SymbolBody *SymbolTable::addUndefined(StringRef Name) {
|
|||
return addUndefined(Name, nullptr, false)->body();
|
||||
}
|
||||
|
||||
void SymbolTable::printMap(llvm::raw_ostream &OS) {
|
||||
for (ObjectFile *File : ObjectFiles) {
|
||||
OS << toString(File) << ":\n";
|
||||
for (SymbolBody *Body : File->getSymbols())
|
||||
if (auto *R = dyn_cast<DefinedRegular>(Body))
|
||||
if (R->getChunk()->isLive())
|
||||
OS << Twine::utohexstr(Config->ImageBase + R->getRVA())
|
||||
<< " " << R->getName() << "\n";
|
||||
}
|
||||
std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
|
||||
LTO.reset(new BitcodeCompiler);
|
||||
for (BitcodeFile *F : BitcodeFiles)
|
||||
LTO->add(*F);
|
||||
return LTO->compile();
|
||||
}
|
||||
|
||||
void SymbolTable::addCombinedLTOObjects() {
|
||||
if (BitcodeFiles.empty())
|
||||
return;
|
||||
|
||||
// Create an object file and add it to the symbol table by replacing any
|
||||
// DefinedBitcode symbols with the definitions in the object file.
|
||||
LTOCodeGenerator CG(BitcodeFile::Context);
|
||||
CG.setOptLevel(Config->LTOOptLevel);
|
||||
for (ObjectFile *Obj : createLTOObjects(&CG))
|
||||
for (StringRef Object : compileBitcodeFiles()) {
|
||||
auto *Obj = make<ObjectFile>(MemoryBufferRef(Object, "lto.tmp"));
|
||||
Obj->parse();
|
||||
}
|
||||
|
||||
// Combine and compile bitcode files and then return the result
|
||||
// as a vector of regular COFF object files.
|
||||
std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
|
||||
// All symbols referenced by non-bitcode objects, including GC roots, must be
|
||||
// preserved. We must also replace bitcode symbols with undefined symbols so
|
||||
// that they may be replaced with real definitions without conflicting.
|
||||
for (BitcodeFile *File : BitcodeFiles)
|
||||
for (SymbolBody *Body : File->getSymbols()) {
|
||||
if (!isa<DefinedBitcode>(Body))
|
||||
continue;
|
||||
if (Body->symbol()->IsUsedInRegularObj)
|
||||
CG->addMustPreserveSymbol(Body->getName());
|
||||
replaceBody<Undefined>(Body->symbol(), Body->getName());
|
||||
}
|
||||
|
||||
CG->setModule(BitcodeFiles[0]->takeModule());
|
||||
for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I)
|
||||
CG->addModule(BitcodeFiles[I]->takeModule().get());
|
||||
|
||||
bool DisableVerify = true;
|
||||
#ifdef NDEBUG
|
||||
DisableVerify = false;
|
||||
#endif
|
||||
if (!CG->optimize(DisableVerify, false, false, false))
|
||||
fatal(""); // optimize() should have emitted any error message.
|
||||
|
||||
Objs.resize(Config->LTOJobs);
|
||||
// Use std::list to avoid invalidation of pointers in OSPtrs.
|
||||
std::list<raw_svector_ostream> OSs;
|
||||
std::vector<raw_pwrite_stream *> OSPtrs;
|
||||
for (SmallString<0> &Obj : Objs) {
|
||||
OSs.emplace_back(Obj);
|
||||
OSPtrs.push_back(&OSs.back());
|
||||
ObjectFiles.push_back(Obj);
|
||||
}
|
||||
|
||||
if (!CG->compileOptimized(OSPtrs))
|
||||
fatal(""); // compileOptimized() should have emitted any error message.
|
||||
|
||||
std::vector<ObjectFile *> ObjFiles;
|
||||
for (SmallString<0> &Obj : Objs) {
|
||||
auto *ObjFile = make<ObjectFile>(MemoryBufferRef(Obj, "<LTO object>"));
|
||||
ObjectFiles.push_back(ObjFile);
|
||||
ObjFiles.push_back(ObjFile);
|
||||
}
|
||||
|
||||
return ObjFiles;
|
||||
}
|
||||
|
||||
} // namespace coff
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#define LLD_COFF_SYMBOL_TABLE_H
|
||||
|
||||
#include "InputFiles.h"
|
||||
#include "LTO.h"
|
||||
#include "llvm/ADT/CachedHashString.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseMapInfo.h"
|
||||
|
|
@ -69,13 +70,11 @@ public:
|
|||
void mangleMaybe(SymbolBody *B);
|
||||
StringRef findMangle(StringRef Name);
|
||||
|
||||
// Print a layout map to OS.
|
||||
void printMap(llvm::raw_ostream &OS);
|
||||
|
||||
// Build a set of COFF objects representing the combined contents of
|
||||
// BitcodeFiles and add them to the symbol table. Called after all files are
|
||||
// added and before the writer writes results to a file.
|
||||
void addCombinedLTOObjects();
|
||||
std::vector<StringRef> compileBitcodeFiles();
|
||||
|
||||
// The writer needs to handle DLL import libraries specially in
|
||||
// order to create the import descriptor table.
|
||||
|
|
@ -93,9 +92,12 @@ public:
|
|||
Symbol *addUndefined(StringRef Name, InputFile *F, bool IsWeakAlias);
|
||||
void addLazy(ArchiveFile *F, const Archive::Symbol Sym);
|
||||
Symbol *addAbsolute(StringRef N, COFFSymbolRef S);
|
||||
Symbol *addRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C);
|
||||
Symbol *addBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable);
|
||||
Symbol *addCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C);
|
||||
Symbol *addRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr,
|
||||
SectionChunk *C = nullptr);
|
||||
Symbol *addCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const llvm::object::coff_symbol_generic *S = nullptr,
|
||||
CommonChunk *C = nullptr);
|
||||
Symbol *addImportData(StringRef N, ImportFile *F);
|
||||
Symbol *addImportThunk(StringRef Name, DefinedImportData *S,
|
||||
uint16_t Machine);
|
||||
|
|
@ -113,12 +115,11 @@ private:
|
|||
StringRef findByPrefix(StringRef Prefix);
|
||||
|
||||
void addCombinedLTOObject(ObjectFile *Obj);
|
||||
std::vector<ObjectFile *> createLTOObjects(llvm::LTOCodeGenerator *CG);
|
||||
|
||||
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> Symtab;
|
||||
|
||||
std::vector<BitcodeFile *> BitcodeFiles;
|
||||
std::vector<SmallString<0>> Objs;
|
||||
std::unique_ptr<BitcodeCompiler> LTO;
|
||||
};
|
||||
|
||||
extern SymbolTable *Symtab;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace lld {
|
|||
namespace coff {
|
||||
|
||||
StringRef SymbolBody::getName() {
|
||||
// DefinedCOFF names are read lazily for a performance reason.
|
||||
// COFF symbol names are read lazily for a performance reason.
|
||||
// Non-external symbol names are never used by the linker except for logging
|
||||
// or debugging. Their internal references are resolved not by name but by
|
||||
// symbol index. And because they are not external, no one can refer them by
|
||||
|
|
@ -39,7 +39,7 @@ StringRef SymbolBody::getName() {
|
|||
// is a waste of time.
|
||||
if (Name.empty()) {
|
||||
auto *D = cast<DefinedCOFF>(this);
|
||||
D->File->getCOFFObj()->getSymbolName(D->Sym, Name);
|
||||
cast<ObjectFile>(D->File)->getCOFFObj()->getSymbolName(D->Sym, Name);
|
||||
}
|
||||
return Name;
|
||||
}
|
||||
|
|
@ -47,15 +47,14 @@ StringRef SymbolBody::getName() {
|
|||
InputFile *SymbolBody::getFile() {
|
||||
if (auto *Sym = dyn_cast<DefinedCOFF>(this))
|
||||
return Sym->File;
|
||||
if (auto *Sym = dyn_cast<DefinedBitcode>(this))
|
||||
return Sym->File;
|
||||
if (auto *Sym = dyn_cast<Lazy>(this))
|
||||
return Sym->File;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
|
||||
size_t SymSize = File->getCOFFObj()->getSymbolTableEntrySize();
|
||||
size_t SymSize =
|
||||
cast<ObjectFile>(File)->getCOFFObj()->getSymbolTableEntrySize();
|
||||
if (SymSize == sizeof(coff_symbol16))
|
||||
return COFFSymbolRef(reinterpret_cast<const coff_symbol16 *>(Sym));
|
||||
assert(SymSize == sizeof(coff_symbol32));
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ using llvm::object::coff_import_header;
|
|||
using llvm::object::coff_symbol_generic;
|
||||
|
||||
class ArchiveFile;
|
||||
class BitcodeFile;
|
||||
class InputFile;
|
||||
class ObjectFile;
|
||||
struct Symbol;
|
||||
|
|
@ -52,13 +51,12 @@ public:
|
|||
DefinedImportDataKind,
|
||||
DefinedAbsoluteKind,
|
||||
DefinedRelativeKind,
|
||||
DefinedBitcodeKind,
|
||||
|
||||
UndefinedKind,
|
||||
LazyKind,
|
||||
|
||||
LastDefinedCOFFKind = DefinedCommonKind,
|
||||
LastDefinedKind = DefinedBitcodeKind,
|
||||
LastDefinedKind = DefinedRelativeKind,
|
||||
};
|
||||
|
||||
Kind kind() const { return static_cast<Kind>(SymbolKind); }
|
||||
|
|
@ -81,7 +79,7 @@ protected:
|
|||
friend SymbolTable;
|
||||
explicit SymbolBody(Kind K, StringRef N = "")
|
||||
: SymbolKind(K), IsExternal(true), IsCOMDAT(false),
|
||||
IsReplaceable(false), WrittenToSymtab(false), Name(N) {}
|
||||
WrittenToSymtab(false), Name(N) {}
|
||||
|
||||
const unsigned SymbolKind : 8;
|
||||
unsigned IsExternal : 1;
|
||||
|
|
@ -89,11 +87,9 @@ protected:
|
|||
// This bit is used by the \c DefinedRegular subclass.
|
||||
unsigned IsCOMDAT : 1;
|
||||
|
||||
// This bit is used by the \c DefinedBitcode subclass.
|
||||
unsigned IsReplaceable : 1;
|
||||
|
||||
public:
|
||||
// This bit is used by Writer::createSymbolAndStringTable().
|
||||
// This bit is used by Writer::createSymbolAndStringTable() to prevent
|
||||
// symbols from being written to the symbol table more than once.
|
||||
unsigned WrittenToSymtab : 1;
|
||||
|
||||
protected:
|
||||
|
|
@ -104,7 +100,7 @@ protected:
|
|||
// etc.
|
||||
class Defined : public SymbolBody {
|
||||
public:
|
||||
Defined(Kind K, StringRef N = "") : SymbolBody(K, N) {}
|
||||
Defined(Kind K, StringRef N) : SymbolBody(K, N) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() <= LastDefinedKind;
|
||||
|
|
@ -127,22 +123,25 @@ public:
|
|||
bool isExecutable();
|
||||
};
|
||||
|
||||
// Symbols defined via a COFF object file.
|
||||
// Symbols defined via a COFF object file or bitcode file. For COFF files, this
|
||||
// stores a coff_symbol_generic*, and names of internal symbols are lazily
|
||||
// loaded through that. For bitcode files, Sym is nullptr and the name is stored
|
||||
// as a StringRef.
|
||||
class DefinedCOFF : public Defined {
|
||||
friend SymbolBody;
|
||||
public:
|
||||
DefinedCOFF(Kind K, ObjectFile *F, COFFSymbolRef S)
|
||||
: Defined(K), File(F), Sym(S.getGeneric()) {}
|
||||
DefinedCOFF(Kind K, InputFile *F, StringRef N, const coff_symbol_generic *S)
|
||||
: Defined(K, N), File(F), Sym(S) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() <= LastDefinedCOFFKind;
|
||||
}
|
||||
|
||||
ObjectFile *getFile() { return File; }
|
||||
InputFile *getFile() { return File; }
|
||||
|
||||
COFFSymbolRef getCOFFSymbol();
|
||||
|
||||
ObjectFile *File;
|
||||
InputFile *File;
|
||||
|
||||
protected:
|
||||
const coff_symbol_generic *Sym;
|
||||
|
|
@ -151,10 +150,13 @@ protected:
|
|||
// Regular defined symbols read from object file symbol tables.
|
||||
class DefinedRegular : public DefinedCOFF {
|
||||
public:
|
||||
DefinedRegular(ObjectFile *F, COFFSymbolRef S, SectionChunk *C)
|
||||
: DefinedCOFF(DefinedRegularKind, F, S), Data(&C->Repl) {
|
||||
IsExternal = S.isExternal();
|
||||
IsCOMDAT = C->isCOMDAT();
|
||||
DefinedRegular(InputFile *F, StringRef N, bool IsCOMDAT,
|
||||
bool IsExternal = false,
|
||||
const coff_symbol_generic *S = nullptr,
|
||||
SectionChunk *C = nullptr)
|
||||
: DefinedCOFF(DefinedRegularKind, F, N, S), Data(C ? &C->Repl : nullptr) {
|
||||
this->IsExternal = IsExternal;
|
||||
this->IsCOMDAT = IsCOMDAT;
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
|
|
@ -172,9 +174,11 @@ private:
|
|||
|
||||
class DefinedCommon : public DefinedCOFF {
|
||||
public:
|
||||
DefinedCommon(ObjectFile *F, COFFSymbolRef S, CommonChunk *C)
|
||||
: DefinedCOFF(DefinedCommonKind, F, S), Data(C) {
|
||||
IsExternal = S.isExternal();
|
||||
DefinedCommon(InputFile *F, StringRef N, uint64_t Size,
|
||||
const coff_symbol_generic *S = nullptr,
|
||||
CommonChunk *C = nullptr)
|
||||
: DefinedCOFF(DefinedCommonKind, F, N, S), Data(C), Size(Size) {
|
||||
this->IsExternal = true;
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
|
|
@ -185,8 +189,9 @@ public:
|
|||
|
||||
private:
|
||||
friend SymbolTable;
|
||||
uint64_t getSize() { return Sym->Value; }
|
||||
uint64_t getSize() const { return Size; }
|
||||
CommonChunk *Data;
|
||||
uint64_t Size;
|
||||
};
|
||||
|
||||
// Absolute symbols.
|
||||
|
|
@ -340,26 +345,6 @@ private:
|
|||
LocalImportChunk *Data;
|
||||
};
|
||||
|
||||
class DefinedBitcode : public Defined {
|
||||
friend SymbolBody;
|
||||
public:
|
||||
DefinedBitcode(BitcodeFile *F, StringRef N, bool IsReplaceable)
|
||||
: Defined(DefinedBitcodeKind, N), File(F) {
|
||||
// IsReplaceable tracks whether the bitcode symbol may be replaced with some
|
||||
// other (defined, common or bitcode) symbol. This is the case for common,
|
||||
// comdat and weak external symbols. We try to replace bitcode symbols with
|
||||
// "real" symbols (see SymbolTable::add{Regular,Bitcode}), and resolve the
|
||||
// result against the real symbol from the combined LTO object.
|
||||
this->IsReplaceable = IsReplaceable;
|
||||
}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == DefinedBitcodeKind;
|
||||
}
|
||||
|
||||
BitcodeFile *File;
|
||||
};
|
||||
|
||||
inline uint64_t Defined::getRVA() {
|
||||
switch (kind()) {
|
||||
case DefinedAbsoluteKind:
|
||||
|
|
@ -376,8 +361,6 @@ inline uint64_t Defined::getRVA() {
|
|||
return cast<DefinedCommon>(this)->getRVA();
|
||||
case DefinedRegularKind:
|
||||
return cast<DefinedRegular>(this)->getRVA();
|
||||
case DefinedBitcodeKind:
|
||||
llvm_unreachable("There is no address for a bitcode symbol.");
|
||||
case LazyKind:
|
||||
case UndefinedKind:
|
||||
llvm_unreachable("Cannot get the address for an undefined symbol.");
|
||||
|
|
@ -401,10 +384,9 @@ struct Symbol {
|
|||
// This field is used to store the Symbol's SymbolBody. This instantiation of
|
||||
// AlignedCharArrayUnion gives us a struct with a char array field that is
|
||||
// large and aligned enough to store any derived class of SymbolBody.
|
||||
llvm::AlignedCharArrayUnion<DefinedRegular, DefinedCommon, DefinedAbsolute,
|
||||
DefinedRelative, Lazy, Undefined,
|
||||
DefinedImportData, DefinedImportThunk,
|
||||
DefinedLocalImport, DefinedBitcode>
|
||||
llvm::AlignedCharArrayUnion<
|
||||
DefinedRegular, DefinedCommon, DefinedAbsolute, DefinedRelative, Lazy,
|
||||
Undefined, DefinedImportData, DefinedImportThunk, DefinedLocalImport>
|
||||
Body;
|
||||
|
||||
SymbolBody *body() {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "DLL.h"
|
||||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "MapFile.h"
|
||||
#include "Memory.h"
|
||||
#include "PDB.h"
|
||||
#include "SymbolTable.h"
|
||||
|
|
@ -39,7 +40,6 @@ using namespace llvm::support::endian;
|
|||
using namespace lld;
|
||||
using namespace lld::coff;
|
||||
|
||||
static const int PageSize = 4096;
|
||||
static const int SectorSize = 512;
|
||||
static const int DOSStubSize = 64;
|
||||
static const int NumberfOfDataDirectory = 16;
|
||||
|
|
@ -163,51 +163,6 @@ namespace coff {
|
|||
|
||||
void writeResult(SymbolTable *T) { Writer(T).run(); }
|
||||
|
||||
// OutputSection represents a section in an output file. It's a
|
||||
// container of chunks. OutputSection and Chunk are 1:N relationship.
|
||||
// Chunks cannot belong to more than one OutputSections. The writer
|
||||
// creates multiple OutputSections and assign them unique,
|
||||
// non-overlapping file offsets and RVAs.
|
||||
class OutputSection {
|
||||
public:
|
||||
OutputSection(StringRef N) : Name(N), Header({}) {}
|
||||
void setRVA(uint64_t);
|
||||
void setFileOffset(uint64_t);
|
||||
void addChunk(Chunk *C);
|
||||
StringRef getName() { return Name; }
|
||||
std::vector<Chunk *> &getChunks() { return Chunks; }
|
||||
void addPermissions(uint32_t C);
|
||||
void setPermissions(uint32_t C);
|
||||
uint32_t getPermissions() { return Header.Characteristics & PermMask; }
|
||||
uint32_t getCharacteristics() { return Header.Characteristics; }
|
||||
uint64_t getRVA() { return Header.VirtualAddress; }
|
||||
uint64_t getFileOff() { return Header.PointerToRawData; }
|
||||
void writeHeaderTo(uint8_t *Buf);
|
||||
|
||||
// Returns the size of this section in an executable memory image.
|
||||
// This may be smaller than the raw size (the raw size is multiple
|
||||
// of disk sector size, so there may be padding at end), or may be
|
||||
// larger (if that's the case, the loader reserves spaces after end
|
||||
// of raw data).
|
||||
uint64_t getVirtualSize() { return Header.VirtualSize; }
|
||||
|
||||
// Returns the size of the section in the output file.
|
||||
uint64_t getRawSize() { return Header.SizeOfRawData; }
|
||||
|
||||
// Set offset into the string table storing this section name.
|
||||
// Used only when the name is longer than 8 bytes.
|
||||
void setStringTableOff(uint32_t V) { StringTableOff = V; }
|
||||
|
||||
// N.B. The section index is one based.
|
||||
uint32_t SectionIndex = 0;
|
||||
|
||||
private:
|
||||
StringRef Name;
|
||||
coff_section Header;
|
||||
uint32_t StringTableOff = 0;
|
||||
std::vector<Chunk *> Chunks;
|
||||
};
|
||||
|
||||
void OutputSection::setRVA(uint64_t RVA) {
|
||||
Header.VirtualAddress = RVA;
|
||||
for (Chunk *C : Chunks)
|
||||
|
|
@ -303,8 +258,14 @@ void Writer::run() {
|
|||
sortExceptionTable();
|
||||
writeBuildId();
|
||||
|
||||
if (!Config->PDBPath.empty())
|
||||
createPDB(Config->PDBPath, Symtab, SectionTable, BuildId->DI);
|
||||
if (!Config->PDBPath.empty()) {
|
||||
const llvm::codeview::DebugInfo *DI = nullptr;
|
||||
if (Config->DebugTypes & static_cast<unsigned>(coff::DebugType::CV))
|
||||
DI = BuildId->DI;
|
||||
createPDB(Config->PDBPath, Symtab, SectionTable, DI);
|
||||
}
|
||||
|
||||
writeMapFile(OutputSections);
|
||||
|
||||
if (auto EC = Buffer->commit())
|
||||
fatal(EC, "failed to write the output file");
|
||||
|
|
@ -641,6 +602,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
|
|||
PE->SizeOfStackCommit = Config->StackCommit;
|
||||
PE->SizeOfHeapReserve = Config->HeapReserve;
|
||||
PE->SizeOfHeapCommit = Config->HeapCommit;
|
||||
if (Config->AppContainer)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_APPCONTAINER;
|
||||
if (Config->DynamicBase)
|
||||
PE->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
|
||||
if (Config->HighEntropyVA)
|
||||
|
|
@ -830,7 +793,7 @@ void Writer::writeBuildId() {
|
|||
"only PDB 7.0 is supported");
|
||||
assert(sizeof(Res) == sizeof(BuildId->DI->PDB70.Signature) &&
|
||||
"signature size mismatch");
|
||||
memcpy(BuildId->DI->PDB70.Signature, Res,
|
||||
memcpy(BuildId->DI->PDB70.Signature, Res.Bytes.data(),
|
||||
sizeof(codeview::PDB70DebugInfo::Signature));
|
||||
// TODO(compnerd) track the Age
|
||||
BuildId->DI->PDB70.Age = 1;
|
||||
|
|
|
|||
|
|
@ -10,14 +10,65 @@
|
|||
#ifndef LLD_COFF_WRITER_H
|
||||
#define LLD_COFF_WRITER_H
|
||||
|
||||
#include "Chunks.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Object/COFF.h"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
class SymbolTable;
|
||||
|
||||
static const int PageSize = 4096;
|
||||
|
||||
void writeResult(SymbolTable *T);
|
||||
|
||||
// OutputSection represents a section in an output file. It's a
|
||||
// container of chunks. OutputSection and Chunk are 1:N relationship.
|
||||
// Chunks cannot belong to more than one OutputSections. The writer
|
||||
// creates multiple OutputSections and assign them unique,
|
||||
// non-overlapping file offsets and RVAs.
|
||||
class OutputSection {
|
||||
public:
|
||||
OutputSection(llvm::StringRef N) : Name(N), Header({}) {}
|
||||
void setRVA(uint64_t);
|
||||
void setFileOffset(uint64_t);
|
||||
void addChunk(Chunk *C);
|
||||
llvm::StringRef getName() { return Name; }
|
||||
std::vector<Chunk *> &getChunks() { return Chunks; }
|
||||
void addPermissions(uint32_t C);
|
||||
void setPermissions(uint32_t C);
|
||||
uint32_t getPermissions() { return Header.Characteristics & PermMask; }
|
||||
uint32_t getCharacteristics() { return Header.Characteristics; }
|
||||
uint64_t getRVA() { return Header.VirtualAddress; }
|
||||
uint64_t getFileOff() { return Header.PointerToRawData; }
|
||||
void writeHeaderTo(uint8_t *Buf);
|
||||
|
||||
// Returns the size of this section in an executable memory image.
|
||||
// This may be smaller than the raw size (the raw size is multiple
|
||||
// of disk sector size, so there may be padding at end), or may be
|
||||
// larger (if that's the case, the loader reserves spaces after end
|
||||
// of raw data).
|
||||
uint64_t getVirtualSize() { return Header.VirtualSize; }
|
||||
|
||||
// Returns the size of the section in the output file.
|
||||
uint64_t getRawSize() { return Header.SizeOfRawData; }
|
||||
|
||||
// Set offset into the string table storing this section name.
|
||||
// Used only when the name is longer than 8 bytes.
|
||||
void setStringTableOff(uint32_t V) { StringTableOff = V; }
|
||||
|
||||
// N.B. The section index is one based.
|
||||
uint32_t SectionIndex = 0;
|
||||
|
||||
private:
|
||||
llvm::StringRef Name;
|
||||
llvm::object::coff_section Header;
|
||||
uint32_t StringTableOff = 0;
|
||||
std::vector<Chunk *> Chunks;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,16 +11,19 @@ add_lld_library(lldELF
|
|||
DriverUtils.cpp
|
||||
EhFrame.cpp
|
||||
Error.cpp
|
||||
Filesystem.cpp
|
||||
GdbIndex.cpp
|
||||
ICF.cpp
|
||||
InputFiles.cpp
|
||||
InputSection.cpp
|
||||
LTO.cpp
|
||||
LinkerScript.cpp
|
||||
MapFile.cpp
|
||||
MarkLive.cpp
|
||||
Mips.cpp
|
||||
OutputSections.cpp
|
||||
Relocations.cpp
|
||||
ScriptLexer.cpp
|
||||
ScriptParser.cpp
|
||||
Strings.cpp
|
||||
SymbolTable.cpp
|
||||
|
|
@ -53,7 +56,7 @@ add_lld_library(lldELF
|
|||
LINK_LIBS
|
||||
lldConfig
|
||||
lldCore
|
||||
${PTHREAD_LIB}
|
||||
${LLVM_PTHREAD_LIB}
|
||||
|
||||
DEPENDS
|
||||
ELFOptionsTableGen
|
||||
|
|
|
|||
90
ELF/Config.h
90
ELF/Config.h
|
|
@ -13,7 +13,10 @@
|
|||
#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/Support/CachePruning.h"
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Support/ELF.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -34,14 +37,14 @@ enum ELFKind {
|
|||
// For --build-id.
|
||||
enum class BuildIdKind { None, Fast, Md5, Sha1, Hexstring, Uuid };
|
||||
|
||||
// For --discard-{all,locals,none} and --retain-symbols-file.
|
||||
enum class DiscardPolicy { Default, All, Locals, RetainFile, None };
|
||||
// For --discard-{all,locals,none}.
|
||||
enum class DiscardPolicy { Default, All, Locals, None };
|
||||
|
||||
// For --strip-{all,debug}.
|
||||
enum class StripPolicy { None, All, Debug };
|
||||
|
||||
// For --unresolved-symbols.
|
||||
enum class UnresolvedPolicy { NoUndef, ReportError, Warn, Ignore };
|
||||
enum class UnresolvedPolicy { ReportError, Warn, WarnAll, Ignore, IgnoreAll };
|
||||
|
||||
// For --sort-section and linkerscript sorting rules.
|
||||
enum class SortSectionPolicy { Default, None, Alignment, Name, Priority };
|
||||
|
|
@ -58,11 +61,10 @@ struct SymbolVersion {
|
|||
// This struct contains symbols version definition that
|
||||
// can be found in version script if it is used for link.
|
||||
struct VersionDefinition {
|
||||
VersionDefinition(llvm::StringRef Name, uint16_t Id) : Name(Name), Id(Id) {}
|
||||
llvm::StringRef Name;
|
||||
uint16_t Id;
|
||||
uint16_t Id = 0;
|
||||
std::vector<SymbolVersion> Globals;
|
||||
size_t NameOff; // Offset in string table.
|
||||
size_t NameOff = 0; // Offset in the string table
|
||||
};
|
||||
|
||||
// This struct contains the global configuration for the linker.
|
||||
|
|
@ -72,6 +74,7 @@ struct VersionDefinition {
|
|||
struct Configuration {
|
||||
InputFile *FirstElf = nullptr;
|
||||
uint8_t OSABI = 0;
|
||||
llvm::CachePruningPolicy ThinLTOCachePolicy;
|
||||
llvm::StringMap<uint64_t> SectionStartMap;
|
||||
llvm::StringRef DynamicLinker;
|
||||
llvm::StringRef Entry;
|
||||
|
|
@ -80,10 +83,12 @@ struct Configuration {
|
|||
llvm::StringRef Init;
|
||||
llvm::StringRef LTOAAPipeline;
|
||||
llvm::StringRef LTONewPmPasses;
|
||||
llvm::StringRef MapFile;
|
||||
llvm::StringRef OutputFile;
|
||||
llvm::StringRef OptRemarksFilename;
|
||||
llvm::StringRef SoName;
|
||||
llvm::StringRef Sysroot;
|
||||
llvm::StringSet<> RetainSymbolsFile;
|
||||
llvm::StringRef ThinLTOCacheDir;
|
||||
std::string RPath;
|
||||
std::vector<VersionDefinition> VersionDefinitions;
|
||||
std::vector<llvm::StringRef> AuxiliaryList;
|
||||
|
|
@ -94,6 +99,7 @@ struct Configuration {
|
|||
std::vector<SymbolVersion> VersionScriptLocals;
|
||||
std::vector<uint8_t> BuildIdVector;
|
||||
bool AllowMultipleDefinition;
|
||||
bool ArchiveWithoutSymbolsSeen = false;
|
||||
bool AsNeeded = false;
|
||||
bool Bsymbolic;
|
||||
bool BsymbolicFunctions;
|
||||
|
|
@ -102,30 +108,29 @@ struct Configuration {
|
|||
bool Demangle = true;
|
||||
bool DisableVerify;
|
||||
bool EhFrameHdr;
|
||||
bool EmitRelocs;
|
||||
bool EnableNewDtags;
|
||||
bool ExportDynamic;
|
||||
bool FatalWarnings;
|
||||
bool GcSections;
|
||||
bool GdbIndex;
|
||||
bool GnuHash = false;
|
||||
bool GnuHash;
|
||||
bool ICF;
|
||||
bool Mips64EL = false;
|
||||
bool MipsN32Abi = false;
|
||||
bool NoGnuUnique;
|
||||
bool NoUndefinedVersion;
|
||||
bool Nostdlib;
|
||||
bool OFormatBinary;
|
||||
bool OMagic;
|
||||
bool Pic;
|
||||
bool Omagic;
|
||||
bool OptRemarksWithHotness;
|
||||
bool Pie;
|
||||
bool PrintGcSections;
|
||||
bool Rela;
|
||||
bool Relocatable;
|
||||
bool SaveTemps;
|
||||
bool SingleRoRx;
|
||||
bool Shared;
|
||||
bool Static = false;
|
||||
bool SysvHash = true;
|
||||
bool SysvHash;
|
||||
bool Target1Rel;
|
||||
bool Threads;
|
||||
bool Trace;
|
||||
|
|
@ -134,17 +139,20 @@ struct Configuration {
|
|||
bool WarnMissingEntry;
|
||||
bool ZCombreloc;
|
||||
bool ZExecstack;
|
||||
bool ZNocopyreloc;
|
||||
bool ZNodelete;
|
||||
bool ZNodlopen;
|
||||
bool ZNow;
|
||||
bool ZOrigin;
|
||||
bool ZRelro;
|
||||
bool ZText;
|
||||
bool ExitEarly;
|
||||
bool ZWxneeded;
|
||||
DiscardPolicy Discard;
|
||||
SortSectionPolicy SortSection;
|
||||
StripPolicy Strip = StripPolicy::None;
|
||||
StripPolicy Strip;
|
||||
UnresolvedPolicy UnresolvedSymbols;
|
||||
Target2Policy Target2 = Target2Policy::GotRel;
|
||||
Target2Policy Target2;
|
||||
BuildIdKind BuildId = BuildIdKind::None;
|
||||
ELFKind EKind = ELFNoneKind;
|
||||
uint16_t DefaultSymbolVersion = llvm::ELF::VER_NDX_GLOBAL;
|
||||
|
|
@ -157,6 +165,58 @@ struct Configuration {
|
|||
unsigned LTOO;
|
||||
unsigned Optimize;
|
||||
unsigned ThinLTOJobs;
|
||||
|
||||
// The following config options do not directly correspond to any
|
||||
// particualr command line options.
|
||||
|
||||
// True if we need to pass through relocations in input files to the
|
||||
// output file. Usually false because we consume relocations.
|
||||
bool CopyRelocs;
|
||||
|
||||
// True if the target is ELF64. False if ELF32.
|
||||
bool Is64;
|
||||
|
||||
// True if the target is little-endian. False if big-endian.
|
||||
bool IsLE;
|
||||
|
||||
// endianness::little if IsLE is true. endianness::big otherwise.
|
||||
llvm::support::endianness Endianness;
|
||||
|
||||
// True if the target is the little-endian MIPS64.
|
||||
//
|
||||
// The reason why we have this variable only for the MIPS is because
|
||||
// we use this often. Some ELF headers for MIPS64EL are in a
|
||||
// mixed-endian (which is horrible and I'd say that's a serious spec
|
||||
// bug), and we need to know whether we are reading MIPS ELF files or
|
||||
// not in various places.
|
||||
//
|
||||
// (Note that MIPS64EL is not a typo for MIPS64LE. This is the official
|
||||
// name whatever that means. A fun hypothesis is that "EL" is short for
|
||||
// little-endian written in the little-endian order, but I don't know
|
||||
// if that's true.)
|
||||
bool IsMips64EL;
|
||||
|
||||
// The ELF spec defines two types of relocation table entries, RELA and
|
||||
// REL. RELA is a triplet of (offset, info, addend) while REL is a
|
||||
// tuple of (offset, info). Addends for REL are implicit and read from
|
||||
// the location where the relocations are applied. So, REL is more
|
||||
// compact than RELA but requires a bit of more work to process.
|
||||
//
|
||||
// (From the linker writer's view, this distinction is not necessary.
|
||||
// If the ELF had chosen whichever and sticked with it, it would have
|
||||
// been easier to write code to process relocations, but it's too late
|
||||
// to change the spec.)
|
||||
//
|
||||
// Each ABI defines its relocation type. IsRela is true if target
|
||||
// uses RELA. As far as we know, all 64-bit ABIs are using RELA. A
|
||||
// few 32-bit ABIs are using RELA too.
|
||||
bool IsRela;
|
||||
|
||||
// True if we are creating position-independent code.
|
||||
bool Pic;
|
||||
|
||||
// 4 for ELF32, 8 for ELF64.
|
||||
int Wordsize;
|
||||
};
|
||||
|
||||
// The only instance of Configuration struct.
|
||||
|
|
|
|||
467
ELF/Driver.cpp
467
ELF/Driver.cpp
|
|
@ -6,15 +6,34 @@
|
|||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// The driver drives the entire linking process. It is responsible for
|
||||
// parsing command line options and doing whatever it is instructed to do.
|
||||
//
|
||||
// One notable thing in the LLD's driver when compared to other linkers is
|
||||
// that the LLD's driver is agnostic on the host operating system.
|
||||
// Other linkers usually have implicit default values (such as a dynamic
|
||||
// linker path or library paths) for each host OS.
|
||||
//
|
||||
// I don't think implicit default values are useful because they are
|
||||
// usually explicitly specified by the compiler driver. They can even
|
||||
// be harmful when you are doing cross-linking. Therefore, in LLD, we
|
||||
// simply trust the compiler driver to pass all required options and
|
||||
// don't try to make effort on our side.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Driver.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "Filesystem.h"
|
||||
#include "ICF.h"
|
||||
#include "InputFiles.h"
|
||||
#include "InputSection.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "ScriptParser.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Target.h"
|
||||
|
|
@ -48,16 +67,19 @@ BumpPtrAllocator elf::BAlloc;
|
|||
StringSaver elf::Saver{BAlloc};
|
||||
std::vector<SpecificAllocBase *> elf::SpecificAllocBase::Instances;
|
||||
|
||||
static void setConfigs();
|
||||
|
||||
bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
|
||||
raw_ostream &Error) {
|
||||
ErrorCount = 0;
|
||||
ErrorOS = &Error;
|
||||
Argv0 = Args[0];
|
||||
InputSections.clear();
|
||||
Tar = nullptr;
|
||||
|
||||
Config = make<Configuration>();
|
||||
Driver = make<LinkerDriver>();
|
||||
ScriptConfig = make<ScriptConfiguration>();
|
||||
Script = make<LinkerScript>();
|
||||
|
||||
Driver->main(Args, CanExitEarly);
|
||||
freeArena();
|
||||
|
|
@ -78,10 +100,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
|
|||
.Cases("aarch64elf", "aarch64linux", {ELF64LEKind, EM_AARCH64})
|
||||
.Case("armelf_linux_eabi", {ELF32LEKind, EM_ARM})
|
||||
.Case("elf32_x86_64", {ELF32LEKind, EM_X86_64})
|
||||
.Case("elf32btsmip", {ELF32BEKind, EM_MIPS})
|
||||
.Case("elf32ltsmip", {ELF32LEKind, EM_MIPS})
|
||||
.Case("elf32btsmipn32", {ELF32BEKind, EM_MIPS})
|
||||
.Case("elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
|
||||
.Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS})
|
||||
.Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
|
||||
.Case("elf32ppc", {ELF32BEKind, EM_PPC})
|
||||
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
|
||||
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
|
||||
|
|
@ -133,7 +153,7 @@ LinkerDriver::getArchiveMembers(MemoryBufferRef MB) {
|
|||
|
||||
// Opens and parses a file. Path has to be resolved already.
|
||||
// Newly created memory buffers are owned by this driver.
|
||||
void LinkerDriver::addFile(StringRef Path) {
|
||||
void LinkerDriver::addFile(StringRef Path, bool WithLOption) {
|
||||
using namespace sys::fs;
|
||||
|
||||
Optional<MemoryBufferRef> Buffer = readFile(Path);
|
||||
|
|
@ -164,6 +184,19 @@ void LinkerDriver::addFile(StringRef Path) {
|
|||
return;
|
||||
}
|
||||
Files.push_back(createSharedFile(MBRef));
|
||||
|
||||
// DSOs usually have DT_SONAME tags in their ELF headers, and the
|
||||
// sonames are used to identify DSOs. But if they are missing,
|
||||
// they are identified by filenames. We don't know whether the new
|
||||
// file has a DT_SONAME or not because we haven't parsed it yet.
|
||||
// Here, we set the default soname for the file because we might
|
||||
// need it later.
|
||||
//
|
||||
// If a file was specified by -lfoo, the directory part is not
|
||||
// significant, as a user did not specify it. This behavior is
|
||||
// compatible with GNU.
|
||||
Files.back()->DefaultSoName =
|
||||
WithLOption ? sys::path::filename(Path) : Path;
|
||||
return;
|
||||
default:
|
||||
if (InLib)
|
||||
|
|
@ -176,7 +209,7 @@ void LinkerDriver::addFile(StringRef Path) {
|
|||
// Add a given library by searching it from input search paths.
|
||||
void LinkerDriver::addLibrary(StringRef Name) {
|
||||
if (Optional<std::string> Path = searchLibrary(Name))
|
||||
addFile(*Path);
|
||||
addFile(*Path, /*WithLOption=*/true);
|
||||
else
|
||||
error("unable to find library -l" + Name);
|
||||
}
|
||||
|
|
@ -281,11 +314,27 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
|
|||
return;
|
||||
}
|
||||
|
||||
// GNU linkers disagree here. Though both -version and -v are mentioned
|
||||
// in help to print the version information, GNU ld just normally exits,
|
||||
// while gold can continue linking. We are compatible with ld.bfd here.
|
||||
if (Args.hasArg(OPT_version) || Args.hasArg(OPT_v))
|
||||
outs() << getLLDVersion() << "\n";
|
||||
// Handle -v or -version.
|
||||
//
|
||||
// A note about "compatible with GNU linkers" message: this is a hack for
|
||||
// scripts generated by GNU Libtool 2.4.6 (released in February 2014 and
|
||||
// still the newest version in March 2017) or earlier to recognize LLD as
|
||||
// a GNU compatible linker. As long as an output for the -v option
|
||||
// contains "GNU" or "with BFD", they recognize us as GNU-compatible.
|
||||
//
|
||||
// This is somewhat ugly hack, but in reality, we had no choice other
|
||||
// than doing this. Considering the very long release cycle of Libtool,
|
||||
// it is not easy to improve it to recognize LLD as a GNU compatible
|
||||
// linker in a timely manner. Even if we can make it, there are still a
|
||||
// lot of "configure" scripts out there that are generated by old version
|
||||
// of Libtool. We cannot convince every software developer to migrate to
|
||||
// the latest version and re-generate scripts. So we have this hack.
|
||||
if (Args.hasArg(OPT_v) || Args.hasArg(OPT_version))
|
||||
message(getLLDVersion() + " (compatible with GNU linkers)");
|
||||
|
||||
// ld.bfd always exits after printing out the version string.
|
||||
// ld.gold proceeds if a given option is -v. Because gold's behavior
|
||||
// is more permissive than ld.bfd, we chose what gold does here.
|
||||
if (Args.hasArg(OPT_version))
|
||||
return;
|
||||
|
||||
|
|
@ -311,6 +360,7 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
|
|||
initLLVM(Args);
|
||||
createFiles(Args);
|
||||
inferMachineType();
|
||||
setConfigs();
|
||||
checkOptions(Args);
|
||||
if (ErrorCount)
|
||||
return;
|
||||
|
|
@ -333,26 +383,68 @@ void LinkerDriver::main(ArrayRef<const char *> ArgsArr, bool CanExitEarly) {
|
|||
}
|
||||
}
|
||||
|
||||
static UnresolvedPolicy getUnresolvedSymbolOption(opt::InputArgList &Args) {
|
||||
if (Args.hasArg(OPT_noinhibit_exec))
|
||||
return UnresolvedPolicy::Warn;
|
||||
if (Args.hasArg(OPT_no_undefined) || hasZOption(Args, "defs"))
|
||||
return UnresolvedPolicy::NoUndef;
|
||||
if (Config->Relocatable)
|
||||
return UnresolvedPolicy::Ignore;
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_unresolved_symbols)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "ignore-all" || S == "ignore-in-object-files")
|
||||
return UnresolvedPolicy::Ignore;
|
||||
if (S == "ignore-in-shared-libs" || S == "report-all")
|
||||
return UnresolvedPolicy::ReportError;
|
||||
error("unknown --unresolved-symbols value: " + S);
|
||||
}
|
||||
return UnresolvedPolicy::ReportError;
|
||||
static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2,
|
||||
bool Default) {
|
||||
if (auto *Arg = Args.getLastArg(K1, K2))
|
||||
return Arg->getOption().getID() == K1;
|
||||
return Default;
|
||||
}
|
||||
|
||||
static Target2Policy getTarget2Option(opt::InputArgList &Args) {
|
||||
static std::vector<StringRef> getArgs(opt::InputArgList &Args, int Id) {
|
||||
std::vector<StringRef> V;
|
||||
for (auto *Arg : Args.filtered(Id))
|
||||
V.push_back(Arg->getValue());
|
||||
return V;
|
||||
}
|
||||
|
||||
static std::string getRPath(opt::InputArgList &Args) {
|
||||
std::vector<StringRef> V = getArgs(Args, OPT_rpath);
|
||||
return llvm::join(V.begin(), V.end(), ":");
|
||||
}
|
||||
|
||||
// Determines what we should do if there are remaining unresolved
|
||||
// symbols after the name resolution.
|
||||
static UnresolvedPolicy getUnresolvedSymbolPolicy(opt::InputArgList &Args) {
|
||||
// -noinhibit-exec or -r imply some default values.
|
||||
if (Args.hasArg(OPT_noinhibit_exec))
|
||||
return UnresolvedPolicy::WarnAll;
|
||||
if (Args.hasArg(OPT_relocatable))
|
||||
return UnresolvedPolicy::IgnoreAll;
|
||||
|
||||
UnresolvedPolicy ErrorOrWarn = getArg(Args, OPT_error_unresolved_symbols,
|
||||
OPT_warn_unresolved_symbols, true)
|
||||
? UnresolvedPolicy::ReportError
|
||||
: UnresolvedPolicy::Warn;
|
||||
|
||||
// Process the last of -unresolved-symbols, -no-undefined or -z defs.
|
||||
for (auto *Arg : llvm::reverse(Args)) {
|
||||
switch (Arg->getOption().getID()) {
|
||||
case OPT_unresolved_symbols: {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "ignore-all" || S == "ignore-in-object-files")
|
||||
return UnresolvedPolicy::Ignore;
|
||||
if (S == "ignore-in-shared-libs" || S == "report-all")
|
||||
return ErrorOrWarn;
|
||||
error("unknown --unresolved-symbols value: " + S);
|
||||
continue;
|
||||
}
|
||||
case OPT_no_undefined:
|
||||
return ErrorOrWarn;
|
||||
case OPT_z:
|
||||
if (StringRef(Arg->getValue()) == "defs")
|
||||
return ErrorOrWarn;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// -shared implies -unresolved-symbols=ignore-all because missing
|
||||
// symbols are likely to be resolved at runtime using other DSOs.
|
||||
if (Config->Shared)
|
||||
return UnresolvedPolicy::Ignore;
|
||||
return ErrorOrWarn;
|
||||
}
|
||||
|
||||
static Target2Policy getTarget2(opt::InputArgList &Args) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_target2)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "rel")
|
||||
|
|
@ -376,16 +468,10 @@ static bool isOutputFormatBinary(opt::InputArgList &Args) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool getArg(opt::InputArgList &Args, unsigned K1, unsigned K2,
|
||||
bool Default) {
|
||||
if (auto *Arg = Args.getLastArg(K1, K2))
|
||||
return Arg->getOption().getID() == K1;
|
||||
return Default;
|
||||
}
|
||||
|
||||
static DiscardPolicy getDiscardOption(opt::InputArgList &Args) {
|
||||
if (Config->Relocatable)
|
||||
static DiscardPolicy getDiscard(opt::InputArgList &Args) {
|
||||
if (Args.hasArg(OPT_relocatable))
|
||||
return DiscardPolicy::None;
|
||||
|
||||
auto *Arg =
|
||||
Args.getLastArg(OPT_discard_all, OPT_discard_locals, OPT_discard_none);
|
||||
if (!Arg)
|
||||
|
|
@ -397,13 +483,23 @@ static DiscardPolicy getDiscardOption(opt::InputArgList &Args) {
|
|||
return DiscardPolicy::None;
|
||||
}
|
||||
|
||||
static StripPolicy getStripOption(opt::InputArgList &Args) {
|
||||
if (auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug)) {
|
||||
if (Arg->getOption().getID() == OPT_strip_all)
|
||||
return StripPolicy::All;
|
||||
return StripPolicy::Debug;
|
||||
}
|
||||
return StripPolicy::None;
|
||||
static StringRef getDynamicLinker(opt::InputArgList &Args) {
|
||||
auto *Arg = Args.getLastArg(OPT_dynamic_linker, OPT_no_dynamic_linker);
|
||||
if (!Arg || Arg->getOption().getID() == OPT_no_dynamic_linker)
|
||||
return "";
|
||||
return Arg->getValue();
|
||||
}
|
||||
|
||||
static StripPolicy getStrip(opt::InputArgList &Args) {
|
||||
if (Args.hasArg(OPT_relocatable))
|
||||
return StripPolicy::None;
|
||||
|
||||
auto *Arg = Args.getLastArg(OPT_strip_all, OPT_strip_debug);
|
||||
if (!Arg)
|
||||
return StripPolicy::None;
|
||||
if (Arg->getOption().getID() == OPT_strip_all)
|
||||
return StripPolicy::All;
|
||||
return StripPolicy::Debug;
|
||||
}
|
||||
|
||||
static uint64_t parseSectionAddress(StringRef S, opt::Arg *Arg) {
|
||||
|
|
@ -433,7 +529,7 @@ static StringMap<uint64_t> getSectionStartMap(opt::InputArgList &Args) {
|
|||
return Ret;
|
||||
}
|
||||
|
||||
static SortSectionPolicy getSortKind(opt::InputArgList &Args) {
|
||||
static SortSectionPolicy getSortSection(opt::InputArgList &Args) {
|
||||
StringRef S = getString(Args, OPT_sort_section);
|
||||
if (S == "alignment")
|
||||
return SortSectionPolicy::Alignment;
|
||||
|
|
@ -444,6 +540,17 @@ static SortSectionPolicy getSortKind(opt::InputArgList &Args) {
|
|||
return SortSectionPolicy::Default;
|
||||
}
|
||||
|
||||
static std::pair<bool, bool> getHashStyle(opt::InputArgList &Args) {
|
||||
StringRef S = getString(Args, OPT_hash_style, "sysv");
|
||||
if (S == "sysv")
|
||||
return {true, false};
|
||||
if (S == "gnu")
|
||||
return {false, true};
|
||||
if (S != "both")
|
||||
error("unknown -hash-style: " + S);
|
||||
return {true, true};
|
||||
}
|
||||
|
||||
static std::vector<StringRef> getLines(MemoryBufferRef MB) {
|
||||
SmallVector<StringRef, 0> Arr;
|
||||
MB.getBuffer().split(Arr, '\n');
|
||||
|
|
@ -459,14 +566,87 @@ static std::vector<StringRef> getLines(MemoryBufferRef MB) {
|
|||
|
||||
// Initializes Config members by the command line options.
|
||||
void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
||||
for (auto *Arg : Args.filtered(OPT_L))
|
||||
Config->SearchPaths.push_back(Arg->getValue());
|
||||
Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition);
|
||||
Config->AuxiliaryList = getArgs(Args, OPT_auxiliary);
|
||||
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
|
||||
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
|
||||
Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common,
|
||||
!Args.hasArg(OPT_relocatable));
|
||||
Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true);
|
||||
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
|
||||
Config->Discard = getDiscard(Args);
|
||||
Config->DynamicLinker = getDynamicLinker(Args);
|
||||
Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr);
|
||||
Config->EmitRelocs = Args.hasArg(OPT_emit_relocs);
|
||||
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
|
||||
Config->Entry = getString(Args, OPT_entry);
|
||||
Config->ExportDynamic =
|
||||
getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false);
|
||||
Config->FatalWarnings =
|
||||
getArg(Args, OPT_fatal_warnings, OPT_no_fatal_warnings, false);
|
||||
Config->Fini = getString(Args, OPT_fini, "_fini");
|
||||
Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false);
|
||||
Config->GdbIndex = Args.hasArg(OPT_gdb_index);
|
||||
Config->ICF = Args.hasArg(OPT_icf);
|
||||
Config->Init = getString(Args, OPT_init, "_init");
|
||||
Config->LTOAAPipeline = getString(Args, OPT_lto_aa_pipeline);
|
||||
Config->LTONewPmPasses = getString(Args, OPT_lto_newpm_passes);
|
||||
Config->LTOO = getInteger(Args, OPT_lto_O, 2);
|
||||
Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1);
|
||||
Config->MapFile = getString(Args, OPT_Map);
|
||||
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
|
||||
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
|
||||
Config->Nostdlib = Args.hasArg(OPT_nostdlib);
|
||||
Config->OFormatBinary = isOutputFormatBinary(Args);
|
||||
Config->Omagic = Args.hasArg(OPT_omagic);
|
||||
Config->OptRemarksFilename = getString(Args, OPT_opt_remarks_filename);
|
||||
Config->OptRemarksWithHotness = Args.hasArg(OPT_opt_remarks_with_hotness);
|
||||
Config->Optimize = getInteger(Args, OPT_O, 1);
|
||||
Config->OutputFile = getString(Args, OPT_o);
|
||||
Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false);
|
||||
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
|
||||
Config->RPath = getRPath(Args);
|
||||
Config->Relocatable = Args.hasArg(OPT_relocatable);
|
||||
Config->SaveTemps = Args.hasArg(OPT_save_temps);
|
||||
Config->SearchPaths = getArgs(Args, OPT_L);
|
||||
Config->SectionStartMap = getSectionStartMap(Args);
|
||||
Config->Shared = Args.hasArg(OPT_shared);
|
||||
Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
|
||||
Config->SoName = getString(Args, OPT_soname);
|
||||
Config->SortSection = getSortSection(Args);
|
||||
Config->Strip = getStrip(Args);
|
||||
Config->Sysroot = getString(Args, OPT_sysroot);
|
||||
Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false);
|
||||
Config->Target2 = getTarget2(Args);
|
||||
Config->ThinLTOCacheDir = getString(Args, OPT_thinlto_cache_dir);
|
||||
Config->ThinLTOCachePolicy =
|
||||
check(parseCachePruningPolicy(getString(Args, OPT_thinlto_cache_policy)),
|
||||
"--thinlto-cache-policy: invalid cache policy");
|
||||
Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u);
|
||||
Config->Threads = getArg(Args, OPT_threads, OPT_no_threads, true);
|
||||
Config->Trace = Args.hasArg(OPT_trace);
|
||||
Config->Undefined = getArgs(Args, OPT_undefined);
|
||||
Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args);
|
||||
Config->Verbose = Args.hasArg(OPT_verbose);
|
||||
Config->WarnCommon = Args.hasArg(OPT_warn_common);
|
||||
Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
|
||||
Config->ZExecstack = hasZOption(Args, "execstack");
|
||||
Config->ZNocopyreloc = hasZOption(Args, "nocopyreloc");
|
||||
Config->ZNodelete = hasZOption(Args, "nodelete");
|
||||
Config->ZNodlopen = hasZOption(Args, "nodlopen");
|
||||
Config->ZNow = hasZOption(Args, "now");
|
||||
Config->ZOrigin = hasZOption(Args, "origin");
|
||||
Config->ZRelro = !hasZOption(Args, "norelro");
|
||||
Config->ZStackSize = getZOptionValue(Args, "stack-size", 0);
|
||||
Config->ZText = !hasZOption(Args, "notext");
|
||||
Config->ZWxneeded = hasZOption(Args, "wxneeded");
|
||||
|
||||
std::vector<StringRef> RPaths;
|
||||
for (auto *Arg : Args.filtered(OPT_rpath))
|
||||
RPaths.push_back(Arg->getValue());
|
||||
if (!RPaths.empty())
|
||||
Config->RPath = llvm::join(RPaths.begin(), RPaths.end(), ":");
|
||||
if (Config->LTOO > 3)
|
||||
error("invalid optimization level for LTO: " + getString(Args, OPT_lto_O));
|
||||
if (Config->LTOPartitions == 0)
|
||||
error("--lto-partitions: number of threads must be > 0");
|
||||
if (Config->ThinLTOJobs == 0)
|
||||
error("--thinlto-jobs: number of threads must be > 0");
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_m)) {
|
||||
// Parse ELF{32,64}{LE,BE} and CPU type.
|
||||
|
|
@ -477,98 +657,21 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
|||
Config->Emulation = S;
|
||||
}
|
||||
|
||||
Config->AllowMultipleDefinition = Args.hasArg(OPT_allow_multiple_definition);
|
||||
Config->Bsymbolic = Args.hasArg(OPT_Bsymbolic);
|
||||
Config->BsymbolicFunctions = Args.hasArg(OPT_Bsymbolic_functions);
|
||||
Config->Demangle = getArg(Args, OPT_demangle, OPT_no_demangle, true);
|
||||
Config->DisableVerify = Args.hasArg(OPT_disable_verify);
|
||||
Config->EhFrameHdr = Args.hasArg(OPT_eh_frame_hdr);
|
||||
Config->EnableNewDtags = !Args.hasArg(OPT_disable_new_dtags);
|
||||
Config->ExportDynamic = Args.hasArg(OPT_export_dynamic);
|
||||
Config->FatalWarnings = Args.hasArg(OPT_fatal_warnings);
|
||||
Config->GcSections = getArg(Args, OPT_gc_sections, OPT_no_gc_sections, false);
|
||||
Config->GdbIndex = Args.hasArg(OPT_gdb_index);
|
||||
Config->ICF = Args.hasArg(OPT_icf);
|
||||
Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
|
||||
Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
|
||||
Config->Nostdlib = Args.hasArg(OPT_nostdlib);
|
||||
Config->OMagic = Args.hasArg(OPT_omagic);
|
||||
Config->Pie = getArg(Args, OPT_pie, OPT_nopie, false);
|
||||
Config->PrintGcSections = Args.hasArg(OPT_print_gc_sections);
|
||||
Config->Relocatable = Args.hasArg(OPT_relocatable);
|
||||
Config->DefineCommon = getArg(Args, OPT_define_common, OPT_no_define_common,
|
||||
!Config->Relocatable);
|
||||
Config->Discard = getDiscardOption(Args);
|
||||
Config->SaveTemps = Args.hasArg(OPT_save_temps);
|
||||
Config->SingleRoRx = Args.hasArg(OPT_no_rosegment);
|
||||
Config->Shared = Args.hasArg(OPT_shared);
|
||||
Config->Target1Rel = getArg(Args, OPT_target1_rel, OPT_target1_abs, false);
|
||||
Config->Threads = getArg(Args, OPT_threads, OPT_no_threads, true);
|
||||
Config->Trace = Args.hasArg(OPT_trace);
|
||||
Config->Verbose = Args.hasArg(OPT_verbose);
|
||||
Config->WarnCommon = Args.hasArg(OPT_warn_common);
|
||||
|
||||
Config->DynamicLinker = getString(Args, OPT_dynamic_linker);
|
||||
Config->Entry = getString(Args, OPT_entry);
|
||||
Config->Fini = getString(Args, OPT_fini, "_fini");
|
||||
Config->Init = getString(Args, OPT_init, "_init");
|
||||
Config->LTOAAPipeline = getString(Args, OPT_lto_aa_pipeline);
|
||||
Config->LTONewPmPasses = getString(Args, OPT_lto_newpm_passes);
|
||||
Config->OutputFile = getString(Args, OPT_o);
|
||||
Config->SoName = getString(Args, OPT_soname);
|
||||
Config->Sysroot = getString(Args, OPT_sysroot);
|
||||
|
||||
Config->Optimize = getInteger(Args, OPT_O, 1);
|
||||
Config->LTOO = getInteger(Args, OPT_lto_O, 2);
|
||||
if (Config->LTOO > 3)
|
||||
error("invalid optimization level for LTO: " + getString(Args, OPT_lto_O));
|
||||
Config->LTOPartitions = getInteger(Args, OPT_lto_partitions, 1);
|
||||
if (Config->LTOPartitions == 0)
|
||||
error("--lto-partitions: number of threads must be > 0");
|
||||
Config->ThinLTOJobs = getInteger(Args, OPT_thinlto_jobs, -1u);
|
||||
if (Config->ThinLTOJobs == 0)
|
||||
error("--thinlto-jobs: number of threads must be > 0");
|
||||
|
||||
Config->ZCombreloc = !hasZOption(Args, "nocombreloc");
|
||||
Config->ZExecstack = hasZOption(Args, "execstack");
|
||||
Config->ZNodelete = hasZOption(Args, "nodelete");
|
||||
Config->ZNow = hasZOption(Args, "now");
|
||||
Config->ZOrigin = hasZOption(Args, "origin");
|
||||
Config->ZRelro = !hasZOption(Args, "norelro");
|
||||
Config->ZStackSize = getZOptionValue(Args, "stack-size", -1);
|
||||
Config->ZWxneeded = hasZOption(Args, "wxneeded");
|
||||
|
||||
Config->OFormatBinary = isOutputFormatBinary(Args);
|
||||
Config->SectionStartMap = getSectionStartMap(Args);
|
||||
Config->SortSection = getSortKind(Args);
|
||||
Config->Target2 = getTarget2Option(Args);
|
||||
Config->UnresolvedSymbols = getUnresolvedSymbolOption(Args);
|
||||
if (Args.hasArg(OPT_print_map))
|
||||
Config->MapFile = "-";
|
||||
|
||||
// --omagic is an option to create old-fashioned executables in which
|
||||
// .text segments are writable. Today, the option is still in use to
|
||||
// create special-purpose programs such as boot loaders. It doesn't
|
||||
// make sense to create PT_GNU_RELRO for such executables.
|
||||
if (Config->OMagic)
|
||||
if (Config->Omagic)
|
||||
Config->ZRelro = false;
|
||||
|
||||
if (!Config->Relocatable)
|
||||
Config->Strip = getStripOption(Args);
|
||||
std::tie(Config->SysvHash, Config->GnuHash) = getHashStyle(Args);
|
||||
|
||||
// Config->Pic is true if we are generating position-independent code.
|
||||
Config->Pic = Config->Pie || Config->Shared;
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_hash_style)) {
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "gnu") {
|
||||
Config->GnuHash = true;
|
||||
Config->SysvHash = false;
|
||||
} else if (S == "both") {
|
||||
Config->GnuHash = true;
|
||||
} else if (S != "sysv")
|
||||
error("unknown hash style: " + S);
|
||||
}
|
||||
|
||||
// Parse --build-id or --build-id=<style>.
|
||||
// Parse --build-id or --build-id=<style>. We handle "tree" as a
|
||||
// synonym for "sha1" because all of our hash functions including
|
||||
// -build-id=sha1 are tree hashes for performance reasons.
|
||||
if (Args.hasArg(OPT_build_id))
|
||||
Config->BuildId = BuildIdKind::Fast;
|
||||
if (auto *Arg = Args.getLastArg(OPT_build_id_eq)) {
|
||||
|
|
@ -589,15 +692,10 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
|||
}
|
||||
}
|
||||
|
||||
for (auto *Arg : Args.filtered(OPT_auxiliary))
|
||||
Config->AuxiliaryList.push_back(Arg->getValue());
|
||||
if (!Config->Shared && !Config->AuxiliaryList.empty())
|
||||
error("-f may not be used without -shared");
|
||||
|
||||
for (auto *Arg : Args.filtered(OPT_undefined))
|
||||
Config->Undefined.push_back(Arg->getValue());
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_dynamic_list))
|
||||
for (auto *Arg : Args.filtered(OPT_dynamic_list))
|
||||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
readDynamicList(*Buffer);
|
||||
|
||||
|
|
@ -605,13 +703,14 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
|||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
Config->SymbolOrderingFile = getLines(*Buffer);
|
||||
|
||||
// If --retain-symbol-file is used, we'll retail 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.
|
||||
if (auto *Arg = Args.getLastArg(OPT_retain_symbols_file)) {
|
||||
Config->Discard = DiscardPolicy::RetainFile;
|
||||
Config->DefaultSymbolVersion = VER_NDX_LOCAL;
|
||||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
for (StringRef S : getLines(*Buffer))
|
||||
Config->RetainSymbolsFile.insert(S);
|
||||
Config->VersionScriptGlobals.push_back(
|
||||
{S, /*IsExternCpp*/ false, /*HasWildcard*/ false});
|
||||
}
|
||||
|
||||
for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
|
||||
|
|
@ -627,11 +726,37 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
|||
Config->DefaultSymbolVersion = VER_NDX_LOCAL;
|
||||
}
|
||||
|
||||
if (getArg(Args, OPT_export_dynamic, OPT_no_export_dynamic, false))
|
||||
Config->DefaultSymbolVersion = VER_NDX_GLOBAL;
|
||||
|
||||
if (auto *Arg = Args.getLastArg(OPT_version_script))
|
||||
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
|
||||
readVersionScript(*Buffer);
|
||||
}
|
||||
|
||||
// Some Config members do not directly correspond to any particular
|
||||
// command line options, but computed based on other Config values.
|
||||
// This function initialize such members. See Config.h for the details
|
||||
// of these values.
|
||||
static void setConfigs() {
|
||||
ELFKind Kind = Config->EKind;
|
||||
uint16_t Machine = Config->EMachine;
|
||||
|
||||
// There is an ILP32 ABI for x86-64, although it's not very popular.
|
||||
// It is called the x32 ABI.
|
||||
bool IsX32 = (Kind == ELF32LEKind && Machine == EM_X86_64);
|
||||
|
||||
Config->CopyRelocs = (Config->Relocatable || Config->EmitRelocs);
|
||||
Config->Is64 = (Kind == ELF64LEKind || Kind == ELF64BEKind);
|
||||
Config->IsLE = (Kind == ELF32LEKind || Kind == ELF64LEKind);
|
||||
Config->Endianness =
|
||||
Config->IsLE ? support::endianness::little : support::endianness::big;
|
||||
Config->IsMips64EL = (Kind == ELF64LEKind && Machine == EM_MIPS);
|
||||
Config->IsRela = Config->Is64 || IsX32 || Config->MipsN32Abi;
|
||||
Config->Pic = Config->Pie || Config->Shared;
|
||||
Config->Wordsize = Config->Is64 ? 8 : 4;
|
||||
}
|
||||
|
||||
// Returns a value of "-format" option.
|
||||
static bool getBinaryOption(StringRef S) {
|
||||
if (S == "binary")
|
||||
|
|
@ -650,7 +775,7 @@ void LinkerDriver::createFiles(opt::InputArgList &Args) {
|
|||
addLibrary(Arg->getValue());
|
||||
break;
|
||||
case OPT_INPUT:
|
||||
addFile(Arg->getValue());
|
||||
addFile(Arg->getValue(), /*WithLOption=*/false);
|
||||
break;
|
||||
case OPT_alias_script_T:
|
||||
case OPT_script:
|
||||
|
|
@ -744,12 +869,7 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
SymbolTable<ELFT> Symtab;
|
||||
elf::Symtab<ELFT>::X = &Symtab;
|
||||
Target = createTarget();
|
||||
ScriptBase = Script<ELFT>::X = make<LinkerScript<ELFT>>();
|
||||
|
||||
Config->Rela =
|
||||
ELFT::Is64Bits || Config->EMachine == EM_X86_64 || Config->MipsN32Abi;
|
||||
Config->Mips64EL =
|
||||
(Config->EMachine == EM_MIPS && Config->EKind == ELF64LEKind);
|
||||
Config->MaxPageSize = getMaxPageSize(Args);
|
||||
Config->ImageBase = getImageBase(Args);
|
||||
|
||||
|
|
@ -757,6 +877,14 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
if (Config->OutputFile.empty())
|
||||
Config->OutputFile = "a.out";
|
||||
|
||||
// Fail early if the output file or map file is not writable. If a user has a
|
||||
// long link, e.g. due to a large LTO link, they do not wish to run it and
|
||||
// find that it failed because there was a mistake in their command-line.
|
||||
if (!isFileWritable(Config->OutputFile, "output file"))
|
||||
return;
|
||||
if (!isFileWritable(Config->MapFile, "map file"))
|
||||
return;
|
||||
|
||||
// Use default entry point name if no name was given via the command
|
||||
// line nor linker scripts. For some reason, MIPS entry point name is
|
||||
// different from others.
|
||||
|
|
@ -792,6 +920,11 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
if (ErrorCount)
|
||||
return;
|
||||
|
||||
// Some symbols (such as __ehdr_start) are defined lazily only when there
|
||||
// are undefined symbols for them, so we add these to trigger that logic.
|
||||
for (StringRef Sym : Script->Opt.ReferencedSymbols)
|
||||
Symtab.addUndefined(Sym);
|
||||
|
||||
for (auto *Arg : Args.filtered(OPT_wrap))
|
||||
Symtab.wrap(Arg->getValue());
|
||||
|
||||
|
|
@ -799,12 +932,12 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
// Beyond this point, no new files are added.
|
||||
// Aggregate all input sections into one place.
|
||||
for (elf::ObjectFile<ELFT> *F : Symtab.getObjectFiles())
|
||||
for (InputSectionBase<ELFT> *S : F->getSections())
|
||||
if (S && S != &InputSection<ELFT>::Discarded)
|
||||
Symtab.Sections.push_back(S);
|
||||
for (InputSectionBase *S : F->getSections())
|
||||
if (S && S != &InputSection::Discarded)
|
||||
InputSections.push_back(S);
|
||||
for (BinaryFile *F : Symtab.getBinaryFiles())
|
||||
for (InputSectionData *S : F->getSections())
|
||||
Symtab.Sections.push_back(cast<InputSection<ELFT>>(S));
|
||||
for (InputSectionBase *S : F->getSections())
|
||||
InputSections.push_back(cast<InputSection>(S));
|
||||
|
||||
// Do size optimizations: garbage collection and identical code folding.
|
||||
if (Config->GcSections)
|
||||
|
|
@ -814,15 +947,15 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
|
||||
// MergeInputSection::splitIntoPieces needs to be called before
|
||||
// any call of MergeInputSection::getOffset. Do that.
|
||||
forEach(Symtab.Sections.begin(), Symtab.Sections.end(),
|
||||
[](InputSectionBase<ELFT> *S) {
|
||||
if (!S->Live)
|
||||
return;
|
||||
if (Decompressor::isCompressedELFSection(S->Flags, S->Name))
|
||||
S->uncompress();
|
||||
if (auto *MS = dyn_cast<MergeInputSection<ELFT>>(S))
|
||||
MS->splitIntoPieces();
|
||||
});
|
||||
parallelForEach(InputSections.begin(), InputSections.end(),
|
||||
[](InputSectionBase *S) {
|
||||
if (!S->Live)
|
||||
return;
|
||||
if (Decompressor::isCompressedELFSection(S->Flags, S->Name))
|
||||
S->uncompress();
|
||||
if (auto *MS = dyn_cast<MergeInputSection>(S))
|
||||
MS->splitIntoPieces();
|
||||
});
|
||||
|
||||
// Write the result to the file.
|
||||
writeResult<ELFT>();
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ extern class LinkerDriver *Driver;
|
|||
class LinkerDriver {
|
||||
public:
|
||||
void main(ArrayRef<const char *> Args, bool CanExitEarly);
|
||||
void addFile(StringRef Path);
|
||||
void addFile(StringRef Path, bool WithLOption);
|
||||
void addLibrary(StringRef Name);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#include "Driver.h"
|
||||
#include "Error.h"
|
||||
#include "Memory.h"
|
||||
#include "ScriptParser.h"
|
||||
#include "lld/Config/Version.h"
|
||||
#include "lld/Core/Reproduce.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
|
|
@ -54,12 +53,10 @@ ELFOptTable::ELFOptTable() : OptTable(OptInfo) {}
|
|||
|
||||
// Parse -color-diagnostics={auto,always,never} or -no-color-diagnostics.
|
||||
static bool getColorDiagnostics(opt::InputArgList &Args) {
|
||||
bool Default = (ErrorOS == &errs() && Process::StandardErrHasColors());
|
||||
|
||||
auto *Arg = Args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,
|
||||
OPT_no_color_diagnostics);
|
||||
if (!Arg)
|
||||
return Default;
|
||||
return ErrorOS->has_colors();
|
||||
if (Arg->getOption().getID() == OPT_color_diagnostics)
|
||||
return true;
|
||||
if (Arg->getOption().getID() == OPT_no_color_diagnostics)
|
||||
|
|
@ -67,7 +64,7 @@ static bool getColorDiagnostics(opt::InputArgList &Args) {
|
|||
|
||||
StringRef S = Arg->getValue();
|
||||
if (S == "auto")
|
||||
return Default;
|
||||
return ErrorOS->has_colors();
|
||||
if (S == "always")
|
||||
return true;
|
||||
if (S != "never")
|
||||
|
|
@ -120,6 +117,20 @@ opt::InputArgList ELFOptTable::parse(ArrayRef<const char *> Argv) {
|
|||
void elf::printHelp(const char *Argv0) {
|
||||
ELFOptTable Table;
|
||||
Table.PrintHelp(outs(), Argv0, "lld", false);
|
||||
outs() << "\n";
|
||||
|
||||
// Scripts generated by Libtool versions up to at least 2.4.6 (the most
|
||||
// recent version as of March 2017) expect /: supported targets:.* elf/
|
||||
// in a message for the -help option. If it doesn't match, the scripts
|
||||
// assume that the linker doesn't support very basic features such as
|
||||
// shared libraries. Therefore, we need to print out at least "elf".
|
||||
// Here, we print out all the targets that we support.
|
||||
outs() << Argv0 << ": supported targets: "
|
||||
<< "elf32-i386 elf32-iamcu elf32-littlearm elf32-ntradbigmips "
|
||||
<< "elf32-ntradlittlemips elf32-powerpc elf32-tradbigmips "
|
||||
<< "elf32-tradlittlemips elf32-x86-64 "
|
||||
<< "elf64-amdgpu elf64-littleaarch64 elf64-powerpc elf64-tradbigmips "
|
||||
<< "elf64-tradlittlemips elf64-x86-64\n";
|
||||
}
|
||||
|
||||
// Reconstructs command line arguments so that so that you can re-run
|
||||
|
|
@ -136,6 +147,13 @@ std::string elf::createResponseFile(const opt::InputArgList &Args) {
|
|||
case OPT_INPUT:
|
||||
OS << quote(rewritePath(Arg->getValue())) << "\n";
|
||||
break;
|
||||
case OPT_o:
|
||||
// If -o path contains directories, "lld @response.txt" will likely
|
||||
// fail because the archive we are creating doesn't contain empty
|
||||
// directories for the output path (-o doesn't create directories).
|
||||
// Strip directories to prevent the issue.
|
||||
OS << "-o " << quote(sys::path::filename(Arg->getValue())) << "\n";
|
||||
break;
|
||||
case OPT_L:
|
||||
case OPT_dynamic_list:
|
||||
case OPT_rpath:
|
||||
|
|
|
|||
|
|
@ -38,13 +38,14 @@ using namespace lld::elf;
|
|||
namespace {
|
||||
template <class ELFT> class EhReader {
|
||||
public:
|
||||
EhReader(InputSectionBase<ELFT> *S, ArrayRef<uint8_t> D) : IS(S), D(D) {}
|
||||
EhReader(InputSectionBase *S, ArrayRef<uint8_t> D) : IS(S), D(D) {}
|
||||
size_t readEhRecordSize();
|
||||
uint8_t getFdeEncoding();
|
||||
|
||||
private:
|
||||
template <class P> void failOn(const P *Loc, const Twine &Msg) {
|
||||
fatal(IS->getLocation((const uint8_t *)Loc - IS->Data.data()) + ": " + Msg);
|
||||
fatal("corrupted .eh_frame: " + Msg + "\n>>> defined in " +
|
||||
IS->getObjMsg<ELFT>((const uint8_t *)Loc - IS->Data.data()));
|
||||
}
|
||||
|
||||
uint8_t readByte();
|
||||
|
|
@ -53,15 +54,16 @@ private:
|
|||
void skipLeb128();
|
||||
void skipAugP();
|
||||
|
||||
InputSectionBase<ELFT> *IS;
|
||||
InputSectionBase *IS;
|
||||
ArrayRef<uint8_t> D;
|
||||
};
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
size_t elf::readEhRecordSize(InputSectionBase<ELFT> *S, size_t Off) {
|
||||
size_t elf::readEhRecordSize(InputSectionBase *S, size_t Off) {
|
||||
return EhReader<ELFT>(S, S->Data.slice(Off)).readEhRecordSize();
|
||||
}
|
||||
|
||||
// .eh_frame section is a sequence of records. Each record starts with
|
||||
// a 4 byte length field. This function reads the length.
|
||||
template <class ELFT> size_t EhReader<ELFT>::readEhRecordSize() {
|
||||
|
|
@ -121,11 +123,11 @@ template <class ELFT> void EhReader<ELFT>::skipLeb128() {
|
|||
failOn(ErrPos, "corrupted CIE (failed to read LEB128)");
|
||||
}
|
||||
|
||||
template <class ELFT> static size_t getAugPSize(unsigned Enc) {
|
||||
static size_t getAugPSize(unsigned Enc) {
|
||||
switch (Enc & 0x0f) {
|
||||
case DW_EH_PE_absptr:
|
||||
case DW_EH_PE_signed:
|
||||
return ELFT::Is64Bits ? 8 : 4;
|
||||
return Config->Wordsize;
|
||||
case DW_EH_PE_udata2:
|
||||
case DW_EH_PE_sdata2:
|
||||
return 2;
|
||||
|
|
@ -143,7 +145,7 @@ template <class ELFT> void EhReader<ELFT>::skipAugP() {
|
|||
uint8_t Enc = readByte();
|
||||
if ((Enc & 0xf0) == DW_EH_PE_aligned)
|
||||
failOn(D.data() - 1, "DW_EH_PE_aligned encoding is not supported");
|
||||
size_t Size = getAugPSize<ELFT>(Enc);
|
||||
size_t Size = getAugPSize(Enc);
|
||||
if (Size == 0)
|
||||
failOn(D.data() - 1, "unknown FDE encoding");
|
||||
if (Size >= D.size())
|
||||
|
|
@ -152,7 +154,7 @@ template <class ELFT> void EhReader<ELFT>::skipAugP() {
|
|||
}
|
||||
|
||||
template <class ELFT> uint8_t elf::getFdeEncoding(EhSectionPiece *P) {
|
||||
auto *IS = static_cast<InputSectionBase<ELFT> *>(P->ID);
|
||||
auto *IS = static_cast<InputSectionBase *>(P->ID);
|
||||
return EhReader<ELFT>(IS, P->data()).getFdeEncoding();
|
||||
}
|
||||
|
||||
|
|
@ -199,14 +201,10 @@ template <class ELFT> uint8_t EhReader<ELFT>::getFdeEncoding() {
|
|||
return DW_EH_PE_absptr;
|
||||
}
|
||||
|
||||
template size_t elf::readEhRecordSize<ELF32LE>(InputSectionBase<ELF32LE> *S,
|
||||
size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF32BE>(InputSectionBase<ELF32BE> *S,
|
||||
size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF64LE>(InputSectionBase<ELF64LE> *S,
|
||||
size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF64BE>(InputSectionBase<ELF64BE> *S,
|
||||
size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF32LE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF32BE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF64LE>(InputSectionBase *S, size_t Off);
|
||||
template size_t elf::readEhRecordSize<ELF64BE>(InputSectionBase *S, size_t Off);
|
||||
|
||||
template uint8_t elf::getFdeEncoding<ELF32LE>(EhSectionPiece *P);
|
||||
template uint8_t elf::getFdeEncoding<ELF32BE>(EhSectionPiece *P);
|
||||
|
|
|
|||
|
|
@ -14,11 +14,10 @@
|
|||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
template <class ELFT> class InputSectionBase;
|
||||
class InputSectionBase;
|
||||
struct EhSectionPiece;
|
||||
|
||||
template <class ELFT>
|
||||
size_t readEhRecordSize(InputSectionBase<ELFT> *S, size_t Off);
|
||||
template <class ELFT> size_t readEhRecordSize(InputSectionBase *S, size_t Off);
|
||||
template <class ELFT> uint8_t getFdeEncoding(EhSectionPiece *P);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
using namespace lld::elf;
|
||||
using namespace llvm;
|
||||
|
||||
namespace lld {
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
uint64_t elf::ErrorCount;
|
||||
raw_ostream *elf::ErrorOS;
|
||||
|
|
@ -33,6 +33,18 @@ StringRef elf::Argv0;
|
|||
// but outs() or errs() are not thread-safe. We protect them using a mutex.
|
||||
static std::mutex Mu;
|
||||
|
||||
// Prints "\n" or does nothing, depending on Msg contents of
|
||||
// the previous call of this function.
|
||||
static void newline(const Twine &Msg) {
|
||||
// True if the previous error message contained "\n".
|
||||
// We want to separate multi-line error messages with a newline.
|
||||
static bool Flag;
|
||||
|
||||
if (Flag)
|
||||
*ErrorOS << "\n";
|
||||
Flag = (StringRef(Msg.str()).find('\n') != StringRef::npos);
|
||||
}
|
||||
|
||||
static void print(StringRef S, raw_ostream::Colors C) {
|
||||
*ErrorOS << Argv0 + ": ";
|
||||
if (Config->ColorDiagnostics) {
|
||||
|
|
@ -45,9 +57,16 @@ static void print(StringRef S, raw_ostream::Colors C) {
|
|||
}
|
||||
|
||||
void elf::log(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
if (Config->Verbose)
|
||||
if (Config->Verbose) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Argv0 << ": " << Msg << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void elf::message(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
outs() << Msg << "\n";
|
||||
outs().flush();
|
||||
}
|
||||
|
||||
void elf::warn(const Twine &Msg) {
|
||||
|
|
@ -55,13 +74,16 @@ void elf::warn(const Twine &Msg) {
|
|||
error(Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
newline(Msg);
|
||||
print("warning: ", raw_ostream::MAGENTA);
|
||||
*ErrorOS << Msg << "\n";
|
||||
}
|
||||
|
||||
void elf::error(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
newline(Msg);
|
||||
|
||||
if (Config->ErrorLimit == 0 || ErrorCount < Config->ErrorLimit) {
|
||||
print("error: ", raw_ostream::RED);
|
||||
|
|
@ -77,10 +99,6 @@ void elf::error(const Twine &Msg) {
|
|||
++ErrorCount;
|
||||
}
|
||||
|
||||
void elf::error(std::error_code EC, const Twine &Prefix) {
|
||||
error(Prefix + ": " + EC.message());
|
||||
}
|
||||
|
||||
void elf::exitLld(int Val) {
|
||||
// Dealloc/destroy ManagedStatic variables before calling
|
||||
// _exit(). In a non-LTO build, this is a nop. In an LTO
|
||||
|
|
@ -93,18 +111,6 @@ void elf::exitLld(int Val) {
|
|||
}
|
||||
|
||||
void elf::fatal(const Twine &Msg) {
|
||||
std::lock_guard<std::mutex> Lock(Mu);
|
||||
print("error: ", raw_ostream::RED);
|
||||
*ErrorOS << Msg << "\n";
|
||||
error(Msg);
|
||||
exitLld(1);
|
||||
}
|
||||
|
||||
void elf::fatal(std::error_code EC, const Twine &Prefix) {
|
||||
fatal(Prefix + ": " + EC.message());
|
||||
}
|
||||
|
||||
void elf::fatal(Error &E, const Twine &Prefix) {
|
||||
fatal(Prefix + ": " + llvm::toString(std::move(E)));
|
||||
}
|
||||
|
||||
} // namespace lld
|
||||
|
|
|
|||
15
ELF/Error.h
15
ELF/Error.h
|
|
@ -15,10 +15,14 @@
|
|||
// Error prints out an error message and increment a global variable
|
||||
// ErrorCount to record the fact that we met an error condition. It does
|
||||
// not exit, so it is safe for a lld-as-a-library use case. It is generally
|
||||
// useful because it can report more than one errors in a single run.
|
||||
// useful because it can report more than one error in a single run.
|
||||
//
|
||||
// Warn doesn't do anything but printing out a given message.
|
||||
//
|
||||
// It is not recommended to use llvm::outs() or llvm::errs() directly
|
||||
// in LLD because they are not thread-safe. The functions declared in
|
||||
// this file are mutually excluded, so you want to use them instead.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_ERROR_H
|
||||
|
|
@ -36,15 +40,12 @@ extern llvm::raw_ostream *ErrorOS;
|
|||
extern llvm::StringRef Argv0;
|
||||
|
||||
void log(const Twine &Msg);
|
||||
void message(const Twine &Msg);
|
||||
void warn(const Twine &Msg);
|
||||
|
||||
void error(const Twine &Msg);
|
||||
void error(std::error_code EC, const Twine &Prefix);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
|
||||
|
||||
LLVM_ATTRIBUTE_NORETURN void exitLld(int Val);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(std::error_code EC, const Twine &Prefix);
|
||||
LLVM_ATTRIBUTE_NORETURN void fatal(Error &E, const Twine &Prefix);
|
||||
|
||||
// check() functions are convenient functions to strip errors
|
||||
// from error-or-value objects.
|
||||
|
|
@ -68,7 +69,7 @@ template <class T> T check(ErrorOr<T> E, const Twine &Prefix) {
|
|||
|
||||
template <class T> T check(Expected<T> E, const Twine &Prefix) {
|
||||
if (!E)
|
||||
fatal(Prefix + ": " + errorToErrorCode(E.takeError()).message());
|
||||
fatal(Prefix + ": " + toString(E.takeError()));
|
||||
return std::move(*E);
|
||||
}
|
||||
|
||||
|
|
|
|||
79
ELF/Filesystem.cpp
Normal file
79
ELF/Filesystem.cpp
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
//===- Filesystem.cpp -----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains a few utility functions to handle files.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Filesystem.h"
|
||||
#include "Config.h"
|
||||
#include "Error.h"
|
||||
#include "llvm/Support/FileSystem.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include <thread>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// Removes a given file asynchronously. This is a performance hack,
|
||||
// so remove this when operating systems are improved.
|
||||
//
|
||||
// On Linux (and probably on other Unix-like systems), unlink(2) is a
|
||||
// noticeably slow system call. As of 2016, unlink takes 250
|
||||
// milliseconds to remove a 1 GB file on ext4 filesystem on my machine.
|
||||
//
|
||||
// To create a new result file, we first remove existing file. So, if
|
||||
// you repeatedly link a 1 GB program in a regular compile-link-debug
|
||||
// cycle, every cycle wastes 250 milliseconds only to remove a file.
|
||||
// Since LLD can link a 1 GB binary in about 5 seconds, that waste
|
||||
// actually counts.
|
||||
//
|
||||
// This function spawns a background thread to call unlink.
|
||||
// The calling thread returns almost immediately.
|
||||
void elf::unlinkAsync(StringRef Path) {
|
||||
if (!Config->Threads || !sys::fs::exists(Config->OutputFile))
|
||||
return;
|
||||
|
||||
// First, rename Path to avoid race condition. We cannot remove
|
||||
// Path from a different thread because we are now going to create
|
||||
// Path as a new file. If we do that in a different thread, the new
|
||||
// thread can remove the new file.
|
||||
SmallString<128> TempPath;
|
||||
if (sys::fs::createUniqueFile(Path + "tmp%%%%%%%%", TempPath))
|
||||
return;
|
||||
if (sys::fs::rename(Path, TempPath)) {
|
||||
sys::fs::remove(TempPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove TempPath in background.
|
||||
std::thread([=] { ::remove(TempPath.str().str().c_str()); }).detach();
|
||||
}
|
||||
|
||||
// Returns true if a given file seems to be writable.
|
||||
//
|
||||
// Determining whether a file is writable or not is amazingly hard,
|
||||
// and after all the only reliable way of doing that is to actually
|
||||
// create a file. But we don't want to do that in this function
|
||||
// because LLD shouldn't update any file if it will end in a failure.
|
||||
// We also don't want to reimplement heuristics. So we'll let
|
||||
// FileOutputBuffer do the work.
|
||||
//
|
||||
// FileOutputBuffer doesn't touch a desitnation file until commit()
|
||||
// is called. We use that class without calling commit() to predict
|
||||
// if the given file is writable.
|
||||
bool elf::isFileWritable(StringRef Path, StringRef Desc) {
|
||||
if (auto EC = FileOutputBuffer::create(Path, 1).getError()) {
|
||||
error("cannot open " + Desc + " " + Path + ": " + EC.message());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
22
ELF/Filesystem.h
Normal file
22
ELF/Filesystem.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//===- Filesystem.h ---------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_FILESYSTEM_H
|
||||
#define LLD_ELF_FILESYSTEM_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
void unlinkAsync(StringRef Path);
|
||||
bool isFileWritable(StringRef Path, StringRef FileDescription);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
202
ELF/GdbIndex.cpp
202
ELF/GdbIndex.cpp
|
|
@ -7,199 +7,43 @@
|
|||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// File contains classes for implementation of --gdb-index command line option.
|
||||
// The -gdb-index option instructs the linker to emit a .gdb_index section.
|
||||
// The section contains information to make gdb startup faster.
|
||||
// The format of the section is described at
|
||||
// https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html.
|
||||
//
|
||||
// If that option is used, linker should emit a .gdb_index section that allows
|
||||
// debugger to locate and read .dwo files, containing neccessary debug
|
||||
// information.
|
||||
// More information about implementation can be found in DWARF specification,
|
||||
// latest version is available at http://dwarfstd.org.
|
||||
//
|
||||
// .gdb_index section format:
|
||||
// (Information is based on/taken from
|
||||
// https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html (*))
|
||||
//
|
||||
// A mapped index consists of several areas, laid out in order:
|
||||
// 1) The file header.
|
||||
// 2) "The CU (compilation unit) list. This is a sequence of pairs of 64-bit
|
||||
// little-endian values, sorted by the CU offset. The first element in each
|
||||
// pair is the offset of a CU in the .debug_info section. The second element
|
||||
// in each pair is the length of that CU. References to a CU elsewhere in the
|
||||
// map are done using a CU index, which is just the 0-based index into this
|
||||
// table. Note that if there are type CUs, then conceptually CUs and type CUs
|
||||
// form a single list for the purposes of CU indices."(*)
|
||||
// 3) The types CU list. Depricated as .debug_types does not appear in the DWARF
|
||||
// v5 specification.
|
||||
// 4) The address area. The address area is a sequence of address
|
||||
// entries, where each entrie contains low address, high address and CU
|
||||
// index.
|
||||
// 5) "The symbol table. This is an open-addressed hash table. The size of the
|
||||
// hash table is always a power of 2. Each slot in the hash table consists of
|
||||
// a pair of offset_type values. The first value is the offset of the
|
||||
// symbol's name in the constant pool. The second value is the offset of the
|
||||
// CU vector in the constant pool."(*)
|
||||
// 6) "The constant pool. This is simply a bunch of bytes. It is organized so
|
||||
// that alignment is correct: CU vectors are stored first, followed by
|
||||
// strings." (*)
|
||||
//
|
||||
// For constructing the .gdb_index section following steps should be performed:
|
||||
// 1) For file header nothing special should be done. It contains the offsets to
|
||||
// the areas below.
|
||||
// 2) Scan the compilation unit headers of the .debug_info sections to build a
|
||||
// list of compilation units.
|
||||
// 3) CU Types are no longer needed as DWARF skeleton type units never made it
|
||||
// into the standard. lld does nothing to support parsing of .debug_types
|
||||
// and generates empty types CU area in .gdb_index section.
|
||||
// 4) Address area entries are extracted from DW_TAG_compile_unit DIEs of
|
||||
// .debug_info sections.
|
||||
// 5) For building the symbol table linker extracts the public names from the
|
||||
// .debug_gnu_pubnames and .debug_gnu_pubtypes sections. Then it builds the
|
||||
// hashtable in according to .gdb_index format specification.
|
||||
// 6) Constant pool is populated at the same time as symbol table.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "GdbIndex.h"
|
||||
#include "Memory.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
template <class ELFT>
|
||||
GdbIndexBuilder<ELFT>::GdbIndexBuilder(InputSection<ELFT> *DebugInfoSec)
|
||||
: DebugInfoSec(DebugInfoSec) {
|
||||
if (Expected<std::unique_ptr<object::ObjectFile>> Obj =
|
||||
object::ObjectFile::createObjectFile(DebugInfoSec->getFile()->MB))
|
||||
Dwarf.reset(new DWARFContextInMemory(*Obj.get(), this));
|
||||
else
|
||||
error(toString(DebugInfoSec->getFile()) + ": error creating DWARF context");
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::vector<std::pair<typename ELFT::uint, typename ELFT::uint>>
|
||||
GdbIndexBuilder<ELFT>::readCUList() {
|
||||
std::vector<std::pair<uintX_t, uintX_t>> Ret;
|
||||
for (std::unique_ptr<DWARFCompileUnit> &CU : Dwarf->compile_units())
|
||||
Ret.push_back(
|
||||
{DebugInfoSec->OutSecOff + CU->getOffset(), CU->getLength() + 4});
|
||||
return Ret;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::vector<std::pair<StringRef, uint8_t>>
|
||||
GdbIndexBuilder<ELFT>::readPubNamesAndTypes() {
|
||||
const bool IsLE = ELFT::TargetEndianness == llvm::support::little;
|
||||
StringRef Data[] = {Dwarf->getGnuPubNamesSection(),
|
||||
Dwarf->getGnuPubTypesSection()};
|
||||
|
||||
std::vector<std::pair<StringRef, uint8_t>> Ret;
|
||||
for (StringRef D : Data) {
|
||||
DWARFDebugPubTable PubTable(D, IsLE, true);
|
||||
for (const DWARFDebugPubTable::Set &S : PubTable.getData())
|
||||
for (const DWARFDebugPubTable::Entry &E : S.Entries)
|
||||
Ret.push_back({E.Name, E.Descriptor.toBits()});
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
std::pair<bool, GdbSymbol *> GdbHashTab::add(uint32_t Hash, size_t Offset) {
|
||||
if (Size * 4 / 3 >= Table.size())
|
||||
expand();
|
||||
|
||||
GdbSymbol **Slot = findSlot(Hash, Offset);
|
||||
bool New = false;
|
||||
if (*Slot == nullptr) {
|
||||
++Size;
|
||||
*Slot = new (Alloc) GdbSymbol(Hash, Offset);
|
||||
New = true;
|
||||
}
|
||||
return {New, *Slot};
|
||||
GdbSymbol *&Sym = Map[Offset];
|
||||
if (Sym)
|
||||
return {false, Sym};
|
||||
Sym = make<GdbSymbol>(Hash, Offset);
|
||||
return {true, Sym};
|
||||
}
|
||||
|
||||
void GdbHashTab::expand() {
|
||||
if (Table.empty()) {
|
||||
Table.resize(InitialSize);
|
||||
return;
|
||||
}
|
||||
std::vector<GdbSymbol *> NewTable(Table.size() * 2);
|
||||
NewTable.swap(Table);
|
||||
void GdbHashTab::finalizeContents() {
|
||||
uint32_t Size = std::max<uint32_t>(1024, NextPowerOf2(Map.size() * 4 / 3));
|
||||
uint32_t Mask = Size - 1;
|
||||
Table.resize(Size);
|
||||
|
||||
for (GdbSymbol *Sym : NewTable) {
|
||||
if (!Sym)
|
||||
continue;
|
||||
GdbSymbol **Slot = findSlot(Sym->NameHash, Sym->NameOffset);
|
||||
*Slot = Sym;
|
||||
for (auto &P : Map) {
|
||||
GdbSymbol *Sym = P.second;
|
||||
uint32_t I = Sym->NameHash & Mask;
|
||||
uint32_t Step = ((Sym->NameHash * 17) & Mask) | 1;
|
||||
|
||||
while (Table[I])
|
||||
I = (I + Step) & Mask;
|
||||
Table[I] = Sym;
|
||||
}
|
||||
}
|
||||
|
||||
// Methods finds a slot for symbol with given hash. The step size used to find
|
||||
// the next candidate slot when handling a hash collision is specified in
|
||||
// .gdb_index section format. The hash value for a table entry is computed by
|
||||
// applying an iterative hash function to the symbol's name.
|
||||
GdbSymbol **GdbHashTab::findSlot(uint32_t Hash, size_t Offset) {
|
||||
uint32_t Index = Hash & (Table.size() - 1);
|
||||
uint32_t Step = ((Hash * 17) & (Table.size() - 1)) | 1;
|
||||
|
||||
for (;;) {
|
||||
GdbSymbol *S = Table[Index];
|
||||
if (!S || ((S->NameOffset == Offset) && (S->NameHash == Hash)))
|
||||
return &Table[Index];
|
||||
Index = (Index + Step) & (Table.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static InputSectionBase<ELFT> *
|
||||
findSection(ArrayRef<InputSectionBase<ELFT> *> Arr, uint64_t Offset) {
|
||||
for (InputSectionBase<ELFT> *S : Arr)
|
||||
if (S && S != &InputSection<ELFT>::Discarded)
|
||||
if (Offset >= S->Offset && Offset < S->Offset + S->getSize())
|
||||
return S;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::vector<AddressEntry<ELFT>>
|
||||
GdbIndexBuilder<ELFT>::readAddressArea(size_t CurrentCU) {
|
||||
std::vector<AddressEntry<ELFT>> Ret;
|
||||
for (const auto &CU : Dwarf->compile_units()) {
|
||||
DWARFAddressRangesVector Ranges;
|
||||
CU->collectAddressRanges(Ranges);
|
||||
|
||||
ArrayRef<InputSectionBase<ELFT> *> Sections =
|
||||
DebugInfoSec->getFile()->getSections();
|
||||
|
||||
for (std::pair<uint64_t, uint64_t> &R : Ranges)
|
||||
if (InputSectionBase<ELFT> *S = findSection(Sections, R.first))
|
||||
Ret.push_back(
|
||||
{S, R.first - S->Offset, R.second - S->Offset, CurrentCU});
|
||||
++CurrentCU;
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// We return file offset as load address for allocatable sections. That is
|
||||
// currently used for collecting address ranges in readAddressArea(). We are
|
||||
// able then to find section index that range belongs to.
|
||||
template <class ELFT>
|
||||
uint64_t GdbIndexBuilder<ELFT>::getSectionLoadAddress(
|
||||
const object::SectionRef &Sec) const {
|
||||
if (static_cast<const ELFSectionRef &>(Sec).getFlags() & ELF::SHF_ALLOC)
|
||||
return static_cast<const ELFSectionRef &>(Sec).getOffset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::unique_ptr<LoadedObjectInfo> GdbIndexBuilder<ELFT>::clone() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
template class GdbIndexBuilder<ELF32LE>;
|
||||
template class GdbIndexBuilder<ELF32BE>;
|
||||
template class GdbIndexBuilder<ELF64LE>;
|
||||
template class GdbIndexBuilder<ELF64BE>;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,48 +17,16 @@
|
|||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
template <class ELFT> class InputSection;
|
||||
class InputSection;
|
||||
|
||||
// Struct represents single entry of address area of gdb index.
|
||||
template <class ELFT> struct AddressEntry {
|
||||
InputSectionBase<ELFT> *Section;
|
||||
struct AddressEntry {
|
||||
InputSectionBase *Section;
|
||||
uint64_t LowAddress;
|
||||
uint64_t HighAddress;
|
||||
size_t CuIndex;
|
||||
};
|
||||
|
||||
// GdbIndexBuilder is a helper class used for extracting data required
|
||||
// for building .gdb_index section from objects.
|
||||
template <class ELFT> class GdbIndexBuilder : public llvm::LoadedObjectInfo {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
InputSection<ELFT> *DebugInfoSec;
|
||||
|
||||
std::unique_ptr<llvm::DWARFContext> Dwarf;
|
||||
|
||||
public:
|
||||
GdbIndexBuilder(InputSection<ELFT> *DebugInfoSec);
|
||||
|
||||
// Extracts the compilation units. Each first element of pair is a offset of a
|
||||
// CU in the .debug_info section and second is the length of that CU.
|
||||
std::vector<std::pair<uintX_t, uintX_t>> readCUList();
|
||||
|
||||
// Extracts the vector of address area entries. Accepts global index of last
|
||||
// parsed CU.
|
||||
std::vector<AddressEntry<ELFT>> readAddressArea(size_t CurrentCU);
|
||||
|
||||
// Method extracts public names and types. It returns list of name and
|
||||
// gnu_pub* kind pairs.
|
||||
std::vector<std::pair<StringRef, uint8_t>> readPubNamesAndTypes();
|
||||
|
||||
private:
|
||||
// Method returns section file offset as a load addres for DWARF parser. That
|
||||
// allows to find the target section index for address ranges.
|
||||
uint64_t
|
||||
getSectionLoadAddress(const llvm::object::SectionRef &Sec) const override;
|
||||
std::unique_ptr<llvm::LoadedObjectInfo> clone() const override;
|
||||
};
|
||||
|
||||
// Element of GdbHashTab hash table.
|
||||
struct GdbSymbol {
|
||||
GdbSymbol(uint32_t Hash, size_t Offset)
|
||||
|
|
@ -75,22 +43,13 @@ class GdbHashTab final {
|
|||
public:
|
||||
std::pair<bool, GdbSymbol *> add(uint32_t Hash, size_t Offset);
|
||||
|
||||
void finalizeContents();
|
||||
size_t getCapacity() { return Table.size(); }
|
||||
GdbSymbol *getSymbol(size_t I) { return Table[I]; }
|
||||
|
||||
private:
|
||||
void expand();
|
||||
|
||||
GdbSymbol **findSlot(uint32_t Hash, size_t Offset);
|
||||
|
||||
llvm::BumpPtrAllocator Alloc;
|
||||
llvm::DenseMap<size_t, GdbSymbol *> Map;
|
||||
std::vector<GdbSymbol *> Table;
|
||||
|
||||
// Size keeps the amount of filled entries in Table.
|
||||
size_t Size = 0;
|
||||
|
||||
// Initial size must be a power of 2.
|
||||
static const int32_t InitialSize = 1024;
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
|
|
|
|||
100
ELF/ICF.cpp
100
ELF/ICF.cpp
|
|
@ -77,7 +77,6 @@
|
|||
#include "Config.h"
|
||||
#include "SymbolTable.h"
|
||||
#include "Threads.h"
|
||||
|
||||
#include "llvm/ADT/Hashing.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
#include "llvm/Support/ELF.h"
|
||||
|
|
@ -102,11 +101,11 @@ private:
|
|||
bool constantEq(ArrayRef<RelTy> RelsA, ArrayRef<RelTy> RelsB);
|
||||
|
||||
template <class RelTy>
|
||||
bool variableEq(const InputSection<ELFT> *A, ArrayRef<RelTy> RelsA,
|
||||
const InputSection<ELFT> *B, ArrayRef<RelTy> RelsB);
|
||||
bool variableEq(const InputSection *A, ArrayRef<RelTy> RelsA,
|
||||
const InputSection *B, ArrayRef<RelTy> RelsB);
|
||||
|
||||
bool equalsConstant(const InputSection<ELFT> *A, const InputSection<ELFT> *B);
|
||||
bool equalsVariable(const InputSection<ELFT> *A, const InputSection<ELFT> *B);
|
||||
bool equalsConstant(const InputSection *A, const InputSection *B);
|
||||
bool equalsVariable(const InputSection *A, const InputSection *B);
|
||||
|
||||
size_t findBoundary(size_t Begin, size_t End);
|
||||
|
||||
|
|
@ -115,7 +114,7 @@ private:
|
|||
|
||||
void forEachClass(std::function<void(size_t, size_t)> Fn);
|
||||
|
||||
std::vector<InputSection<ELFT> *> Sections;
|
||||
std::vector<InputSection *> Sections;
|
||||
|
||||
// We repeat the main loop while `Repeat` is true.
|
||||
std::atomic<bool> Repeat;
|
||||
|
|
@ -154,17 +153,17 @@ private:
|
|||
|
||||
// Returns a hash value for S. Note that the information about
|
||||
// relocation targets is not included in the hash value.
|
||||
template <class ELFT> static uint32_t getHash(InputSection<ELFT> *S) {
|
||||
template <class ELFT> static uint32_t getHash(InputSection *S) {
|
||||
return hash_combine(S->Flags, S->getSize(), S->NumRelocations);
|
||||
}
|
||||
|
||||
// Returns true if section S is subject of ICF.
|
||||
template <class ELFT> static bool isEligible(InputSection<ELFT> *S) {
|
||||
static bool isEligible(InputSection *S) {
|
||||
// .init and .fini contains instructions that must be executed to
|
||||
// initialize and finalize the process. They cannot and should not
|
||||
// be merged.
|
||||
return S->Live && (S->Flags & SHF_ALLOC) && !(S->Flags & SHF_WRITE) &&
|
||||
S->Name != ".init" && S->Name != ".fini";
|
||||
return S->Live && (S->Flags & SHF_ALLOC) && (S->Flags & SHF_EXECINSTR) &&
|
||||
!(S->Flags & SHF_WRITE) && S->Name != ".init" && S->Name != ".fini";
|
||||
}
|
||||
|
||||
// Split an equivalence class into smaller classes.
|
||||
|
|
@ -181,17 +180,17 @@ void ICF<ELFT>::segregate(size_t Begin, size_t End, bool Constant) {
|
|||
while (Begin < End) {
|
||||
// Divide [Begin, End) into two. Let Mid be the start index of the
|
||||
// second group.
|
||||
auto Bound = std::stable_partition(
|
||||
Sections.begin() + Begin + 1, Sections.begin() + End,
|
||||
[&](InputSection<ELFT> *S) {
|
||||
if (Constant)
|
||||
return equalsConstant(Sections[Begin], S);
|
||||
return equalsVariable(Sections[Begin], S);
|
||||
});
|
||||
auto Bound =
|
||||
std::stable_partition(Sections.begin() + Begin + 1,
|
||||
Sections.begin() + End, [&](InputSection *S) {
|
||||
if (Constant)
|
||||
return equalsConstant(Sections[Begin], S);
|
||||
return equalsVariable(Sections[Begin], S);
|
||||
});
|
||||
size_t Mid = Bound - Sections.begin();
|
||||
|
||||
// Now we split [Begin, End) into [Begin, Mid) and [Mid, End) by
|
||||
// updating the sections in [Begin, End). We use Mid as an equivalence
|
||||
// updating the sections in [Begin, Mid). We use Mid as an equivalence
|
||||
// class ID because every group ends with a unique index.
|
||||
for (size_t I = Begin; I < Mid; ++I)
|
||||
Sections[I]->Class[Next] = Mid;
|
||||
|
|
@ -210,7 +209,7 @@ template <class RelTy>
|
|||
bool ICF<ELFT>::constantEq(ArrayRef<RelTy> RelsA, ArrayRef<RelTy> RelsB) {
|
||||
auto Eq = [](const RelTy &A, const RelTy &B) {
|
||||
return A.r_offset == B.r_offset &&
|
||||
A.getType(Config->Mips64EL) == B.getType(Config->Mips64EL) &&
|
||||
A.getType(Config->IsMips64EL) == B.getType(Config->IsMips64EL) &&
|
||||
getAddend<ELFT>(A) == getAddend<ELFT>(B);
|
||||
};
|
||||
|
||||
|
|
@ -221,40 +220,43 @@ bool ICF<ELFT>::constantEq(ArrayRef<RelTy> RelsA, ArrayRef<RelTy> RelsB) {
|
|||
// Compare "non-moving" part of two InputSections, namely everything
|
||||
// except relocation targets.
|
||||
template <class ELFT>
|
||||
bool ICF<ELFT>::equalsConstant(const InputSection<ELFT> *A,
|
||||
const InputSection<ELFT> *B) {
|
||||
bool ICF<ELFT>::equalsConstant(const InputSection *A, const InputSection *B) {
|
||||
if (A->NumRelocations != B->NumRelocations || A->Flags != B->Flags ||
|
||||
A->getSize() != B->getSize() || A->Data != B->Data)
|
||||
return false;
|
||||
|
||||
if (A->AreRelocsRela)
|
||||
return constantEq(A->relas(), B->relas());
|
||||
return constantEq(A->rels(), B->rels());
|
||||
return constantEq(A->template relas<ELFT>(), B->template relas<ELFT>());
|
||||
return constantEq(A->template rels<ELFT>(), B->template rels<ELFT>());
|
||||
}
|
||||
|
||||
// Compare two lists of relocations. Returns true if all pairs of
|
||||
// relocations point to the same section in terms of ICF.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
bool ICF<ELFT>::variableEq(const InputSection<ELFT> *A, ArrayRef<RelTy> RelsA,
|
||||
const InputSection<ELFT> *B, ArrayRef<RelTy> RelsB) {
|
||||
bool ICF<ELFT>::variableEq(const InputSection *A, ArrayRef<RelTy> RelsA,
|
||||
const InputSection *B, ArrayRef<RelTy> RelsB) {
|
||||
auto Eq = [&](const RelTy &RA, const RelTy &RB) {
|
||||
// The two sections must be identical.
|
||||
SymbolBody &SA = A->getFile()->getRelocTargetSym(RA);
|
||||
SymbolBody &SB = B->getFile()->getRelocTargetSym(RB);
|
||||
SymbolBody &SA = A->template getFile<ELFT>()->getRelocTargetSym(RA);
|
||||
SymbolBody &SB = B->template getFile<ELFT>()->getRelocTargetSym(RB);
|
||||
if (&SA == &SB)
|
||||
return true;
|
||||
|
||||
// Or, the two sections must be in the same equivalence class.
|
||||
auto *DA = dyn_cast<DefinedRegular<ELFT>>(&SA);
|
||||
auto *DB = dyn_cast<DefinedRegular<ELFT>>(&SB);
|
||||
auto *DA = dyn_cast<DefinedRegular>(&SA);
|
||||
auto *DB = dyn_cast<DefinedRegular>(&SB);
|
||||
if (!DA || !DB)
|
||||
return false;
|
||||
if (DA->Value != DB->Value)
|
||||
return false;
|
||||
|
||||
auto *X = dyn_cast<InputSection<ELFT>>(DA->Section);
|
||||
auto *Y = dyn_cast<InputSection<ELFT>>(DB->Section);
|
||||
// Either both symbols must be absolute...
|
||||
if (!DA->Section || !DB->Section)
|
||||
return !DA->Section && !DB->Section;
|
||||
|
||||
// Or the two sections must be in the same equivalence class.
|
||||
auto *X = dyn_cast<InputSection>(DA->Section);
|
||||
auto *Y = dyn_cast<InputSection>(DB->Section);
|
||||
if (!X || !Y)
|
||||
return false;
|
||||
|
||||
|
|
@ -271,11 +273,11 @@ bool ICF<ELFT>::variableEq(const InputSection<ELFT> *A, ArrayRef<RelTy> RelsA,
|
|||
|
||||
// Compare "moving" part of two InputSections, namely relocation targets.
|
||||
template <class ELFT>
|
||||
bool ICF<ELFT>::equalsVariable(const InputSection<ELFT> *A,
|
||||
const InputSection<ELFT> *B) {
|
||||
bool ICF<ELFT>::equalsVariable(const InputSection *A, const InputSection *B) {
|
||||
if (A->AreRelocsRela)
|
||||
return variableEq(A, A->relas(), B, B->relas());
|
||||
return variableEq(A, A->rels(), B, B->rels());
|
||||
return variableEq(A, A->template relas<ELFT>(), B,
|
||||
B->template relas<ELFT>());
|
||||
return variableEq(A, A->template rels<ELFT>(), B, B->template rels<ELFT>());
|
||||
}
|
||||
|
||||
template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
|
||||
|
|
@ -291,7 +293,7 @@ template <class ELFT> size_t ICF<ELFT>::findBoundary(size_t Begin, size_t End) {
|
|||
// groups of sections, grouped by the class.
|
||||
//
|
||||
// This function calls Fn on every group that starts within [Begin, End).
|
||||
// Note that a group must starts in that range but doesn't necessarily
|
||||
// Note that a group must start in that range but doesn't necessarily
|
||||
// have to end before End.
|
||||
template <class ELFT>
|
||||
void ICF<ELFT>::forEachClassRange(size_t Begin, size_t End,
|
||||
|
|
@ -323,8 +325,9 @@ void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) {
|
|||
// Split sections into 256 shards and call Fn in parallel.
|
||||
size_t NumShards = 256;
|
||||
size_t Step = Sections.size() / NumShards;
|
||||
forLoop(0, NumShards,
|
||||
[&](size_t I) { forEachClassRange(I * Step, (I + 1) * Step, Fn); });
|
||||
parallelFor(0, NumShards, [&](size_t I) {
|
||||
forEachClassRange(I * Step, (I + 1) * Step, Fn);
|
||||
});
|
||||
forEachClassRange(Step * NumShards, Sections.size(), Fn);
|
||||
++Cnt;
|
||||
}
|
||||
|
|
@ -332,20 +335,20 @@ void ICF<ELFT>::forEachClass(std::function<void(size_t, size_t)> Fn) {
|
|||
// The main function of ICF.
|
||||
template <class ELFT> void ICF<ELFT>::run() {
|
||||
// Collect sections to merge.
|
||||
for (InputSectionBase<ELFT> *Sec : Symtab<ELFT>::X->Sections)
|
||||
if (auto *S = dyn_cast<InputSection<ELFT>>(Sec))
|
||||
for (InputSectionBase *Sec : InputSections)
|
||||
if (auto *S = dyn_cast<InputSection>(Sec))
|
||||
if (isEligible(S))
|
||||
Sections.push_back(S);
|
||||
|
||||
// Initially, we use hash values to partition sections.
|
||||
for (InputSection<ELFT> *S : Sections)
|
||||
for (InputSection *S : Sections)
|
||||
// Set MSB to 1 to avoid collisions with non-hash IDs.
|
||||
S->Class[0] = getHash(S) | (1 << 31);
|
||||
S->Class[0] = getHash<ELFT>(S) | (1 << 31);
|
||||
|
||||
// From now on, sections in Sections vector are ordered so that sections
|
||||
// in the same equivalence class are consecutive in the vector.
|
||||
std::stable_sort(Sections.begin(), Sections.end(),
|
||||
[](InputSection<ELFT> *A, InputSection<ELFT> *B) {
|
||||
[](InputSection *A, InputSection *B) {
|
||||
return A->Class[0] < B->Class[0];
|
||||
});
|
||||
|
||||
|
|
@ -372,6 +375,15 @@ template <class ELFT> void ICF<ELFT>::run() {
|
|||
Sections[Begin]->replace(Sections[I]);
|
||||
}
|
||||
});
|
||||
|
||||
// Mark ARM Exception Index table sections that refer to folded code
|
||||
// sections as not live. These sections have an implict dependency
|
||||
// via the link order dependency.
|
||||
if (Config->EMachine == EM_ARM)
|
||||
for (InputSectionBase *Sec : InputSections)
|
||||
if (auto *S = dyn_cast<InputSection>(Sec))
|
||||
if (S->Flags & SHF_LINK_ORDER)
|
||||
S->Live = S->getLinkOrderDep()->Live;
|
||||
}
|
||||
|
||||
// ICF entry point function.
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@
|
|||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/Bitcode/BitcodeReader.h"
|
||||
#include "llvm/CodeGen/Analysis.h"
|
||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
|
|
@ -38,6 +37,8 @@ using namespace lld::elf;
|
|||
|
||||
TarWriter *elf::Tar;
|
||||
|
||||
InputFile::InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
|
||||
|
||||
namespace {
|
||||
// In ELF object file all section addresses are zero. If we have multiple
|
||||
// .text sections (when using -ffunction-section or comdat group) then
|
||||
|
|
@ -56,14 +57,13 @@ public:
|
|||
}
|
||||
|
||||
Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
|
||||
if (Config->Verbose)
|
||||
outs() << Path << "\n";
|
||||
|
||||
log(Path);
|
||||
auto MBOrErr = MemoryBuffer::getFile(Path);
|
||||
if (auto EC = MBOrErr.getError()) {
|
||||
error(EC, "cannot open " + Path);
|
||||
error("cannot open " + Path + ": " + EC.message());
|
||||
return None;
|
||||
}
|
||||
|
||||
std::unique_ptr<MemoryBuffer> &MB = *MBOrErr;
|
||||
MemoryBufferRef MBRef = MB->getMemBufferRef();
|
||||
make<std::unique_ptr<MemoryBuffer>>(std::move(MB)); // take MB ownership
|
||||
|
|
@ -75,15 +75,13 @@ Optional<MemoryBufferRef> elf::readFile(StringRef Path) {
|
|||
|
||||
template <class ELFT> void elf::ObjectFile<ELFT>::initializeDwarfLine() {
|
||||
std::unique_ptr<object::ObjectFile> Obj =
|
||||
check(object::ObjectFile::createObjectFile(this->MB),
|
||||
"createObjectFile failed");
|
||||
check(object::ObjectFile::createObjectFile(this->MB), toString(this));
|
||||
|
||||
ObjectInfo ObjInfo;
|
||||
DWARFContextInMemory Dwarf(*Obj, &ObjInfo);
|
||||
DwarfLine.reset(new DWARFDebugLine(&Dwarf.getLineSection().Relocs));
|
||||
DataExtractor LineData(Dwarf.getLineSection().Data,
|
||||
ELFT::TargetEndianness == support::little,
|
||||
ELFT::Is64Bits ? 8 : 4);
|
||||
DataExtractor LineData(Dwarf.getLineSection().Data, Config->IsLE,
|
||||
Config->Wordsize);
|
||||
|
||||
// The second parameter is offset in .debug_line section
|
||||
// for compilation unit (CU) of interest. We have only one
|
||||
|
|
@ -94,34 +92,49 @@ template <class ELFT> void elf::ObjectFile<ELFT>::initializeDwarfLine() {
|
|||
// Returns source line information for a given offset
|
||||
// using DWARF debug info.
|
||||
template <class ELFT>
|
||||
std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase<ELFT> *S,
|
||||
uintX_t Offset) {
|
||||
Optional<DILineInfo> elf::ObjectFile<ELFT>::getDILineInfo(InputSectionBase *S,
|
||||
uint64_t Offset) {
|
||||
if (!DwarfLine)
|
||||
initializeDwarfLine();
|
||||
|
||||
// The offset to CU is 0.
|
||||
const DWARFDebugLine::LineTable *Tbl = DwarfLine->getLineTable(0);
|
||||
if (!Tbl)
|
||||
return "";
|
||||
return None;
|
||||
|
||||
// Use fake address calcuated by adding section file offset and offset in
|
||||
// section. See comments for ObjectInfo class.
|
||||
DILineInfo Info;
|
||||
Tbl->getFileLineInfoForAddress(
|
||||
S->Offset + Offset, nullptr,
|
||||
S->getOffsetInFile() + Offset, nullptr,
|
||||
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, Info);
|
||||
if (Info.Line == 0)
|
||||
return "";
|
||||
return Info.FileName + ":" + std::to_string(Info.Line);
|
||||
return None;
|
||||
return Info;
|
||||
}
|
||||
|
||||
// Returns source line information for a given offset
|
||||
// using DWARF debug info.
|
||||
template <class ELFT>
|
||||
std::string elf::ObjectFile<ELFT>::getLineInfo(InputSectionBase *S,
|
||||
uint64_t Offset) {
|
||||
if (Optional<DILineInfo> Info = getDILineInfo(S, Offset))
|
||||
return Info->FileName + ":" + std::to_string(Info->Line);
|
||||
return "";
|
||||
}
|
||||
|
||||
// Returns "(internal)", "foo.a(bar.o)" or "baz.o".
|
||||
std::string lld::toString(const InputFile *F) {
|
||||
if (!F)
|
||||
return "(internal)";
|
||||
if (!F->ArchiveName.empty())
|
||||
return (F->ArchiveName + "(" + F->getName() + ")").str();
|
||||
return F->getName();
|
||||
|
||||
if (F->ToStringCache.empty()) {
|
||||
if (F->ArchiveName.empty())
|
||||
F->ToStringCache = F->getName();
|
||||
else
|
||||
F->ToStringCache = (F->ArchiveName + "(" + F->getName() + ")").str();
|
||||
}
|
||||
return F->ToStringCache;
|
||||
}
|
||||
|
||||
template <class ELFT> static ELFKind getELFKind() {
|
||||
|
|
@ -144,29 +157,26 @@ typename ELFT::SymRange ELFFileBase<ELFT>::getGlobalSymbols() {
|
|||
|
||||
template <class ELFT>
|
||||
uint32_t ELFFileBase<ELFT>::getSectionIndex(const Elf_Sym &Sym) const {
|
||||
return check(getObj().getSectionIndex(&Sym, Symbols, SymtabSHNDX));
|
||||
return check(getObj().getSectionIndex(&Sym, Symbols, SymtabSHNDX),
|
||||
toString(this));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ELFFileBase<ELFT>::initSymtab(ArrayRef<Elf_Shdr> Sections,
|
||||
const Elf_Shdr *Symtab) {
|
||||
FirstNonLocal = Symtab->sh_info;
|
||||
Symbols = check(getObj().symbols(Symtab));
|
||||
Symbols = check(getObj().symbols(Symtab), toString(this));
|
||||
if (FirstNonLocal == 0 || FirstNonLocal > Symbols.size())
|
||||
fatal(toString(this) + ": invalid sh_info in symbol table");
|
||||
|
||||
StringTable = check(getObj().getStringTableForSymtab(*Symtab, Sections));
|
||||
StringTable = check(getObj().getStringTableForSymtab(*Symtab, Sections),
|
||||
toString(this));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
elf::ObjectFile<ELFT>::ObjectFile(MemoryBufferRef M)
|
||||
: ELFFileBase<ELFT>(Base::ObjectKind, M) {}
|
||||
|
||||
template <class ELFT>
|
||||
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getNonLocalSymbols() {
|
||||
return makeArrayRef(this->SymbolBodies).slice(this->FirstNonLocal);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
ArrayRef<SymbolBody *> elf::ObjectFile<ELFT>::getLocalSymbols() {
|
||||
if (this->SymbolBodies.empty())
|
||||
|
|
@ -196,19 +206,20 @@ StringRef
|
|||
elf::ObjectFile<ELFT>::getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
|
||||
const Elf_Shdr &Sec) {
|
||||
if (this->Symbols.empty())
|
||||
this->initSymtab(Sections,
|
||||
check(object::getSection<ELFT>(Sections, Sec.sh_link)));
|
||||
const Elf_Sym *Sym =
|
||||
check(object::getSymbol<ELFT>(this->Symbols, Sec.sh_info));
|
||||
return check(Sym->getName(this->StringTable));
|
||||
this->initSymtab(
|
||||
Sections,
|
||||
check(object::getSection<ELFT>(Sections, Sec.sh_link), toString(this)));
|
||||
const Elf_Sym *Sym = check(
|
||||
object::getSymbol<ELFT>(this->Symbols, Sec.sh_info), toString(this));
|
||||
return check(Sym->getName(this->StringTable), toString(this));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
ArrayRef<typename elf::ObjectFile<ELFT>::Elf_Word>
|
||||
elf::ObjectFile<ELFT>::getShtGroupEntries(const Elf_Shdr &Sec) {
|
||||
const ELFFile<ELFT> &Obj = this->getObj();
|
||||
ArrayRef<Elf_Word> Entries =
|
||||
check(Obj.template getSectionContentsAsArray<Elf_Word>(&Sec));
|
||||
ArrayRef<Elf_Word> Entries = check(
|
||||
Obj.template getSectionContentsAsArray<Elf_Word>(&Sec), toString(this));
|
||||
if (Entries.empty() || Entries[0] != GRP_COMDAT)
|
||||
fatal(toString(this) + ": unsupported SHT_GROUP format");
|
||||
return Entries.slice(1);
|
||||
|
|
@ -242,14 +253,14 @@ bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
|
|||
// the section does not hold a table of fixed-size entries". We know
|
||||
// that Rust 1.13 produces a string mergeable section with a zero
|
||||
// sh_entsize. Here we just accept it rather than being picky about it.
|
||||
uintX_t EntSize = Sec.sh_entsize;
|
||||
uint64_t EntSize = Sec.sh_entsize;
|
||||
if (EntSize == 0)
|
||||
return false;
|
||||
if (Sec.sh_size % EntSize)
|
||||
fatal(toString(this) +
|
||||
": SHF_MERGE section size must be a multiple of sh_entsize");
|
||||
|
||||
uintX_t Flags = Sec.sh_flags;
|
||||
uint64_t Flags = Sec.sh_flags;
|
||||
if (!(Flags & SHF_MERGE))
|
||||
return false;
|
||||
if (Flags & SHF_WRITE)
|
||||
|
|
@ -270,76 +281,79 @@ bool elf::ObjectFile<ELFT>::shouldMerge(const Elf_Shdr &Sec) {
|
|||
template <class ELFT>
|
||||
void elf::ObjectFile<ELFT>::initializeSections(
|
||||
DenseSet<CachedHashStringRef> &ComdatGroups) {
|
||||
ArrayRef<Elf_Shdr> ObjSections = check(this->getObj().sections());
|
||||
ArrayRef<Elf_Shdr> ObjSections =
|
||||
check(this->getObj().sections(), toString(this));
|
||||
const ELFFile<ELFT> &Obj = this->getObj();
|
||||
uint64_t Size = ObjSections.size();
|
||||
Sections.resize(Size);
|
||||
this->Sections.resize(Size);
|
||||
unsigned I = -1;
|
||||
StringRef SectionStringTable = check(Obj.getSectionStringTable(ObjSections));
|
||||
StringRef SectionStringTable =
|
||||
check(Obj.getSectionStringTable(ObjSections), toString(this));
|
||||
for (const Elf_Shdr &Sec : ObjSections) {
|
||||
++I;
|
||||
if (Sections[I] == &InputSection<ELFT>::Discarded)
|
||||
if (this->Sections[I] == &InputSection::Discarded)
|
||||
continue;
|
||||
|
||||
// SHF_EXCLUDE'ed sections are discarded by the linker. However,
|
||||
// if -r is given, we'll let the final link discard such sections.
|
||||
// This is compatible with GNU.
|
||||
if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) {
|
||||
Sections[I] = &InputSection<ELFT>::Discarded;
|
||||
this->Sections[I] = &InputSection::Discarded;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (Sec.sh_type) {
|
||||
case SHT_GROUP:
|
||||
Sections[I] = &InputSection<ELFT>::Discarded;
|
||||
if (ComdatGroups.insert(CachedHashStringRef(
|
||||
getShtGroupSignature(ObjSections, Sec)))
|
||||
this->Sections[I] = &InputSection::Discarded;
|
||||
if (ComdatGroups
|
||||
.insert(
|
||||
CachedHashStringRef(getShtGroupSignature(ObjSections, Sec)))
|
||||
.second)
|
||||
continue;
|
||||
for (uint32_t SecIndex : getShtGroupEntries(Sec)) {
|
||||
if (SecIndex >= Size)
|
||||
fatal(toString(this) + ": invalid section index in group: " +
|
||||
Twine(SecIndex));
|
||||
Sections[SecIndex] = &InputSection<ELFT>::Discarded;
|
||||
fatal(toString(this) +
|
||||
": invalid section index in group: " + Twine(SecIndex));
|
||||
this->Sections[SecIndex] = &InputSection::Discarded;
|
||||
}
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
this->initSymtab(ObjSections, &Sec);
|
||||
break;
|
||||
case SHT_SYMTAB_SHNDX:
|
||||
this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec, ObjSections));
|
||||
this->SymtabSHNDX =
|
||||
check(Obj.getSHNDXTable(Sec, ObjSections), toString(this));
|
||||
break;
|
||||
case SHT_STRTAB:
|
||||
case SHT_NULL:
|
||||
break;
|
||||
default:
|
||||
Sections[I] = createInputSection(Sec, SectionStringTable);
|
||||
this->Sections[I] = createInputSection(Sec, SectionStringTable);
|
||||
}
|
||||
|
||||
// .ARM.exidx sections have a reverse dependency on the InputSection they
|
||||
// have a SHF_LINK_ORDER dependency, this is identified by the sh_link.
|
||||
if (Sec.sh_flags & SHF_LINK_ORDER) {
|
||||
if (Sec.sh_link >= Sections.size())
|
||||
if (Sec.sh_link >= this->Sections.size())
|
||||
fatal(toString(this) + ": invalid sh_link index: " +
|
||||
Twine(Sec.sh_link));
|
||||
auto *IS = cast<InputSection<ELFT>>(Sections[Sec.sh_link]);
|
||||
IS->DependentSection = Sections[I];
|
||||
this->Sections[Sec.sh_link]->DependentSections.push_back(
|
||||
this->Sections[I]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase<ELFT> *
|
||||
elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
|
||||
InputSectionBase *elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
|
||||
uint32_t Idx = Sec.sh_info;
|
||||
if (Idx >= Sections.size())
|
||||
if (Idx >= this->Sections.size())
|
||||
fatal(toString(this) + ": invalid relocated section index: " + Twine(Idx));
|
||||
InputSectionBase<ELFT> *Target = Sections[Idx];
|
||||
InputSectionBase *Target = this->Sections[Idx];
|
||||
|
||||
// Strictly speaking, a relocation section must be included in the
|
||||
// group of the section it relocates. However, LLVM 3.3 and earlier
|
||||
// would fail to do so, so we gracefully handle that case.
|
||||
if (Target == &InputSection<ELFT>::Discarded)
|
||||
if (Target == &InputSection::Discarded)
|
||||
return nullptr;
|
||||
|
||||
if (!Target)
|
||||
|
|
@ -348,11 +362,11 @@ elf::ObjectFile<ELFT>::getRelocTarget(const Elf_Shdr &Sec) {
|
|||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase<ELFT> *
|
||||
InputSectionBase *
|
||||
elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec,
|
||||
StringRef SectionStringTable) {
|
||||
StringRef Name =
|
||||
check(this->getObj().getSectionName(&Sec, SectionStringTable));
|
||||
StringRef Name = check(
|
||||
this->getObj().getSectionName(&Sec, SectionStringTable), toString(this));
|
||||
|
||||
switch (Sec.sh_type) {
|
||||
case SHT_ARM_ATTRIBUTES:
|
||||
|
|
@ -361,62 +375,91 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec,
|
|||
// attribute section for dlopen to work.
|
||||
// In a full implementation we would merge all attribute sections.
|
||||
if (In<ELFT>::ARMAttributes == nullptr) {
|
||||
In<ELFT>::ARMAttributes = make<InputSection<ELFT>>(this, &Sec, Name);
|
||||
In<ELFT>::ARMAttributes = make<InputSection>(this, &Sec, Name);
|
||||
return In<ELFT>::ARMAttributes;
|
||||
}
|
||||
return &InputSection<ELFT>::Discarded;
|
||||
return &InputSection::Discarded;
|
||||
case SHT_RELA:
|
||||
case SHT_REL: {
|
||||
// Find the relocation target section and associate this
|
||||
// section with it. Target can be discarded, for example
|
||||
// if it is a duplicated member of SHT_GROUP section, we
|
||||
// do not create or proccess relocatable sections then.
|
||||
InputSectionBase *Target = getRelocTarget(Sec);
|
||||
if (!Target)
|
||||
return nullptr;
|
||||
|
||||
// This section contains relocation information.
|
||||
// If -r is given, we do not interpret or apply relocation
|
||||
// but just copy relocation sections to output.
|
||||
if (Config->Relocatable)
|
||||
return make<InputSection<ELFT>>(this, &Sec, Name);
|
||||
return make<InputSection>(this, &Sec, Name);
|
||||
|
||||
// Find the relocation target section and associate this
|
||||
// section with it.
|
||||
InputSectionBase<ELFT> *Target = getRelocTarget(Sec);
|
||||
if (!Target)
|
||||
return nullptr;
|
||||
if (Target->FirstRelocation)
|
||||
fatal(toString(this) +
|
||||
": multiple relocation sections to one section are not supported");
|
||||
if (!isa<InputSection<ELFT>>(Target) && !isa<EhInputSection<ELFT>>(Target))
|
||||
if (isa<MergeInputSection>(Target))
|
||||
fatal(toString(this) +
|
||||
": relocations pointing to SHF_MERGE are not supported");
|
||||
|
||||
size_t NumRelocations;
|
||||
if (Sec.sh_type == SHT_RELA) {
|
||||
ArrayRef<Elf_Rela> Rels = check(this->getObj().relas(&Sec));
|
||||
ArrayRef<Elf_Rela> Rels =
|
||||
check(this->getObj().relas(&Sec), toString(this));
|
||||
Target->FirstRelocation = Rels.begin();
|
||||
NumRelocations = Rels.size();
|
||||
Target->AreRelocsRela = true;
|
||||
} else {
|
||||
ArrayRef<Elf_Rel> Rels = check(this->getObj().rels(&Sec));
|
||||
ArrayRef<Elf_Rel> Rels = check(this->getObj().rels(&Sec), toString(this));
|
||||
Target->FirstRelocation = Rels.begin();
|
||||
NumRelocations = Rels.size();
|
||||
Target->AreRelocsRela = false;
|
||||
}
|
||||
assert(isUInt<31>(NumRelocations));
|
||||
Target->NumRelocations = NumRelocations;
|
||||
|
||||
// Relocation sections processed by the linker are usually removed
|
||||
// from the output, so returning `nullptr` for the normal case.
|
||||
// However, if -emit-relocs is given, we need to leave them in the output.
|
||||
// (Some post link analysis tools need this information.)
|
||||
if (Config->EmitRelocs) {
|
||||
InputSection *RelocSec = make<InputSection>(this, &Sec, Name);
|
||||
// We will not emit relocation section if target was discarded.
|
||||
Target->DependentSections.push_back(RelocSec);
|
||||
return RelocSec;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// .note.GNU-stack is a marker section to control the presence of
|
||||
// PT_GNU_STACK segment in outputs. Since the presence of the segment
|
||||
// is controlled only by the command line option (-z execstack) in LLD,
|
||||
// .note.GNU-stack is ignored.
|
||||
// The GNU linker uses .note.GNU-stack section as a marker indicating
|
||||
// that the code in the object file does not expect that the stack is
|
||||
// executable (in terms of NX bit). If all input files have the marker,
|
||||
// the GNU linker adds a PT_GNU_STACK segment to tells the loader to
|
||||
// make the stack non-executable. Most object files have this section as
|
||||
// of 2017.
|
||||
//
|
||||
// But making the stack non-executable is a norm today for security
|
||||
// reasons. Failure to do so may result in a serious security issue.
|
||||
// Therefore, we make LLD always add PT_GNU_STACK unless it is
|
||||
// explicitly told to do otherwise (by -z execstack). Because the stack
|
||||
// executable-ness is controlled solely by command line options,
|
||||
// .note.GNU-stack sections are simply ignored.
|
||||
if (Name == ".note.GNU-stack")
|
||||
return &InputSection<ELFT>::Discarded;
|
||||
return &InputSection::Discarded;
|
||||
|
||||
// Split stacks is a feature to support a discontiguous stack. At least
|
||||
// as of 2017, it seems that the feature is not being used widely.
|
||||
// Only GNU gold supports that. We don't. For the details about that,
|
||||
// see https://gcc.gnu.org/wiki/SplitStacks
|
||||
if (Name == ".note.GNU-split-stack") {
|
||||
error("objects using splitstacks are not supported");
|
||||
return &InputSection<ELFT>::Discarded;
|
||||
error(toString(this) +
|
||||
": object file compiled with -fsplit-stack is not supported");
|
||||
return &InputSection::Discarded;
|
||||
}
|
||||
|
||||
if (Config->Strip != StripPolicy::None && Name.startswith(".debug"))
|
||||
return &InputSection<ELFT>::Discarded;
|
||||
return &InputSection::Discarded;
|
||||
|
||||
// The linkonce feature is a sort of proto-comdat. Some glibc i386 object
|
||||
// files contain definitions of symbol "__x86.get_pc_thunk.bx" in linkonce
|
||||
|
|
@ -424,17 +467,17 @@ elf::ObjectFile<ELFT>::createInputSection(const Elf_Shdr &Sec,
|
|||
// FIXME: This is glibc PR20543, we should remove this hack once that has been
|
||||
// fixed for a while.
|
||||
if (Name.startswith(".gnu.linkonce."))
|
||||
return &InputSection<ELFT>::Discarded;
|
||||
return &InputSection::Discarded;
|
||||
|
||||
// The linker merges EH (exception handling) frames and creates a
|
||||
// .eh_frame_hdr section for runtime. So we handle them with a special
|
||||
// class. For relocatable outputs, they are just passed through.
|
||||
if (Name == ".eh_frame" && !Config->Relocatable)
|
||||
return make<EhInputSection<ELFT>>(this, &Sec, Name);
|
||||
return make<EhInputSection>(this, &Sec, Name);
|
||||
|
||||
if (shouldMerge(Sec))
|
||||
return make<MergeInputSection<ELFT>>(this, &Sec, Name);
|
||||
return make<InputSection<ELFT>>(this, &Sec, Name);
|
||||
return make<MergeInputSection>(this, &Sec, Name);
|
||||
return make<InputSection>(this, &Sec, Name);
|
||||
}
|
||||
|
||||
template <class ELFT> void elf::ObjectFile<ELFT>::initializeSymbols() {
|
||||
|
|
@ -444,12 +487,11 @@ template <class ELFT> void elf::ObjectFile<ELFT>::initializeSymbols() {
|
|||
}
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase<ELFT> *
|
||||
elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
|
||||
InputSectionBase *elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
|
||||
uint32_t Index = this->getSectionIndex(Sym);
|
||||
if (Index >= Sections.size())
|
||||
if (Index >= this->Sections.size())
|
||||
fatal(toString(this) + ": invalid section index: " + Twine(Index));
|
||||
InputSectionBase<ELFT> *S = Sections[Index];
|
||||
InputSectionBase *S = this->Sections[Index];
|
||||
|
||||
// We found that GNU assembler 2.17.50 [FreeBSD] 2007-07-03 could
|
||||
// generate broken objects. STT_SECTION/STT_NOTYPE symbols can be
|
||||
|
|
@ -463,7 +505,7 @@ elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
|
|||
fatal(toString(this) + ": invalid section index: " + Twine(Index));
|
||||
}
|
||||
|
||||
if (S == &InputSection<ELFT>::Discarded)
|
||||
if (S == &InputSection::Discarded)
|
||||
return S;
|
||||
return S->Repl;
|
||||
}
|
||||
|
|
@ -471,30 +513,29 @@ elf::ObjectFile<ELFT>::getSection(const Elf_Sym &Sym) const {
|
|||
template <class ELFT>
|
||||
SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
|
||||
int Binding = Sym->getBinding();
|
||||
InputSectionBase<ELFT> *Sec = getSection(*Sym);
|
||||
InputSectionBase *Sec = getSection(*Sym);
|
||||
|
||||
uint8_t StOther = Sym->st_other;
|
||||
uint8_t Type = Sym->getType();
|
||||
uintX_t Value = Sym->st_value;
|
||||
uintX_t Size = Sym->st_size;
|
||||
uint64_t Value = Sym->st_value;
|
||||
uint64_t Size = Sym->st_size;
|
||||
|
||||
if (Binding == STB_LOCAL) {
|
||||
if (Sym->getType() == STT_FILE)
|
||||
SourceFile = check(Sym->getName(this->StringTable));
|
||||
SourceFile = check(Sym->getName(this->StringTable), toString(this));
|
||||
|
||||
if (this->StringTable.size() <= Sym->st_name)
|
||||
fatal(toString(this) + ": invalid symbol name offset");
|
||||
|
||||
StringRefZ Name = this->StringTable.data() + Sym->st_name;
|
||||
if (Sym->st_shndx == SHN_UNDEF)
|
||||
return new (BAlloc)
|
||||
Undefined<ELFT>(Name, /*IsLocal=*/true, StOther, Type, this);
|
||||
return make<Undefined>(Name, /*IsLocal=*/true, StOther, Type, this);
|
||||
|
||||
return new (BAlloc) DefinedRegular<ELFT>(Name, /*IsLocal=*/true, StOther,
|
||||
Type, Value, Size, Sec, this);
|
||||
return make<DefinedRegular>(Name, /*IsLocal=*/true, StOther, Type, Value,
|
||||
Size, Sec, this);
|
||||
}
|
||||
|
||||
StringRef Name = check(Sym->getName(this->StringTable));
|
||||
StringRef Name = check(Sym->getName(this->StringTable), toString(this));
|
||||
|
||||
switch (Sym->st_shndx) {
|
||||
case SHN_UNDEF:
|
||||
|
|
@ -517,7 +558,7 @@ SymbolBody *elf::ObjectFile<ELFT>::createSymbolBody(const Elf_Sym *Sym) {
|
|||
case STB_GLOBAL:
|
||||
case STB_WEAK:
|
||||
case STB_GNU_UNIQUE:
|
||||
if (Sec == &InputSection<ELFT>::Discarded)
|
||||
if (Sec == &InputSection::Discarded)
|
||||
return elf::Symtab<ELFT>::X
|
||||
->addUndefined(Name, /*IsLocal=*/false, Binding, StOther, Type,
|
||||
/*CanOmitFromDynSym=*/false, this)
|
||||
|
|
@ -533,27 +574,34 @@ template <class ELFT> void ArchiveFile::parse() {
|
|||
MB.getBufferIdentifier() + ": failed to parse archive");
|
||||
|
||||
// Read the symbol table to construct Lazy objects.
|
||||
for (const Archive::Symbol &Sym : File->symbols())
|
||||
for (const Archive::Symbol &Sym : File->symbols()) {
|
||||
Symtab<ELFT>::X->addLazyArchive(this, Sym);
|
||||
}
|
||||
|
||||
if (File->symbols().begin() == File->symbols().end())
|
||||
Config->ArchiveWithoutSymbolsSeen = true;
|
||||
}
|
||||
|
||||
// Returns a buffer pointing to a member file containing a given symbol.
|
||||
std::pair<MemoryBufferRef, uint64_t>
|
||||
ArchiveFile::getMember(const Archive::Symbol *Sym) {
|
||||
Archive::Child C =
|
||||
check(Sym->getMember(),
|
||||
"could not get the member for symbol " + Sym->getName());
|
||||
check(Sym->getMember(), toString(this) +
|
||||
": could not get the member for symbol " +
|
||||
Sym->getName());
|
||||
|
||||
if (!Seen.insert(C.getChildOffset()).second)
|
||||
return {MemoryBufferRef(), 0};
|
||||
|
||||
MemoryBufferRef Ret =
|
||||
check(C.getMemoryBufferRef(),
|
||||
"could not get the buffer for the member defining symbol " +
|
||||
toString(this) +
|
||||
": could not get the buffer for the member defining symbol " +
|
||||
Sym->getName());
|
||||
|
||||
if (C.getParent()->isThin() && Tar)
|
||||
Tar->append(relativeToRoot(check(C.getFullName())), Ret.getBuffer());
|
||||
Tar->append(relativeToRoot(check(C.getFullName(), toString(this))),
|
||||
Ret.getBuffer());
|
||||
if (C.getParent()->isThin())
|
||||
return {Ret, 0};
|
||||
return {Ret, C.getChildOffset()};
|
||||
|
|
@ -567,16 +615,24 @@ template <class ELFT>
|
|||
const typename ELFT::Shdr *
|
||||
SharedFile<ELFT>::getSection(const Elf_Sym &Sym) const {
|
||||
return check(
|
||||
this->getObj().getSection(&Sym, this->Symbols, this->SymtabSHNDX));
|
||||
this->getObj().getSection(&Sym, this->Symbols, this->SymtabSHNDX),
|
||||
toString(this));
|
||||
}
|
||||
|
||||
template <class ELFT> StringRef SharedFile<ELFT>::getSoName() const {
|
||||
if (SoName.empty())
|
||||
return this->DefaultSoName;
|
||||
return SoName;
|
||||
}
|
||||
|
||||
// Partially parse the shared object file so that we can call
|
||||
// getSoName on this object.
|
||||
template <class ELFT> void SharedFile<ELFT>::parseSoName() {
|
||||
const Elf_Shdr *DynamicSec = nullptr;
|
||||
|
||||
const ELFFile<ELFT> Obj = this->getObj();
|
||||
ArrayRef<Elf_Shdr> Sections = check(Obj.sections());
|
||||
ArrayRef<Elf_Shdr> Sections = check(Obj.sections(), toString(this));
|
||||
|
||||
// Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
|
||||
for (const Elf_Shdr &Sec : Sections) {
|
||||
switch (Sec.sh_type) {
|
||||
default:
|
||||
|
|
@ -588,7 +644,8 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
|
|||
DynamicSec = &Sec;
|
||||
break;
|
||||
case SHT_SYMTAB_SHNDX:
|
||||
this->SymtabSHNDX = check(Obj.getSHNDXTable(Sec, Sections));
|
||||
this->SymtabSHNDX =
|
||||
check(Obj.getSHNDXTable(Sec, Sections), toString(this));
|
||||
break;
|
||||
case SHT_GNU_versym:
|
||||
this->VersymSec = &Sec;
|
||||
|
|
@ -602,20 +659,15 @@ template <class ELFT> void SharedFile<ELFT>::parseSoName() {
|
|||
if (this->VersymSec && this->Symbols.empty())
|
||||
error("SHT_GNU_versym should be associated with symbol table");
|
||||
|
||||
// DSOs are identified by soname, and they usually contain
|
||||
// DT_SONAME tag in their header. But if they are missing,
|
||||
// filenames are used as default sonames.
|
||||
SoName = sys::path::filename(this->getName());
|
||||
|
||||
// Search for a DT_SONAME tag to initialize this->SoName.
|
||||
if (!DynamicSec)
|
||||
return;
|
||||
|
||||
ArrayRef<Elf_Dyn> Arr =
|
||||
check(Obj.template getSectionContentsAsArray<Elf_Dyn>(DynamicSec),
|
||||
toString(this) + ": getSectionContentsAsArray failed");
|
||||
toString(this));
|
||||
for (const Elf_Dyn &Dyn : Arr) {
|
||||
if (Dyn.d_tag == DT_SONAME) {
|
||||
uintX_t Val = Dyn.getVal();
|
||||
uint64_t Val = Dyn.getVal();
|
||||
if (Val >= this->StringTable.size())
|
||||
fatal(toString(this) + ": invalid DT_SONAME entry");
|
||||
SoName = StringRef(this->StringTable.data() + Val);
|
||||
|
|
@ -681,7 +733,7 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
|
|||
bool Hidden = VersymIndex & VERSYM_HIDDEN;
|
||||
VersymIndex = VersymIndex & ~VERSYM_HIDDEN;
|
||||
|
||||
StringRef Name = check(Sym.getName(this->StringTable));
|
||||
StringRef Name = check(Sym.getName(this->StringTable), toString(this));
|
||||
if (Sym.isUndefined()) {
|
||||
Undefs.push_back(Name);
|
||||
continue;
|
||||
|
|
@ -707,19 +759,18 @@ template <class ELFT> void SharedFile<ELFT>::parseRest() {
|
|||
}
|
||||
}
|
||||
|
||||
static ELFKind getBitcodeELFKind(MemoryBufferRef MB) {
|
||||
Triple T(check(getBitcodeTargetTriple(MB)));
|
||||
static ELFKind getBitcodeELFKind(const Triple &T) {
|
||||
if (T.isLittleEndian())
|
||||
return T.isArch64Bit() ? ELF64LEKind : ELF32LEKind;
|
||||
return T.isArch64Bit() ? ELF64BEKind : ELF32BEKind;
|
||||
}
|
||||
|
||||
static uint8_t getBitcodeMachineKind(MemoryBufferRef MB) {
|
||||
Triple T(check(getBitcodeTargetTriple(MB)));
|
||||
static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) {
|
||||
switch (T.getArch()) {
|
||||
case Triple::aarch64:
|
||||
return EM_AARCH64;
|
||||
case Triple::arm:
|
||||
case Triple::thumb:
|
||||
return EM_ARM;
|
||||
case Triple::mips:
|
||||
case Triple::mipsel:
|
||||
|
|
@ -735,14 +786,32 @@ static uint8_t getBitcodeMachineKind(MemoryBufferRef MB) {
|
|||
case Triple::x86_64:
|
||||
return EM_X86_64;
|
||||
default:
|
||||
fatal(MB.getBufferIdentifier() +
|
||||
": could not infer e_machine from bitcode target triple " + T.str());
|
||||
fatal(Path + ": could not infer e_machine from bitcode target triple " +
|
||||
T.str());
|
||||
}
|
||||
}
|
||||
|
||||
BitcodeFile::BitcodeFile(MemoryBufferRef MB) : InputFile(BitcodeKind, MB) {
|
||||
EKind = getBitcodeELFKind(MB);
|
||||
EMachine = getBitcodeMachineKind(MB);
|
||||
BitcodeFile::BitcodeFile(MemoryBufferRef MB, StringRef ArchiveName,
|
||||
uint64_t OffsetInArchive)
|
||||
: InputFile(BitcodeKind, MB) {
|
||||
this->ArchiveName = ArchiveName;
|
||||
|
||||
// Here we pass a new MemoryBufferRef which is identified by ArchiveName
|
||||
// (the fully resolved path of the archive) + member name + offset of the
|
||||
// member in the archive.
|
||||
// ThinLTO uses the MemoryBufferRef identifier to access its internal
|
||||
// data structures and if two archives define two members with the same name,
|
||||
// this causes a collision which result in only one of the objects being
|
||||
// taken into consideration at LTO time (which very likely causes undefined
|
||||
// symbols later in the link stage).
|
||||
MemoryBufferRef MBRef(MB.getBuffer(),
|
||||
Saver.save(ArchiveName + MB.getBufferIdentifier() +
|
||||
utostr(OffsetInArchive)));
|
||||
Obj = check(lto::InputFile::create(MBRef), toString(this));
|
||||
|
||||
Triple T(Obj->getTargetTriple());
|
||||
EKind = getBitcodeELFKind(T);
|
||||
EMachine = getBitcodeMachineKind(MB.getBufferIdentifier(), T);
|
||||
}
|
||||
|
||||
static uint8_t mapVisibility(GlobalValue::VisibilityTypes GvVisibility) {
|
||||
|
|
@ -762,25 +831,24 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
|
|||
const lto::InputFile::Symbol &ObjSym,
|
||||
BitcodeFile *F) {
|
||||
StringRef NameRef = Saver.save(ObjSym.getName());
|
||||
uint32_t Flags = ObjSym.getFlags();
|
||||
uint32_t Binding = (Flags & BasicSymbolRef::SF_Weak) ? STB_WEAK : STB_GLOBAL;
|
||||
uint32_t Binding = ObjSym.isWeak() ? STB_WEAK : STB_GLOBAL;
|
||||
|
||||
uint8_t Type = ObjSym.isTLS() ? STT_TLS : STT_NOTYPE;
|
||||
uint8_t Visibility = mapVisibility(ObjSym.getVisibility());
|
||||
bool CanOmitFromDynSym = ObjSym.canBeOmittedFromSymbolTable();
|
||||
|
||||
int C = check(ObjSym.getComdatIndex());
|
||||
int C = ObjSym.getComdatIndex();
|
||||
if (C != -1 && !KeptComdats[C])
|
||||
return Symtab<ELFT>::X->addUndefined(NameRef, /*IsLocal=*/false, Binding,
|
||||
Visibility, Type, CanOmitFromDynSym,
|
||||
F);
|
||||
|
||||
if (Flags & BasicSymbolRef::SF_Undefined)
|
||||
if (ObjSym.isUndefined())
|
||||
return Symtab<ELFT>::X->addUndefined(NameRef, /*IsLocal=*/false, Binding,
|
||||
Visibility, Type, CanOmitFromDynSym,
|
||||
F);
|
||||
|
||||
if (Flags & BasicSymbolRef::SF_Common)
|
||||
if (ObjSym.isCommon())
|
||||
return Symtab<ELFT>::X->addCommon(NameRef, ObjSym.getCommonSize(),
|
||||
ObjSym.getCommonAlignment(), Binding,
|
||||
Visibility, STT_OBJECT, F);
|
||||
|
|
@ -791,24 +859,9 @@ static Symbol *createBitcodeSymbol(const std::vector<bool> &KeptComdats,
|
|||
|
||||
template <class ELFT>
|
||||
void BitcodeFile::parse(DenseSet<CachedHashStringRef> &ComdatGroups) {
|
||||
|
||||
// Here we pass a new MemoryBufferRef which is identified by ArchiveName
|
||||
// (the fully resolved path of the archive) + member name + offset of the
|
||||
// member in the archive.
|
||||
// ThinLTO uses the MemoryBufferRef identifier to access its internal
|
||||
// data structures and if two archives define two members with the same name,
|
||||
// this causes a collision which result in only one of the objects being
|
||||
// taken into consideration at LTO time (which very likely causes undefined
|
||||
// symbols later in the link stage).
|
||||
Obj = check(lto::InputFile::create(MemoryBufferRef(
|
||||
MB.getBuffer(), Saver.save(ArchiveName + MB.getBufferIdentifier() +
|
||||
utostr(OffsetInArchive)))));
|
||||
|
||||
std::vector<bool> KeptComdats;
|
||||
for (StringRef S : Obj->getComdatTable()) {
|
||||
StringRef N = Saver.save(S);
|
||||
KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(N)).second);
|
||||
}
|
||||
for (StringRef S : Obj->getComdatTable())
|
||||
KeptComdats.push_back(ComdatGroups.insert(CachedHashStringRef(S)).second);
|
||||
|
||||
for (const lto::InputFile::Symbol &ObjSym : Obj->symbols())
|
||||
Symbols.push_back(createBitcodeSymbol<ELFT>(KeptComdats, ObjSym, this));
|
||||
|
|
@ -857,8 +910,8 @@ template <class ELFT> void BinaryFile::parse() {
|
|||
StringRef EndName = Saver.save(Twine(Filename) + "_end");
|
||||
StringRef SizeName = Saver.save(Twine(Filename) + "_size");
|
||||
|
||||
auto *Section = make<InputSection<ELFT>>(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
|
||||
8, Data, ".data");
|
||||
auto *Section =
|
||||
make<InputSection>(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, 8, Data, ".data");
|
||||
Sections.push_back(Section);
|
||||
|
||||
elf::Symtab<ELFT>::X->addRegular(StartName, STV_DEFAULT, STT_OBJECT, 0, 0,
|
||||
|
|
@ -878,10 +931,10 @@ static bool isBitcode(MemoryBufferRef MB) {
|
|||
|
||||
InputFile *elf::createObjectFile(MemoryBufferRef MB, StringRef ArchiveName,
|
||||
uint64_t OffsetInArchive) {
|
||||
InputFile *F =
|
||||
isBitcode(MB) ? make<BitcodeFile>(MB) : createELFFile<ObjectFile>(MB);
|
||||
InputFile *F = isBitcode(MB)
|
||||
? make<BitcodeFile>(MB, ArchiveName, OffsetInArchive)
|
||||
: createELFFile<ObjectFile>(MB);
|
||||
F->ArchiveName = ArchiveName;
|
||||
F->OffsetInArchive = OffsetInArchive;
|
||||
return F;
|
||||
}
|
||||
|
||||
|
|
@ -907,27 +960,31 @@ template <class ELFT> std::vector<StringRef> LazyObjectFile::getElfSymbols() {
|
|||
typedef typename ELFT::SymRange Elf_Sym_Range;
|
||||
|
||||
const ELFFile<ELFT> Obj(this->MB.getBuffer());
|
||||
ArrayRef<Elf_Shdr> Sections = check(Obj.sections());
|
||||
ArrayRef<Elf_Shdr> Sections = check(Obj.sections(), toString(this));
|
||||
for (const Elf_Shdr &Sec : Sections) {
|
||||
if (Sec.sh_type != SHT_SYMTAB)
|
||||
continue;
|
||||
Elf_Sym_Range Syms = check(Obj.symbols(&Sec));
|
||||
|
||||
Elf_Sym_Range Syms = check(Obj.symbols(&Sec), toString(this));
|
||||
uint32_t FirstNonLocal = Sec.sh_info;
|
||||
StringRef StringTable = check(Obj.getStringTableForSymtab(Sec, Sections));
|
||||
StringRef StringTable =
|
||||
check(Obj.getStringTableForSymtab(Sec, Sections), toString(this));
|
||||
std::vector<StringRef> V;
|
||||
|
||||
for (const Elf_Sym &Sym : Syms.slice(FirstNonLocal))
|
||||
if (Sym.st_shndx != SHN_UNDEF)
|
||||
V.push_back(check(Sym.getName(StringTable)));
|
||||
V.push_back(check(Sym.getName(StringTable), toString(this)));
|
||||
return V;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<StringRef> LazyObjectFile::getBitcodeSymbols() {
|
||||
std::unique_ptr<lto::InputFile> Obj = check(lto::InputFile::create(this->MB));
|
||||
std::unique_ptr<lto::InputFile> Obj =
|
||||
check(lto::InputFile::create(this->MB), toString(this));
|
||||
std::vector<StringRef> V;
|
||||
for (const lto::InputFile::Symbol &Sym : Obj->symbols())
|
||||
if (!(Sym.getFlags() & BasicSymbolRef::SF_Undefined))
|
||||
if (!Sym.isUndefined())
|
||||
V.push_back(Saver.save(Sym.getName()));
|
||||
return V;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
namespace llvm {
|
||||
class DWARFDebugLine;
|
||||
class TarWriter;
|
||||
struct DILineInfo;
|
||||
namespace lto {
|
||||
class InputFile;
|
||||
}
|
||||
|
|
@ -74,25 +75,34 @@ public:
|
|||
StringRef getName() const { return MB.getBufferIdentifier(); }
|
||||
MemoryBufferRef MB;
|
||||
|
||||
// Returns sections. It is a runtime error to call this function
|
||||
// on files that don't have the notion of sections.
|
||||
ArrayRef<InputSectionBase *> getSections() const {
|
||||
assert(FileKind == ObjectKind || FileKind == BinaryKind);
|
||||
return Sections;
|
||||
}
|
||||
|
||||
// Filename of .a which contained this file. If this file was
|
||||
// not in an archive file, it is the empty string. We use this
|
||||
// string for creating error messages.
|
||||
StringRef ArchiveName;
|
||||
|
||||
// If this file is in an archive, the member contains the offset of
|
||||
// the file in the archive. Otherwise, it's just zero. We store this
|
||||
// field so that we can pass it to lib/LTO in order to disambiguate
|
||||
// between objects.
|
||||
uint64_t OffsetInArchive;
|
||||
|
||||
// If this is an architecture-specific file, the following members
|
||||
// have ELF type (i.e. ELF{32,64}{LE,BE}) and target machine type.
|
||||
ELFKind EKind = ELFNoneKind;
|
||||
uint16_t EMachine = llvm::ELF::EM_NONE;
|
||||
uint8_t OSABI = 0;
|
||||
|
||||
// For SharedKind inputs, the string to use in DT_NEEDED when the library
|
||||
// has no soname.
|
||||
std::string DefaultSoName;
|
||||
|
||||
// Cache for toString(). Only toString() should use this member.
|
||||
mutable std::string ToStringCache;
|
||||
|
||||
protected:
|
||||
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
|
||||
InputFile(Kind K, MemoryBufferRef M);
|
||||
std::vector<InputSectionBase *> Sections;
|
||||
|
||||
private:
|
||||
const Kind FileKind;
|
||||
|
|
@ -136,9 +146,7 @@ template <class ELFT> class ObjectFile : public ELFFileBase<ELFT> {
|
|||
typedef typename ELFT::Rela Elf_Rela;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::SymRange Elf_Sym_Range;
|
||||
typedef typename ELFT::Word Elf_Word;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
StringRef getShtGroupSignature(ArrayRef<Elf_Shdr> Sections,
|
||||
const Elf_Shdr &Sec);
|
||||
|
|
@ -151,13 +159,11 @@ public:
|
|||
|
||||
ArrayRef<SymbolBody *> getSymbols();
|
||||
ArrayRef<SymbolBody *> getLocalSymbols();
|
||||
ArrayRef<SymbolBody *> getNonLocalSymbols();
|
||||
|
||||
explicit ObjectFile(MemoryBufferRef M);
|
||||
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
|
||||
ArrayRef<InputSectionBase<ELFT> *> getSections() const { return Sections; }
|
||||
InputSectionBase<ELFT> *getSection(const Elf_Sym &Sym) const;
|
||||
InputSectionBase *getSection(const Elf_Sym &Sym) const;
|
||||
|
||||
SymbolBody &getSymbolBody(uint32_t SymbolIndex) const {
|
||||
if (SymbolIndex >= SymbolBodies.size())
|
||||
|
|
@ -167,13 +173,14 @@ public:
|
|||
|
||||
template <typename RelT>
|
||||
SymbolBody &getRelocTargetSym(const RelT &Rel) const {
|
||||
uint32_t SymIndex = Rel.getSymbol(Config->Mips64EL);
|
||||
uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL);
|
||||
return getSymbolBody(SymIndex);
|
||||
}
|
||||
|
||||
// Returns source line information for a given offset.
|
||||
// If no information is available, returns "".
|
||||
std::string getLineInfo(InputSectionBase<ELFT> *S, uintX_t Offset);
|
||||
std::string getLineInfo(InputSectionBase *S, uint64_t Offset);
|
||||
llvm::Optional<llvm::DILineInfo> getDILineInfo(InputSectionBase *, uint64_t);
|
||||
|
||||
// MIPS GP0 value defined by this file. This value represents the gp value
|
||||
// used to create the relocatable object and required to support
|
||||
|
|
@ -190,16 +197,13 @@ private:
|
|||
initializeSections(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
void initializeSymbols();
|
||||
void initializeDwarfLine();
|
||||
InputSectionBase<ELFT> *getRelocTarget(const Elf_Shdr &Sec);
|
||||
InputSectionBase<ELFT> *createInputSection(const Elf_Shdr &Sec,
|
||||
StringRef SectionStringTable);
|
||||
InputSectionBase *getRelocTarget(const Elf_Shdr &Sec);
|
||||
InputSectionBase *createInputSection(const Elf_Shdr &Sec,
|
||||
StringRef SectionStringTable);
|
||||
|
||||
bool shouldMerge(const Elf_Shdr &Sec);
|
||||
SymbolBody *createSymbolBody(const Elf_Sym *Sym);
|
||||
|
||||
// List of all sections defined by this file.
|
||||
std::vector<InputSectionBase<ELFT> *> Sections;
|
||||
|
||||
// List of all symbols referenced or defined by this file.
|
||||
std::vector<SymbolBody *> SymbolBodies;
|
||||
|
||||
|
|
@ -256,7 +260,8 @@ private:
|
|||
|
||||
class BitcodeFile : public InputFile {
|
||||
public:
|
||||
explicit BitcodeFile(MemoryBufferRef M);
|
||||
BitcodeFile(MemoryBufferRef M, StringRef ArchiveName,
|
||||
uint64_t OffsetInArchive);
|
||||
static bool classof(const InputFile *F) { return F->kind() == BitcodeKind; }
|
||||
template <class ELFT>
|
||||
void parse(llvm::DenseSet<llvm::CachedHashStringRef> &ComdatGroups);
|
||||
|
|
@ -276,8 +281,6 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
|
|||
typedef typename ELFT::SymRange Elf_Sym_Range;
|
||||
typedef typename ELFT::Verdef Elf_Verdef;
|
||||
typedef typename ELFT::Versym Elf_Versym;
|
||||
typedef typename ELFT::Word Elf_Word;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
std::vector<StringRef> Undefs;
|
||||
StringRef SoName;
|
||||
|
|
@ -285,7 +288,7 @@ template <class ELFT> class SharedFile : public ELFFileBase<ELFT> {
|
|||
const Elf_Shdr *VerdefSec = nullptr;
|
||||
|
||||
public:
|
||||
StringRef getSoName() const { return SoName; }
|
||||
StringRef getSoName() const;
|
||||
const Elf_Shdr *getSection(const Elf_Sym &Sym) const;
|
||||
llvm::ArrayRef<StringRef> getUndefinedSymbols() { return Undefs; }
|
||||
|
||||
|
|
@ -322,10 +325,6 @@ public:
|
|||
explicit BinaryFile(MemoryBufferRef M) : InputFile(BinaryKind, M) {}
|
||||
static bool classof(const InputFile *F) { return F->kind() == BinaryKind; }
|
||||
template <class ELFT> void parse();
|
||||
ArrayRef<InputSectionData *> getSections() const { return Sections; }
|
||||
|
||||
private:
|
||||
std::vector<InputSectionData *> Sections;
|
||||
};
|
||||
|
||||
InputFile *createObjectFile(MemoryBufferRef MB, StringRef ArchiveName = "",
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -27,97 +27,115 @@ class DefinedCommon;
|
|||
class SymbolBody;
|
||||
struct SectionPiece;
|
||||
|
||||
template <class ELFT> class DefinedRegular;
|
||||
class DefinedRegular;
|
||||
class SyntheticSection;
|
||||
template <class ELFT> class EhFrameSection;
|
||||
class MergeSyntheticSection;
|
||||
template <class ELFT> class ObjectFile;
|
||||
template <class ELFT> class OutputSection;
|
||||
class OutputSectionBase;
|
||||
class OutputSection;
|
||||
|
||||
// We need non-template input section class to store symbol layout
|
||||
// in linker script parser structures, where we do not have ELFT
|
||||
// template parameter. For each scripted output section symbol we
|
||||
// store pointer to preceding InputSectionData object or nullptr,
|
||||
// if symbol should be placed at the very beginning of the output
|
||||
// section
|
||||
class InputSectionData {
|
||||
// This is the base class of all sections that lld handles. Some are sections in
|
||||
// input files, some are sections in the produced output file and some exist
|
||||
// just as a convenience for implementing special ways of combining some
|
||||
// sections.
|
||||
class SectionBase {
|
||||
public:
|
||||
enum Kind { Regular, EHFrame, Merge, Synthetic, };
|
||||
enum Kind { Regular, EHFrame, Merge, Synthetic, Output };
|
||||
|
||||
Kind kind() const { return (Kind)SectionKind; }
|
||||
|
||||
StringRef Name;
|
||||
|
||||
unsigned SectionKind : 3;
|
||||
|
||||
// The next two bit fields are only used by InputSectionBase, but we
|
||||
// put them here so the struct packs better.
|
||||
|
||||
// The garbage collector sets sections' Live bits.
|
||||
// If GC is disabled, all sections are considered live by default.
|
||||
InputSectionData(Kind SectionKind, StringRef Name, ArrayRef<uint8_t> Data,
|
||||
bool Live)
|
||||
: SectionKind(SectionKind), Live(Live), Assigned(false), Name(Name),
|
||||
Data(Data) {}
|
||||
unsigned Live : 1; // for garbage collection
|
||||
unsigned Assigned : 1; // for linker script
|
||||
|
||||
private:
|
||||
unsigned SectionKind : 3;
|
||||
|
||||
public:
|
||||
Kind kind() const { return (Kind)SectionKind; }
|
||||
|
||||
unsigned Live : 1; // for garbage collection
|
||||
unsigned Assigned : 1; // for linker script
|
||||
uint32_t Alignment;
|
||||
StringRef Name;
|
||||
ArrayRef<uint8_t> Data;
|
||||
|
||||
template <typename T> llvm::ArrayRef<T> getDataAs() const {
|
||||
size_t S = Data.size();
|
||||
assert(S % sizeof(T) == 0);
|
||||
return llvm::makeArrayRef<T>((const T *)Data.data(), S / sizeof(T));
|
||||
}
|
||||
|
||||
std::vector<Relocation> Relocations;
|
||||
};
|
||||
|
||||
// This corresponds to a section of an input file.
|
||||
template <class ELFT> class InputSectionBase : public InputSectionData {
|
||||
protected:
|
||||
typedef typename ELFT::Chdr Elf_Chdr;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
// The file this section is from.
|
||||
ObjectFile<ELFT> *File;
|
||||
|
||||
public:
|
||||
// These corresponds to the fields in Elf_Shdr.
|
||||
uintX_t Flags;
|
||||
uintX_t Offset = 0;
|
||||
uintX_t Entsize;
|
||||
uint64_t Flags;
|
||||
uint64_t Entsize;
|
||||
uint32_t Type;
|
||||
uint32_t Link;
|
||||
uint32_t Info;
|
||||
|
||||
OutputSection *getOutputSection();
|
||||
const OutputSection *getOutputSection() const {
|
||||
return const_cast<SectionBase *>(this)->getOutputSection();
|
||||
}
|
||||
|
||||
// Translate an offset in the input section to an offset in the output
|
||||
// section.
|
||||
uint64_t getOffset(uint64_t Offset) const;
|
||||
|
||||
uint64_t getOffset(const DefinedRegular &Sym) const;
|
||||
|
||||
protected:
|
||||
SectionBase(Kind SectionKind, StringRef Name, uint64_t Flags,
|
||||
uint64_t Entsize, uint64_t Alignment, uint32_t Type,
|
||||
uint32_t Info, uint32_t Link)
|
||||
: Name(Name), SectionKind(SectionKind), Alignment(Alignment),
|
||||
Flags(Flags), Entsize(Entsize), Type(Type), Link(Link), Info(Info) {
|
||||
Live = false;
|
||||
Assigned = false;
|
||||
}
|
||||
};
|
||||
|
||||
// This corresponds to a section of an input file.
|
||||
class InputSectionBase : public SectionBase {
|
||||
public:
|
||||
static bool classof(const SectionBase *S);
|
||||
|
||||
// The file this section is from.
|
||||
InputFile *File;
|
||||
|
||||
ArrayRef<uint8_t> Data;
|
||||
uint64_t getOffsetInFile() const;
|
||||
|
||||
static InputSectionBase Discarded;
|
||||
|
||||
InputSectionBase()
|
||||
: InputSectionData(Regular, "", ArrayRef<uint8_t>(), false), Repl(this) {
|
||||
: SectionBase(Regular, "", /*Flags*/ 0, /*Entsize*/ 0, /*Alignment*/ 0,
|
||||
/*Type*/ 0,
|
||||
/*Info*/ 0, /*Link*/ 0),
|
||||
Repl(this) {
|
||||
Live = false;
|
||||
Assigned = false;
|
||||
NumRelocations = 0;
|
||||
AreRelocsRela = false;
|
||||
}
|
||||
|
||||
InputSectionBase(ObjectFile<ELFT> *File, const Elf_Shdr *Header,
|
||||
template <class ELFT>
|
||||
InputSectionBase(ObjectFile<ELFT> *File, const typename ELFT::Shdr *Header,
|
||||
StringRef Name, Kind SectionKind);
|
||||
InputSectionBase(ObjectFile<ELFT> *File, uintX_t Flags, uint32_t Type,
|
||||
uintX_t Entsize, uint32_t Link, uint32_t Info,
|
||||
uintX_t Addralign, ArrayRef<uint8_t> Data, StringRef Name,
|
||||
|
||||
InputSectionBase(InputFile *File, uint64_t Flags, uint32_t Type,
|
||||
uint64_t Entsize, uint32_t Link, uint32_t Info,
|
||||
uint32_t Alignment, ArrayRef<uint8_t> Data, StringRef Name,
|
||||
Kind SectionKind);
|
||||
OutputSectionBase *OutSec = nullptr;
|
||||
OutputSection *OutSec = nullptr;
|
||||
|
||||
// Relocations that refer to this section.
|
||||
const Elf_Rel *FirstRelocation = nullptr;
|
||||
const void *FirstRelocation = nullptr;
|
||||
unsigned NumRelocations : 31;
|
||||
unsigned AreRelocsRela : 1;
|
||||
ArrayRef<Elf_Rel> rels() const {
|
||||
template <class ELFT> ArrayRef<typename ELFT::Rel> rels() const {
|
||||
assert(!AreRelocsRela);
|
||||
return llvm::makeArrayRef(FirstRelocation, NumRelocations);
|
||||
return llvm::makeArrayRef(
|
||||
static_cast<const typename ELFT::Rel *>(FirstRelocation),
|
||||
NumRelocations);
|
||||
}
|
||||
ArrayRef<Elf_Rela> relas() const {
|
||||
template <class ELFT> ArrayRef<typename ELFT::Rela> relas() const {
|
||||
assert(AreRelocsRela);
|
||||
return llvm::makeArrayRef(static_cast<const Elf_Rela *>(FirstRelocation),
|
||||
NumRelocations);
|
||||
return llvm::makeArrayRef(
|
||||
static_cast<const typename ELFT::Rela *>(FirstRelocation),
|
||||
NumRelocations);
|
||||
}
|
||||
|
||||
// This pointer points to the "real" instance of this instance.
|
||||
|
|
@ -125,25 +143,38 @@ public:
|
|||
// Repl pointer of one section points to another section. So,
|
||||
// if you need to get a pointer to this instance, do not use
|
||||
// this but instead this->Repl.
|
||||
InputSectionBase<ELFT> *Repl;
|
||||
InputSectionBase *Repl;
|
||||
|
||||
// InputSections that are dependent on us (reverse dependency for GC)
|
||||
llvm::TinyPtrVector<InputSectionBase *> DependentSections;
|
||||
|
||||
// Returns the size of this section (even if this is a common or BSS.)
|
||||
size_t getSize() const;
|
||||
|
||||
ObjectFile<ELFT> *getFile() const { return File; }
|
||||
llvm::object::ELFFile<ELFT> getObj() const { return File->getObj(); }
|
||||
uintX_t getOffset(const DefinedRegular<ELFT> &Sym) const;
|
||||
template <class ELFT> ObjectFile<ELFT> *getFile() const;
|
||||
|
||||
template <class ELFT> llvm::object::ELFFile<ELFT> getObj() const {
|
||||
return getFile<ELFT>()->getObj();
|
||||
}
|
||||
|
||||
InputSectionBase *getLinkOrderDep() const;
|
||||
// Translate an offset in the input section to an offset in the output
|
||||
// section.
|
||||
uintX_t getOffset(uintX_t Offset) const;
|
||||
|
||||
void uncompress();
|
||||
|
||||
// Returns a source location string. Used to construct an error message.
|
||||
std::string getLocation(uintX_t Offset);
|
||||
template <class ELFT> std::string getLocation(uint64_t Offset);
|
||||
template <class ELFT> std::string getSrcMsg(uint64_t Offset);
|
||||
template <class ELFT> std::string getObjMsg(uint64_t Offset);
|
||||
|
||||
void relocate(uint8_t *Buf, uint8_t *BufEnd);
|
||||
template <class ELFT> void relocate(uint8_t *Buf, uint8_t *BufEnd);
|
||||
|
||||
std::vector<Relocation> Relocations;
|
||||
|
||||
template <typename T> llvm::ArrayRef<T> getDataAs() const {
|
||||
size_t S = Data.size();
|
||||
assert(S % sizeof(T) == 0);
|
||||
return llvm::makeArrayRef<T>((const T *)Data.data(), S / sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
// SectionPiece represents a piece of splittable section contents.
|
||||
|
|
@ -162,26 +193,23 @@ static_assert(sizeof(SectionPiece) == 2 * sizeof(size_t),
|
|||
"SectionPiece is too big");
|
||||
|
||||
// This corresponds to a SHF_MERGE section of an input file.
|
||||
template <class ELFT> class MergeInputSection : public InputSectionBase<ELFT> {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
|
||||
class MergeInputSection : public InputSectionBase {
|
||||
public:
|
||||
MergeInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header,
|
||||
template <class ELFT>
|
||||
MergeInputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name);
|
||||
static bool classof(const InputSectionData *S);
|
||||
static bool classof(const SectionBase *S);
|
||||
void splitIntoPieces();
|
||||
|
||||
// Mark the piece at a given offset live. Used by GC.
|
||||
void markLiveAt(uintX_t Offset) {
|
||||
void markLiveAt(uint64_t Offset) {
|
||||
assert(this->Flags & llvm::ELF::SHF_ALLOC);
|
||||
LiveOffsets.insert(Offset);
|
||||
}
|
||||
|
||||
// Translate an offset in the input section to an offset
|
||||
// in the output section.
|
||||
uintX_t getOffset(uintX_t Offset) const;
|
||||
uint64_t getOffset(uint64_t Offset) const;
|
||||
|
||||
// Splittable sections are handled as a sequence of data
|
||||
// rather than a single large blob of data.
|
||||
|
|
@ -203,8 +231,13 @@ public:
|
|||
}
|
||||
|
||||
// Returns the SectionPiece at a given input section offset.
|
||||
SectionPiece *getSectionPiece(uintX_t Offset);
|
||||
const SectionPiece *getSectionPiece(uintX_t Offset) const;
|
||||
SectionPiece *getSectionPiece(uint64_t Offset);
|
||||
const SectionPiece *getSectionPiece(uint64_t Offset) const;
|
||||
|
||||
// MergeInputSections are aggregated to a synthetic input sections,
|
||||
// and then added to an OutputSection. This pointer points to a
|
||||
// synthetic MergeSyntheticSection which this section belongs to.
|
||||
MergeSyntheticSection *MergeSec = nullptr;
|
||||
|
||||
private:
|
||||
void splitStrings(ArrayRef<uint8_t> A, size_t Size);
|
||||
|
|
@ -212,18 +245,18 @@ private:
|
|||
|
||||
std::vector<uint32_t> Hashes;
|
||||
|
||||
mutable llvm::DenseMap<uintX_t, uintX_t> OffsetMap;
|
||||
mutable llvm::DenseMap<uint64_t, uint64_t> OffsetMap;
|
||||
mutable std::once_flag InitOffsetMap;
|
||||
|
||||
llvm::DenseSet<uintX_t> LiveOffsets;
|
||||
llvm::DenseSet<uint64_t> LiveOffsets;
|
||||
};
|
||||
|
||||
struct EhSectionPiece : public SectionPiece {
|
||||
EhSectionPiece(size_t Off, InputSectionData *ID, uint32_t Size,
|
||||
EhSectionPiece(size_t Off, InputSectionBase *ID, uint32_t Size,
|
||||
unsigned FirstRelocation)
|
||||
: SectionPiece(Off, false), ID(ID), Size(Size),
|
||||
FirstRelocation(FirstRelocation) {}
|
||||
InputSectionData *ID;
|
||||
InputSectionBase *ID;
|
||||
uint32_t Size;
|
||||
uint32_t size() const { return Size; }
|
||||
|
||||
|
|
@ -232,85 +265,65 @@ struct EhSectionPiece : public SectionPiece {
|
|||
};
|
||||
|
||||
// This corresponds to a .eh_frame section of an input file.
|
||||
template <class ELFT> class EhInputSection : public InputSectionBase<ELFT> {
|
||||
class EhInputSection : public InputSectionBase {
|
||||
public:
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
EhInputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header, StringRef Name);
|
||||
static bool classof(const InputSectionData *S);
|
||||
void split();
|
||||
template <class RelTy> void split(ArrayRef<RelTy> Rels);
|
||||
template <class ELFT>
|
||||
EhInputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name);
|
||||
static bool classof(const SectionBase *S);
|
||||
template <class ELFT> void split();
|
||||
template <class ELFT, class RelTy> void split(ArrayRef<RelTy> Rels);
|
||||
|
||||
// Splittable sections are handled as a sequence of data
|
||||
// rather than a single large blob of data.
|
||||
std::vector<EhSectionPiece> Pieces;
|
||||
SyntheticSection *EHSec = nullptr;
|
||||
};
|
||||
|
||||
// This corresponds to a non SHF_MERGE section of an input file.
|
||||
template <class ELFT> class InputSection : public InputSectionBase<ELFT> {
|
||||
typedef InputSectionBase<ELFT> Base;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
typedef InputSectionData::Kind Kind;
|
||||
|
||||
// This is a section that is added directly to an output section
|
||||
// instead of needing special combination via a synthetic section. This
|
||||
// includes all input sections with the exceptions of SHF_MERGE and
|
||||
// .eh_frame. It also includes the synthetic sections themselves.
|
||||
class InputSection : public InputSectionBase {
|
||||
public:
|
||||
InputSection();
|
||||
InputSection(uintX_t Flags, uint32_t Type, uintX_t Addralign,
|
||||
ArrayRef<uint8_t> Data, StringRef Name,
|
||||
Kind K = InputSectionData::Regular);
|
||||
InputSection(ObjectFile<ELFT> *F, const Elf_Shdr *Header, StringRef Name);
|
||||
|
||||
static InputSection<ELFT> Discarded;
|
||||
InputSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
|
||||
ArrayRef<uint8_t> Data, StringRef Name, Kind K = Regular);
|
||||
template <class ELFT>
|
||||
InputSection(ObjectFile<ELFT> *F, const typename ELFT::Shdr *Header,
|
||||
StringRef Name);
|
||||
|
||||
// Write this section to a mmap'ed file, assuming Buf is pointing to
|
||||
// beginning of the output section.
|
||||
void writeTo(uint8_t *Buf);
|
||||
template <class ELFT> void writeTo(uint8_t *Buf);
|
||||
|
||||
// The offset from beginning of the output sections this section was assigned
|
||||
// to. The writer sets a value.
|
||||
uint64_t OutSecOff = 0;
|
||||
|
||||
// InputSection that is dependent on us (reverse dependency for GC)
|
||||
InputSectionBase<ELFT> *DependentSection = nullptr;
|
||||
static bool classof(const SectionBase *S);
|
||||
|
||||
static bool classof(const InputSectionData *S);
|
||||
InputSectionBase *getRelocatedSection();
|
||||
|
||||
InputSectionBase<ELFT> *getRelocatedSection();
|
||||
|
||||
// Register thunk related to the symbol. When the section is written
|
||||
// to a mmap'ed file, target is requested to write an actual thunk code.
|
||||
// Now thunks is supported for MIPS and ARM target only.
|
||||
void addThunk(const Thunk<ELFT> *T);
|
||||
|
||||
// The offset of synthetic thunk code from beginning of this section.
|
||||
uint64_t getThunkOff() const;
|
||||
|
||||
// Size of chunk with thunks code.
|
||||
uint64_t getThunksSize() const;
|
||||
|
||||
template <class RelTy>
|
||||
template <class ELFT, class RelTy>
|
||||
void relocateNonAlloc(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
|
||||
|
||||
// Used by ICF.
|
||||
uint32_t Class[2] = {0, 0};
|
||||
|
||||
// Called by ICF to merge two input sections.
|
||||
void replace(InputSection<ELFT> *Other);
|
||||
void replace(InputSection *Other);
|
||||
|
||||
private:
|
||||
template <class RelTy>
|
||||
template <class ELFT, class RelTy>
|
||||
void copyRelocations(uint8_t *Buf, llvm::ArrayRef<RelTy> Rels);
|
||||
|
||||
llvm::TinyPtrVector<const Thunk<ELFT> *> Thunks;
|
||||
};
|
||||
|
||||
template <class ELFT> InputSection<ELFT> InputSection<ELFT>::Discarded;
|
||||
// The list of all input sections.
|
||||
extern std::vector<InputSectionBase *> InputSections;
|
||||
|
||||
} // namespace elf
|
||||
|
||||
template <class ELFT> std::string toString(const elf::InputSectionBase<ELFT> *);
|
||||
std::string toString(const elf::InputSectionBase *);
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
|
|
|
|||
61
ELF/LTO.cpp
61
ELF/LTO.cpp
|
|
@ -12,12 +12,13 @@
|
|||
#include "Error.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Symbols.h"
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/CodeGen/CommandFlags.h"
|
||||
#include "llvm/IR/DiagnosticPrinter.h"
|
||||
#include "llvm/LTO/Caching.h"
|
||||
#include "llvm/LTO/Config.h"
|
||||
#include "llvm/LTO/LTO.h"
|
||||
#include "llvm/Object/SymbolicFile.h"
|
||||
|
|
@ -46,7 +47,7 @@ static void saveBuffer(StringRef Buffer, const Twine &Path) {
|
|||
std::error_code EC;
|
||||
raw_fd_ostream OS(Path.str(), EC, sys::fs::OpenFlags::F_None);
|
||||
if (EC)
|
||||
error(EC, "cannot create " + Path);
|
||||
error("cannot create " + Path + ": " + EC.message());
|
||||
OS << Buffer;
|
||||
}
|
||||
|
||||
|
|
@ -73,6 +74,7 @@ static std::unique_ptr<lto::LTO> createLTO() {
|
|||
Conf.Options.RelaxELFRelocations = true;
|
||||
|
||||
Conf.RelocModel = Config->Pic ? Reloc::PIC_ : Reloc::Static;
|
||||
Conf.CodeModel = GetCodeModelFromCMModel();
|
||||
Conf.DisableVerify = Config->DisableVerify;
|
||||
Conf.DiagHandler = diagnosticHandler;
|
||||
Conf.OptLevel = Config->LTOO;
|
||||
|
|
@ -81,6 +83,10 @@ static std::unique_ptr<lto::LTO> createLTO() {
|
|||
Conf.OptPipeline = Config->LTONewPmPasses;
|
||||
Conf.AAPipeline = Config->LTOAAPipeline;
|
||||
|
||||
// Set up optimization remarks if we've been asked to.
|
||||
Conf.RemarksFilename = Config->OptRemarksFilename;
|
||||
Conf.RemarksWithHotness = Config->OptRemarksWithHotness;
|
||||
|
||||
if (Config->SaveTemps)
|
||||
checkError(Conf.addSaveTemps(std::string(Config->OutputFile) + ".",
|
||||
/*UseInputModulePath*/ true));
|
||||
|
|
@ -96,12 +102,12 @@ BitcodeCompiler::BitcodeCompiler() : LTOObj(createLTO()) {}
|
|||
|
||||
BitcodeCompiler::~BitcodeCompiler() = default;
|
||||
|
||||
template <class ELFT> static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined<ELFT>>(S, S->body()->getName(), /*IsLocal=*/false,
|
||||
STV_DEFAULT, S->body()->Type, nullptr);
|
||||
static void undefine(Symbol *S) {
|
||||
replaceBody<Undefined>(S, S->body()->getName(), /*IsLocal=*/false,
|
||||
STV_DEFAULT, S->body()->Type, nullptr);
|
||||
}
|
||||
|
||||
template <class ELFT> void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
void BitcodeCompiler::add(BitcodeFile &F) {
|
||||
lto::InputFile &Obj = *F.Obj;
|
||||
unsigned SymNum = 0;
|
||||
std::vector<Symbol *> Syms = F.getSymbols();
|
||||
|
|
@ -119,14 +125,12 @@ template <class ELFT> void BitcodeCompiler::add(BitcodeFile &F) {
|
|||
// flags an undefined in IR with a definition in ASM as prevailing.
|
||||
// Once IRObjectFile is fixed to report only one symbol this hack can
|
||||
// be removed.
|
||||
R.Prevailing =
|
||||
!(ObjSym.getFlags() & object::BasicSymbolRef::SF_Undefined) &&
|
||||
B->File == &F;
|
||||
R.Prevailing = !ObjSym.isUndefined() && B->File == &F;
|
||||
|
||||
R.VisibleToRegularObj =
|
||||
Sym->IsUsedInRegularObj || (R.Prevailing && Sym->includeInDynsym());
|
||||
if (R.Prevailing)
|
||||
undefine<ELFT>(Sym);
|
||||
undefine(Sym);
|
||||
}
|
||||
checkError(LTOObj->add(std::move(F.Obj), Resols));
|
||||
}
|
||||
|
|
@ -137,17 +141,34 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
|||
std::vector<InputFile *> Ret;
|
||||
unsigned MaxTasks = LTOObj->getMaxTasks();
|
||||
Buff.resize(MaxTasks);
|
||||
Files.resize(MaxTasks);
|
||||
|
||||
checkError(LTOObj->run([&](size_t Task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
|
||||
}));
|
||||
// The --thinlto-cache-dir option specifies the path to a directory in which
|
||||
// to cache native object files for ThinLTO incremental builds. If a path was
|
||||
// specified, configure LTO to use it as the cache directory.
|
||||
lto::NativeObjectCache Cache;
|
||||
if (!Config->ThinLTOCacheDir.empty())
|
||||
Cache = check(
|
||||
lto::localCache(Config->ThinLTOCacheDir,
|
||||
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
|
||||
Files[Task] = std::move(MB);
|
||||
}));
|
||||
|
||||
checkError(LTOObj->run(
|
||||
[&](size_t Task) {
|
||||
return llvm::make_unique<lto::NativeObjectStream>(
|
||||
llvm::make_unique<raw_svector_ostream>(Buff[Task]));
|
||||
},
|
||||
Cache));
|
||||
|
||||
if (!Config->ThinLTOCacheDir.empty())
|
||||
pruneCache(Config->ThinLTOCacheDir, Config->ThinLTOCachePolicy);
|
||||
|
||||
for (unsigned I = 0; I != MaxTasks; ++I) {
|
||||
if (Buff[I].empty())
|
||||
continue;
|
||||
if (Config->SaveTemps) {
|
||||
if (MaxTasks == 1)
|
||||
if (I == 0)
|
||||
saveBuffer(Buff[I], Config->OutputFile + ".lto.o");
|
||||
else
|
||||
saveBuffer(Buff[I], Config->OutputFile + Twine(I) + ".lto.o");
|
||||
|
|
@ -155,10 +176,10 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
|
|||
InputFile *Obj = createObjectFile(MemoryBufferRef(Buff[I], "lto.tmp"));
|
||||
Ret.push_back(Obj);
|
||||
}
|
||||
|
||||
for (std::unique_ptr<MemoryBuffer> &File : Files)
|
||||
if (File)
|
||||
Ret.push_back(createObjectFile(*File));
|
||||
|
||||
return Ret;
|
||||
}
|
||||
|
||||
template void BitcodeCompiler::template add<ELF32LE>(BitcodeFile &);
|
||||
template void BitcodeCompiler::template add<ELF32BE>(BitcodeFile &);
|
||||
template void BitcodeCompiler::template add<ELF64LE>(BitcodeFile &);
|
||||
template void BitcodeCompiler::template add<ELF64BE>(BitcodeFile &);
|
||||
|
|
|
|||
|
|
@ -43,12 +43,13 @@ public:
|
|||
BitcodeCompiler();
|
||||
~BitcodeCompiler();
|
||||
|
||||
template <class ELFT> void add(BitcodeFile &F);
|
||||
void add(BitcodeFile &F);
|
||||
std::vector<InputFile *> compile();
|
||||
|
||||
private:
|
||||
std::unique_ptr<llvm::lto::LTO> LTOObj;
|
||||
std::vector<SmallString<0>> Buff;
|
||||
std::vector<std::unique_ptr<MemoryBuffer>> Files;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1911
ELF/LinkerScript.cpp
1911
ELF/LinkerScript.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -15,6 +15,7 @@
|
|||
#include "Writer.h"
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
|
|
@ -28,45 +29,32 @@ namespace lld {
|
|||
namespace elf {
|
||||
|
||||
class DefinedCommon;
|
||||
class ScriptParser;
|
||||
class SymbolBody;
|
||||
template <class ELFT> class InputSectionBase;
|
||||
template <class ELFT> class InputSection;
|
||||
class OutputSectionBase;
|
||||
template <class ELFT> class OutputSectionFactory;
|
||||
class InputSectionData;
|
||||
class InputSectionBase;
|
||||
class InputSection;
|
||||
class OutputSection;
|
||||
class OutputSectionFactory;
|
||||
class InputSectionBase;
|
||||
class SectionBase;
|
||||
|
||||
struct ExprValue {
|
||||
SectionBase *Sec;
|
||||
uint64_t Val;
|
||||
bool ForceAbsolute;
|
||||
|
||||
ExprValue(SectionBase *Sec, bool ForceAbsolute, uint64_t Val)
|
||||
: Sec(Sec), Val(Val), ForceAbsolute(ForceAbsolute) {}
|
||||
ExprValue(SectionBase *Sec, uint64_t Val) : ExprValue(Sec, false, Val) {}
|
||||
ExprValue(uint64_t Val) : ExprValue(nullptr, Val) {}
|
||||
bool isAbsolute() const { return ForceAbsolute || Sec == nullptr; }
|
||||
uint64_t getValue() const;
|
||||
uint64_t getSecAddr() const;
|
||||
};
|
||||
|
||||
// This represents an expression in the linker script.
|
||||
// ScriptParser::readExpr reads an expression and returns an Expr.
|
||||
// Later, we evaluate the expression by calling the function
|
||||
// with the value of special context variable ".".
|
||||
struct Expr {
|
||||
std::function<uint64_t(uint64_t)> Val;
|
||||
std::function<bool()> IsAbsolute;
|
||||
|
||||
// If expression is section-relative the function below is used
|
||||
// to get the output section pointer.
|
||||
std::function<const OutputSectionBase *()> Section;
|
||||
|
||||
uint64_t operator()(uint64_t Dot) const { return Val(Dot); }
|
||||
operator bool() const { return (bool)Val; }
|
||||
|
||||
Expr(std::function<uint64_t(uint64_t)> Val, std::function<bool()> IsAbsolute,
|
||||
std::function<const OutputSectionBase *()> Section)
|
||||
: Val(Val), IsAbsolute(IsAbsolute), Section(Section) {}
|
||||
template <typename T>
|
||||
Expr(T V) : Expr(V, [] { return true; }, [] { return nullptr; }) {}
|
||||
Expr() : Expr(nullptr) {}
|
||||
};
|
||||
|
||||
// Parses a linker script. Calling this function updates
|
||||
// Config and ScriptConfig.
|
||||
void readLinkerScript(MemoryBufferRef MB);
|
||||
|
||||
// Parses a version script.
|
||||
void readVersionScript(MemoryBufferRef MB);
|
||||
|
||||
void readDynamicList(MemoryBufferRef MB);
|
||||
// Later, we evaluate the expression by calling the function.
|
||||
typedef std::function<ExprValue()> Expr;
|
||||
|
||||
// This enum is used to implement linker script SECTIONS command.
|
||||
// https://sourceware.org/binutils/docs/ld/SECTIONS.html#SECTIONS
|
||||
|
|
@ -80,16 +68,13 @@ enum SectionsCommandKind {
|
|||
|
||||
struct BaseCommand {
|
||||
BaseCommand(int K) : Kind(K) {}
|
||||
|
||||
virtual ~BaseCommand() = default;
|
||||
|
||||
int Kind;
|
||||
};
|
||||
|
||||
// This represents ". = <expr>" or "<symbol> = <expr>".
|
||||
struct SymbolAssignment : BaseCommand {
|
||||
SymbolAssignment(StringRef Name, Expr E)
|
||||
: BaseCommand(AssignmentKind), Name(Name), Expression(E) {}
|
||||
SymbolAssignment(StringRef Name, Expr E, std::string Loc)
|
||||
: BaseCommand(AssignmentKind), Name(Name), Expression(E), Location(Loc) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
|
||||
|
|
@ -103,6 +88,9 @@ struct SymbolAssignment : BaseCommand {
|
|||
// Command attributes for PROVIDE, HIDDEN and PROVIDE_HIDDEN.
|
||||
bool Provide = false;
|
||||
bool Hidden = false;
|
||||
|
||||
// Holds file name and line number for error reporting.
|
||||
std::string Location;
|
||||
};
|
||||
|
||||
// Linker scripts allow additional constraints to be put on ouput sections.
|
||||
|
|
@ -111,22 +99,37 @@ struct SymbolAssignment : BaseCommand {
|
|||
// with ONLY_IF_RW is created if all input sections are RW.
|
||||
enum class ConstraintKind { NoConstraint, ReadOnly, ReadWrite };
|
||||
|
||||
// This struct is used to represent the location and size of regions of
|
||||
// target memory. Instances of the struct are created by parsing the
|
||||
// MEMORY command.
|
||||
struct MemoryRegion {
|
||||
std::string Name;
|
||||
uint64_t Origin;
|
||||
uint64_t Length;
|
||||
uint64_t Offset;
|
||||
uint32_t Flags;
|
||||
uint32_t NegFlags;
|
||||
};
|
||||
|
||||
struct OutputSectionCommand : BaseCommand {
|
||||
OutputSectionCommand(StringRef Name)
|
||||
: BaseCommand(OutputSectionKind), Name(Name) {}
|
||||
|
||||
static bool classof(const BaseCommand *C);
|
||||
|
||||
OutputSection *Sec = nullptr;
|
||||
MemoryRegion *MemRegion = nullptr;
|
||||
StringRef Name;
|
||||
Expr AddrExpr;
|
||||
Expr AlignExpr;
|
||||
Expr LMAExpr;
|
||||
Expr SubalignExpr;
|
||||
std::vector<std::unique_ptr<BaseCommand>> Commands;
|
||||
std::vector<BaseCommand *> Commands;
|
||||
std::vector<StringRef> Phdrs;
|
||||
uint32_t Filler = 0;
|
||||
llvm::Optional<uint32_t> Filler;
|
||||
ConstraintKind Constraint = ConstraintKind::NoConstraint;
|
||||
std::string Location;
|
||||
std::string MemoryRegionName;
|
||||
};
|
||||
|
||||
// This struct represents one section match pattern in SECTIONS() command.
|
||||
|
|
@ -154,7 +157,7 @@ struct InputSectionDescription : BaseCommand {
|
|||
// will be associated with this InputSectionDescription.
|
||||
std::vector<SectionPattern> SectionPatterns;
|
||||
|
||||
std::vector<InputSectionData *> Sections;
|
||||
std::vector<InputSectionBase *> Sections;
|
||||
};
|
||||
|
||||
// Represents an ASSERT().
|
||||
|
|
@ -187,25 +190,10 @@ struct PhdrsCommand {
|
|||
Expr LMAExpr;
|
||||
};
|
||||
|
||||
class LinkerScriptBase {
|
||||
protected:
|
||||
~LinkerScriptBase() = default;
|
||||
|
||||
public:
|
||||
virtual uint64_t getHeaderSize() = 0;
|
||||
virtual uint64_t getSymbolValue(const Twine &Loc, StringRef S) = 0;
|
||||
virtual bool isDefined(StringRef S) = 0;
|
||||
virtual bool isAbsolute(StringRef S) = 0;
|
||||
virtual const OutputSectionBase *getSymbolSection(StringRef S) = 0;
|
||||
virtual const OutputSectionBase *getOutputSection(const Twine &Loc,
|
||||
StringRef S) = 0;
|
||||
virtual uint64_t getOutputSectionSize(StringRef S) = 0;
|
||||
};
|
||||
|
||||
// ScriptConfiguration holds linker script parse results.
|
||||
struct ScriptConfiguration {
|
||||
// Used to assign addresses to sections.
|
||||
std::vector<std::unique_ptr<BaseCommand>> Commands;
|
||||
std::vector<BaseCommand *> Commands;
|
||||
|
||||
// Used to assign sections to headers.
|
||||
std::vector<PhdrsCommand> PhdrsCommands;
|
||||
|
|
@ -215,20 +203,60 @@ struct ScriptConfiguration {
|
|||
// List of section patterns specified with KEEP commands. They will
|
||||
// be kept even if they are unused and --gc-sections is specified.
|
||||
std::vector<InputSectionDescription *> KeptSections;
|
||||
|
||||
// A map from memory region name to a memory region descriptor.
|
||||
llvm::DenseMap<llvm::StringRef, MemoryRegion> MemoryRegions;
|
||||
|
||||
// A list of symbols referenced by the script.
|
||||
std::vector<llvm::StringRef> ReferencedSymbols;
|
||||
};
|
||||
|
||||
extern ScriptConfiguration *ScriptConfig;
|
||||
class LinkerScript {
|
||||
protected:
|
||||
void assignSymbol(SymbolAssignment *Cmd, bool InSec);
|
||||
void setDot(Expr E, const Twine &Loc, bool InSec);
|
||||
|
||||
// This is a runner of the linker script.
|
||||
template <class ELFT> class LinkerScript final : public LinkerScriptBase {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
std::vector<InputSectionBase *>
|
||||
computeInputSections(const InputSectionDescription *);
|
||||
|
||||
std::vector<InputSectionBase *>
|
||||
createInputSectionList(OutputSectionCommand &Cmd);
|
||||
|
||||
std::vector<size_t> getPhdrIndices(StringRef SectionName);
|
||||
size_t getPhdrIndex(const Twine &Loc, StringRef PhdrName);
|
||||
|
||||
MemoryRegion *findMemoryRegion(OutputSectionCommand *Cmd);
|
||||
|
||||
void switchTo(OutputSection *Sec);
|
||||
void flush();
|
||||
void output(InputSection *Sec);
|
||||
void process(BaseCommand &Base);
|
||||
|
||||
OutputSection *Aether;
|
||||
bool ErrorOnMissingSection = false;
|
||||
|
||||
uint64_t Dot;
|
||||
uint64_t ThreadBssOffset = 0;
|
||||
|
||||
std::function<uint64_t()> LMAOffset;
|
||||
OutputSection *CurOutSec = nullptr;
|
||||
MemoryRegion *CurMemRegion = nullptr;
|
||||
|
||||
llvm::DenseSet<OutputSection *> AlreadyOutputOS;
|
||||
llvm::DenseSet<InputSectionBase *> AlreadyOutputIS;
|
||||
|
||||
public:
|
||||
LinkerScript();
|
||||
~LinkerScript();
|
||||
bool hasPhdrsCommands() { return !Opt.PhdrsCommands.empty(); }
|
||||
uint64_t getDot() { return Dot; }
|
||||
OutputSection *getOutputSection(const Twine &Loc, StringRef S);
|
||||
uint64_t getOutputSectionSize(StringRef S);
|
||||
void discard(ArrayRef<InputSectionBase *> V);
|
||||
|
||||
void processCommands(OutputSectionFactory<ELFT> &Factory);
|
||||
void addOrphanSections(OutputSectionFactory<ELFT> &Factory);
|
||||
ExprValue getSymbolValue(const Twine &Loc, StringRef S);
|
||||
bool isDefined(StringRef S);
|
||||
|
||||
std::vector<OutputSection *> *OutputSections;
|
||||
void addOrphanSections(OutputSectionFactory &Factory);
|
||||
void removeEmptyCommands();
|
||||
void adjustSectionsBeforeSorting();
|
||||
void adjustSectionsAfterSorting();
|
||||
|
|
@ -236,61 +264,24 @@ public:
|
|||
std::vector<PhdrEntry> createPhdrs();
|
||||
bool ignoreInterpSection();
|
||||
|
||||
uint32_t getFiller(StringRef Name);
|
||||
void writeDataBytes(StringRef Name, uint8_t *Buf);
|
||||
llvm::Optional<uint32_t> getFiller(StringRef Name);
|
||||
bool hasLMA(StringRef Name);
|
||||
bool shouldKeep(InputSectionBase<ELFT> *S);
|
||||
bool shouldKeep(InputSectionBase *S);
|
||||
void assignOffsets(OutputSectionCommand *Cmd);
|
||||
void placeOrphanSections();
|
||||
void processNonSectionCommands();
|
||||
void assignAddresses(std::vector<PhdrEntry> &Phdrs);
|
||||
bool hasPhdrsCommands();
|
||||
uint64_t getHeaderSize() override;
|
||||
uint64_t getSymbolValue(const Twine &Loc, StringRef S) override;
|
||||
bool isDefined(StringRef S) override;
|
||||
bool isAbsolute(StringRef S) override;
|
||||
const OutputSectionBase *getSymbolSection(StringRef S) override;
|
||||
const OutputSectionBase *getOutputSection(const Twine &Loc,
|
||||
StringRef S) override;
|
||||
uint64_t getOutputSectionSize(StringRef S) override;
|
||||
|
||||
std::vector<OutputSectionBase *> *OutputSections;
|
||||
|
||||
int getSectionIndex(StringRef Name);
|
||||
|
||||
private:
|
||||
void computeInputSections(InputSectionDescription *);
|
||||
void writeDataBytes(StringRef Name, uint8_t *Buf);
|
||||
void addSymbol(SymbolAssignment *Cmd);
|
||||
void processCommands(OutputSectionFactory &Factory);
|
||||
|
||||
void addSection(OutputSectionFactory<ELFT> &Factory,
|
||||
InputSectionBase<ELFT> *Sec, StringRef Name);
|
||||
void discard(ArrayRef<InputSectionBase<ELFT> *> V);
|
||||
|
||||
std::vector<InputSectionBase<ELFT> *>
|
||||
createInputSectionList(OutputSectionCommand &Cmd);
|
||||
|
||||
// "ScriptConfig" is a bit too long, so define a short name for it.
|
||||
ScriptConfiguration &Opt = *ScriptConfig;
|
||||
|
||||
std::vector<size_t> getPhdrIndices(StringRef SectionName);
|
||||
size_t getPhdrIndex(const Twine &Loc, StringRef PhdrName);
|
||||
|
||||
uintX_t Dot;
|
||||
uintX_t LMAOffset = 0;
|
||||
OutputSectionBase *CurOutSec = nullptr;
|
||||
uintX_t ThreadBssOffset = 0;
|
||||
void switchTo(OutputSectionBase *Sec);
|
||||
void flush();
|
||||
void output(InputSection<ELFT> *Sec);
|
||||
void process(BaseCommand &Base);
|
||||
llvm::DenseSet<OutputSectionBase *> AlreadyOutputOS;
|
||||
llvm::DenseSet<InputSectionData *> AlreadyOutputIS;
|
||||
// Parsed linker script configurations are set to this struct.
|
||||
ScriptConfiguration Opt;
|
||||
};
|
||||
|
||||
// Variable template is a C++14 feature, so we can't template
|
||||
// a global variable. Use a struct to workaround.
|
||||
template <class ELFT> struct Script { static LinkerScript<ELFT> *X; };
|
||||
template <class ELFT> LinkerScript<ELFT> *Script<ELFT>::X;
|
||||
|
||||
extern LinkerScriptBase *ScriptBase;
|
||||
extern LinkerScript *Script;
|
||||
|
||||
} // end namespace elf
|
||||
} // end namespace lld
|
||||
|
|
|
|||
131
ELF/MapFile.cpp
Normal file
131
ELF/MapFile.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
//===- MapFile.cpp --------------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the -Map option. It shows lists in order and
|
||||
// hierarchically the output sections, input sections, input files and
|
||||
// symbol:
|
||||
//
|
||||
// Address Size Align Out In File Symbol
|
||||
// =================================================================
|
||||
// 00201000 00000015 4 .text
|
||||
// 00201000 0000000e 4 .text
|
||||
// 00201000 0000000e 4 test.o
|
||||
// 0020100e 00000000 0 local
|
||||
// 00201005 00000000 0 f(int)
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MapFile.h"
|
||||
#include "InputFiles.h"
|
||||
#include "Strings.h"
|
||||
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::object;
|
||||
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
static void writeOutSecLine(raw_fd_ostream &OS, int Width, uint64_t Address,
|
||||
uint64_t Size, uint64_t Align, StringRef Name) {
|
||||
OS << format("%0*llx %0*llx %5lld ", Width, Address, Width, Size, Align)
|
||||
<< left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeInSecLine(raw_fd_ostream &OS, int Width, uint64_t Address,
|
||||
uint64_t Size, uint64_t Align, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeOutSecLine(OS, Width, Address, Size, Align, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeFileLine(raw_fd_ostream &OS, int Width, uint64_t Address,
|
||||
uint64_t Size, uint64_t Align, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeInSecLine(OS, Width, Address, Size, Align, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
static void writeSymbolLine(raw_fd_ostream &OS, int Width, uint64_t Address,
|
||||
uint64_t Size, StringRef Name) {
|
||||
// Pass an empty name to align the text to the correct column.
|
||||
writeFileLine(OS, Width, Address, Size, 0, "");
|
||||
OS << ' ' << left_justify(Name, 7);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void writeInputSection(raw_fd_ostream &OS, const InputSection *IS,
|
||||
StringRef &PrevName) {
|
||||
int Width = ELFT::Is64Bits ? 16 : 8;
|
||||
StringRef Name = IS->Name;
|
||||
if (Name != PrevName) {
|
||||
writeInSecLine(OS, Width, IS->OutSec->Addr + IS->OutSecOff, IS->getSize(),
|
||||
IS->Alignment, Name);
|
||||
OS << '\n';
|
||||
PrevName = Name;
|
||||
}
|
||||
|
||||
elf::ObjectFile<ELFT> *File = IS->template getFile<ELFT>();
|
||||
if (!File)
|
||||
return;
|
||||
writeFileLine(OS, Width, IS->OutSec->Addr + IS->OutSecOff, IS->getSize(),
|
||||
IS->Alignment, toString(File));
|
||||
OS << '\n';
|
||||
|
||||
for (SymbolBody *Sym : File->getSymbols()) {
|
||||
auto *DR = dyn_cast<DefinedRegular>(Sym);
|
||||
if (!DR)
|
||||
continue;
|
||||
if (DR->Section != IS)
|
||||
continue;
|
||||
if (DR->isSection())
|
||||
continue;
|
||||
writeSymbolLine(OS, Width, Sym->getVA(), Sym->getSize<ELFT>(),
|
||||
toString(*Sym));
|
||||
OS << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void writeMapFile2(raw_fd_ostream &OS,
|
||||
ArrayRef<OutputSection *> OutputSections) {
|
||||
int Width = ELFT::Is64Bits ? 16 : 8;
|
||||
|
||||
OS << left_justify("Address", Width) << ' ' << left_justify("Size", Width)
|
||||
<< " Align Out In File Symbol\n";
|
||||
|
||||
for (OutputSection *Sec : OutputSections) {
|
||||
writeOutSecLine(OS, Width, Sec->Addr, Sec->Size, Sec->Alignment, Sec->Name);
|
||||
OS << '\n';
|
||||
|
||||
StringRef PrevName = "";
|
||||
for (InputSection *IS : Sec->Sections) {
|
||||
writeInputSection<ELFT>(OS, IS, PrevName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void elf::writeMapFile(ArrayRef<OutputSection *> OutputSections) {
|
||||
if (Config->MapFile.empty())
|
||||
return;
|
||||
|
||||
std::error_code EC;
|
||||
raw_fd_ostream OS(Config->MapFile, EC, sys::fs::F_None);
|
||||
if (EC)
|
||||
error("cannot open " + Config->MapFile + ": " + EC.message());
|
||||
else
|
||||
writeMapFile2<ELFT>(OS, OutputSections);
|
||||
}
|
||||
|
||||
template void elf::writeMapFile<ELF32LE>(ArrayRef<OutputSection *>);
|
||||
template void elf::writeMapFile<ELF32BE>(ArrayRef<OutputSection *>);
|
||||
template void elf::writeMapFile<ELF64LE>(ArrayRef<OutputSection *>);
|
||||
template void elf::writeMapFile<ELF64BE>(ArrayRef<OutputSection *>);
|
||||
22
ELF/MapFile.h
Normal file
22
ELF/MapFile.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//===- MapFile.h ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_MAPFILE_H
|
||||
#define LLD_ELF_MAPFILE_H
|
||||
|
||||
#include "OutputSections.h"
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
template <class ELFT>
|
||||
void writeMapFile(llvm::ArrayRef<OutputSection *> OutputSections);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
122
ELF/MarkLive.cpp
122
ELF/MarkLive.cpp
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "InputSection.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Strings.h"
|
||||
#include "SymbolTable.h"
|
||||
|
|
@ -44,51 +45,59 @@ using namespace lld::elf;
|
|||
namespace {
|
||||
// A resolved relocation. The Sec and Offset fields are set if the relocation
|
||||
// was resolved to an offset within a section.
|
||||
template <class ELFT> struct ResolvedReloc {
|
||||
InputSectionBase<ELFT> *Sec;
|
||||
typename ELFT::uint Offset;
|
||||
struct ResolvedReloc {
|
||||
InputSectionBase *Sec;
|
||||
uint64_t Offset;
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
template <class ELFT>
|
||||
static typename ELFT::uint getAddend(InputSectionBase<ELFT> &Sec,
|
||||
static typename ELFT::uint getAddend(InputSectionBase &Sec,
|
||||
const typename ELFT::Rel &Rel) {
|
||||
return Target->getImplicitAddend(Sec.Data.begin() + Rel.r_offset,
|
||||
Rel.getType(Config->Mips64EL));
|
||||
Rel.getType(Config->IsMips64EL));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static typename ELFT::uint getAddend(InputSectionBase<ELFT> &Sec,
|
||||
static typename ELFT::uint getAddend(InputSectionBase &Sec,
|
||||
const typename ELFT::Rela &Rel) {
|
||||
return Rel.r_addend;
|
||||
}
|
||||
|
||||
// There are normally few input sections whose names are valid C
|
||||
// identifiers, so we just store a std::vector instead of a multimap.
|
||||
static DenseMap<StringRef, std::vector<InputSectionBase *>> CNamedSections;
|
||||
|
||||
template <class ELFT, class RelT>
|
||||
static ResolvedReloc<ELFT> resolveReloc(InputSectionBase<ELFT> &Sec,
|
||||
RelT &Rel) {
|
||||
SymbolBody &B = Sec.getFile()->getRelocTargetSym(Rel);
|
||||
auto *D = dyn_cast<DefinedRegular<ELFT>>(&B);
|
||||
if (!D || !D->Section)
|
||||
return {nullptr, 0};
|
||||
typename ELFT::uint Offset = D->Value;
|
||||
if (D->isSection())
|
||||
Offset += getAddend(Sec, Rel);
|
||||
return {D->Section->Repl, Offset};
|
||||
static void resolveReloc(InputSectionBase &Sec, RelT &Rel,
|
||||
std::function<void(ResolvedReloc)> Fn) {
|
||||
SymbolBody &B = Sec.getFile<ELFT>()->getRelocTargetSym(Rel);
|
||||
if (auto *D = dyn_cast<DefinedRegular>(&B)) {
|
||||
if (!D->Section)
|
||||
return;
|
||||
typename ELFT::uint Offset = D->Value;
|
||||
if (D->isSection())
|
||||
Offset += getAddend<ELFT>(Sec, Rel);
|
||||
Fn({cast<InputSectionBase>(D->Section)->Repl, Offset});
|
||||
} else if (auto *U = dyn_cast<Undefined>(&B)) {
|
||||
for (InputSectionBase *Sec : CNamedSections.lookup(U->getName()))
|
||||
Fn({Sec, 0});
|
||||
}
|
||||
}
|
||||
|
||||
// Calls Fn for each section that Sec refers to via relocations.
|
||||
template <class ELFT>
|
||||
static void forEachSuccessor(InputSection<ELFT> &Sec,
|
||||
std::function<void(ResolvedReloc<ELFT>)> Fn) {
|
||||
static void forEachSuccessor(InputSection &Sec,
|
||||
std::function<void(ResolvedReloc)> Fn) {
|
||||
if (Sec.AreRelocsRela) {
|
||||
for (const typename ELFT::Rela &Rel : Sec.relas())
|
||||
Fn(resolveReloc(Sec, Rel));
|
||||
for (const typename ELFT::Rela &Rel : Sec.template relas<ELFT>())
|
||||
resolveReloc<ELFT>(Sec, Rel, Fn);
|
||||
} else {
|
||||
for (const typename ELFT::Rel &Rel : Sec.rels())
|
||||
Fn(resolveReloc(Sec, Rel));
|
||||
for (const typename ELFT::Rel &Rel : Sec.template rels<ELFT>())
|
||||
resolveReloc<ELFT>(Sec, Rel, Fn);
|
||||
}
|
||||
if (Sec.DependentSection)
|
||||
Fn({Sec.DependentSection, 0});
|
||||
for (InputSectionBase *IS : Sec.DependentSections)
|
||||
Fn({IS, 0});
|
||||
}
|
||||
|
||||
// The .eh_frame section is an unfortunate special case.
|
||||
|
|
@ -106,9 +115,8 @@ static void forEachSuccessor(InputSection<ELFT> &Sec,
|
|||
// the gc pass. With that we would be able to also gc some sections holding
|
||||
// LSDAs and personality functions if we found that they were unused.
|
||||
template <class ELFT, class RelTy>
|
||||
static void
|
||||
scanEhFrameSection(EhInputSection<ELFT> &EH, ArrayRef<RelTy> Rels,
|
||||
std::function<void(ResolvedReloc<ELFT>)> Enqueue) {
|
||||
static void scanEhFrameSection(EhInputSection &EH, ArrayRef<RelTy> Rels,
|
||||
std::function<void(ResolvedReloc)> Enqueue) {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
for (unsigned I = 0, N = EH.Pieces.size(); I < N; ++I) {
|
||||
EhSectionPiece &Piece = EH.Pieces[I];
|
||||
|
|
@ -118,7 +126,7 @@ scanEhFrameSection(EhInputSection<ELFT> &EH, ArrayRef<RelTy> Rels,
|
|||
if (read32<E>(Piece.data().data() + 4) == 0) {
|
||||
// This is a CIE, we only need to worry about the first relocation. It is
|
||||
// known to point to the personality function.
|
||||
Enqueue(resolveReloc(EH, Rels[FirstRelI]));
|
||||
resolveReloc<ELFT>(EH, Rels[FirstRelI], Enqueue);
|
||||
continue;
|
||||
}
|
||||
// This is a FDE. The relocations point to the described function or to
|
||||
|
|
@ -129,37 +137,37 @@ scanEhFrameSection(EhInputSection<ELFT> &EH, ArrayRef<RelTy> Rels,
|
|||
const RelTy &Rel = Rels[I2];
|
||||
if (Rel.r_offset >= PieceEnd)
|
||||
break;
|
||||
ResolvedReloc<ELFT> R = resolveReloc(EH, Rels[I2]);
|
||||
if (!R.Sec || R.Sec == &InputSection<ELFT>::Discarded)
|
||||
continue;
|
||||
if (R.Sec->Flags & SHF_EXECINSTR)
|
||||
continue;
|
||||
Enqueue({R.Sec, 0});
|
||||
resolveReloc<ELFT>(EH, Rels[I2], [&](ResolvedReloc R) {
|
||||
if (!R.Sec || R.Sec == &InputSection::Discarded)
|
||||
return;
|
||||
if (R.Sec->Flags & SHF_EXECINSTR)
|
||||
return;
|
||||
Enqueue({R.Sec, 0});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void
|
||||
scanEhFrameSection(EhInputSection<ELFT> &EH,
|
||||
std::function<void(ResolvedReloc<ELFT>)> Enqueue) {
|
||||
static void scanEhFrameSection(EhInputSection &EH,
|
||||
std::function<void(ResolvedReloc)> Enqueue) {
|
||||
if (!EH.NumRelocations)
|
||||
return;
|
||||
|
||||
// Unfortunately we need to split .eh_frame early since some relocations in
|
||||
// .eh_frame keep other section alive and some don't.
|
||||
EH.split();
|
||||
EH.split<ELFT>();
|
||||
|
||||
if (EH.AreRelocsRela)
|
||||
scanEhFrameSection(EH, EH.relas(), Enqueue);
|
||||
scanEhFrameSection<ELFT>(EH, EH.template relas<ELFT>(), Enqueue);
|
||||
else
|
||||
scanEhFrameSection(EH, EH.rels(), Enqueue);
|
||||
scanEhFrameSection<ELFT>(EH, EH.template rels<ELFT>(), Enqueue);
|
||||
}
|
||||
|
||||
// We do not garbage-collect two types of sections:
|
||||
// 1) Sections used by the loader (.init, .fini, .ctors, .dtors or .jcr)
|
||||
// 2) Non-allocatable sections which typically contain debugging information
|
||||
template <class ELFT> static bool isReserved(InputSectionBase<ELFT> *Sec) {
|
||||
template <class ELFT> static bool isReserved(InputSectionBase *Sec) {
|
||||
switch (Sec->Type) {
|
||||
case SHT_FINI_ARRAY:
|
||||
case SHT_INIT_ARRAY:
|
||||
|
|
@ -170,12 +178,7 @@ template <class ELFT> static bool isReserved(InputSectionBase<ELFT> *Sec) {
|
|||
if (!(Sec->Flags & SHF_ALLOC))
|
||||
return true;
|
||||
|
||||
// We do not want to reclaim sections if they can be referred
|
||||
// by __start_* and __stop_* symbols.
|
||||
StringRef S = Sec->Name;
|
||||
if (isValidCIdentifier(S))
|
||||
return true;
|
||||
|
||||
return S.startswith(".ctors") || S.startswith(".dtors") ||
|
||||
S.startswith(".init") || S.startswith(".fini") ||
|
||||
S.startswith(".jcr");
|
||||
|
|
@ -186,14 +189,15 @@ template <class ELFT> static bool isReserved(InputSectionBase<ELFT> *Sec) {
|
|||
// Starting from GC-root sections, this function visits all reachable
|
||||
// sections to set their "Live" bits.
|
||||
template <class ELFT> void elf::markLive() {
|
||||
SmallVector<InputSection<ELFT> *, 256> Q;
|
||||
SmallVector<InputSection *, 256> Q;
|
||||
CNamedSections.clear();
|
||||
|
||||
auto Enqueue = [&](ResolvedReloc<ELFT> R) {
|
||||
auto Enqueue = [&](ResolvedReloc R) {
|
||||
// Skip over discarded sections. This in theory shouldn't happen, because
|
||||
// the ELF spec doesn't allow a relocation to point to a deduplicated
|
||||
// COMDAT section directly. Unfortunately this happens in practice (e.g.
|
||||
// .eh_frame) so we need to add a check.
|
||||
if (!R.Sec || R.Sec == &InputSection<ELFT>::Discarded)
|
||||
if (R.Sec == &InputSection::Discarded)
|
||||
return;
|
||||
|
||||
// We don't gc non alloc sections.
|
||||
|
|
@ -203,20 +207,20 @@ template <class ELFT> void elf::markLive() {
|
|||
// Usually, a whole section is marked as live or dead, but in mergeable
|
||||
// (splittable) sections, each piece of data has independent liveness bit.
|
||||
// So we explicitly tell it which offset is in use.
|
||||
if (auto *MS = dyn_cast<MergeInputSection<ELFT>>(R.Sec))
|
||||
if (auto *MS = dyn_cast<MergeInputSection>(R.Sec))
|
||||
MS->markLiveAt(R.Offset);
|
||||
|
||||
if (R.Sec->Live)
|
||||
return;
|
||||
R.Sec->Live = true;
|
||||
// Add input section to the queue.
|
||||
if (InputSection<ELFT> *S = dyn_cast<InputSection<ELFT>>(R.Sec))
|
||||
if (InputSection *S = dyn_cast<InputSection>(R.Sec))
|
||||
Q.push_back(S);
|
||||
};
|
||||
|
||||
auto MarkSymbol = [&](const SymbolBody *Sym) {
|
||||
if (auto *D = dyn_cast_or_null<DefinedRegular<ELFT>>(Sym))
|
||||
Enqueue({D->Section, D->Value});
|
||||
if (auto *D = dyn_cast_or_null<DefinedRegular>(Sym))
|
||||
Enqueue({cast<InputSectionBase>(D->Section), D->Value});
|
||||
};
|
||||
|
||||
// Add GC root symbols.
|
||||
|
|
@ -234,14 +238,20 @@ template <class ELFT> void elf::markLive() {
|
|||
|
||||
// Preserve special sections and those which are specified in linker
|
||||
// script KEEP command.
|
||||
for (InputSectionBase<ELFT> *Sec : Symtab<ELFT>::X->Sections) {
|
||||
for (InputSectionBase *Sec : InputSections) {
|
||||
// .eh_frame is always marked as live now, but also it can reference to
|
||||
// sections that contain personality. We preserve all non-text sections
|
||||
// referred by .eh_frame here.
|
||||
if (auto *EH = dyn_cast_or_null<EhInputSection<ELFT>>(Sec))
|
||||
if (auto *EH = dyn_cast_or_null<EhInputSection>(Sec))
|
||||
scanEhFrameSection<ELFT>(*EH, Enqueue);
|
||||
if (isReserved(Sec) || Script<ELFT>::X->shouldKeep(Sec))
|
||||
if (Sec->Flags & SHF_LINK_ORDER)
|
||||
continue;
|
||||
if (isReserved<ELFT>(Sec) || Script->shouldKeep(Sec))
|
||||
Enqueue({Sec, 0});
|
||||
else if (isValidCIdentifier(Sec->Name)) {
|
||||
CNamedSections[Saver.save("__start_" + Sec->Name)].push_back(Sec);
|
||||
CNamedSections[Saver.save("__end_" + Sec->Name)].push_back(Sec);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark all reachable sections.
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ def color_diagnostics_eq: J<"color-diagnostics=">,
|
|||
def define_common: F<"define-common">,
|
||||
HelpText<"Assign space to common symbols">;
|
||||
|
||||
def demangle: F<"demangle">, HelpText<"Demangle symbol names">;
|
||||
|
||||
def disable_new_dtags: F<"disable-new-dtags">,
|
||||
HelpText<"Disable new dynamic tags">;
|
||||
|
||||
|
|
@ -68,6 +70,8 @@ def dynamic_list: S<"dynamic-list">,
|
|||
def eh_frame_hdr: F<"eh-frame-hdr">,
|
||||
HelpText<"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header">;
|
||||
|
||||
def emit_relocs: F<"emit-relocs">, HelpText<"Generate relocations in output">;
|
||||
|
||||
def enable_new_dtags: F<"enable-new-dtags">,
|
||||
HelpText<"Enable new dynamic tags">;
|
||||
|
||||
|
|
@ -80,6 +84,9 @@ def entry: S<"entry">, MetaVarName<"<entry>">,
|
|||
def error_limit: S<"error-limit">,
|
||||
HelpText<"Maximum number of errors to emit before stopping (0 = no limit)">;
|
||||
|
||||
def error_unresolved_symbols: F<"error-unresolved-symbols">,
|
||||
HelpText<"Report unresolved symbols as errors">;
|
||||
|
||||
def export_dynamic: F<"export-dynamic">,
|
||||
HelpText<"Put symbols in the dynamic symbol table">;
|
||||
|
||||
|
|
@ -124,6 +131,8 @@ def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
|
|||
|
||||
def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
|
||||
|
||||
def Map: JS<"Map">, HelpText<"Print a link map to the specified file">;
|
||||
|
||||
def nostdlib: F<"nostdlib">,
|
||||
HelpText<"Only search directories specified on the command line">;
|
||||
|
||||
|
|
@ -139,6 +148,12 @@ def no_define_common: F<"no-define-common">,
|
|||
def no_demangle: F<"no-demangle">,
|
||||
HelpText<"Do not demangle symbol names">;
|
||||
|
||||
def no_dynamic_linker: F<"no-dynamic-linker">,
|
||||
HelpText<"Inhibit output of .interp section">;
|
||||
|
||||
def no_export_dynamic: F<"no-export-dynamic">;
|
||||
def no_fatal_warnings: F<"no-fatal-warnings">;
|
||||
|
||||
def no_gc_sections: F<"no-gc-sections">,
|
||||
HelpText<"Disable garbage collection of unused sections">;
|
||||
|
||||
|
|
@ -170,7 +185,7 @@ def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
|
|||
def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
|
||||
HelpText<"Specify the binary format for the output object file">;
|
||||
|
||||
def omagic: F<"omagic">, MetaVarName<"<magic>">,
|
||||
def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
|
||||
HelpText<"Set the text and data sections to be readable and writable">;
|
||||
|
||||
def pie: F<"pie">, HelpText<"Create a position independent executable">;
|
||||
|
|
@ -178,6 +193,9 @@ def pie: F<"pie">, HelpText<"Create a position independent executable">;
|
|||
def print_gc_sections: F<"print-gc-sections">,
|
||||
HelpText<"List removed unused sections">;
|
||||
|
||||
def print_map: F<"print-map">,
|
||||
HelpText<"Print a link map to the standard output">;
|
||||
|
||||
def reproduce: S<"reproduce">,
|
||||
HelpText<"Dump linker invocation and input files for debugging">;
|
||||
|
||||
|
|
@ -221,7 +239,7 @@ def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
|
|||
|
||||
def trace: F<"trace">, HelpText<"Print the names of the input files">;
|
||||
|
||||
def trace_symbol : J<"trace-symbol=">, HelpText<"Trace references to symbols">;
|
||||
def trace_symbol : S<"trace-symbol">, HelpText<"Trace references to symbols">;
|
||||
|
||||
def undefined: S<"undefined">,
|
||||
HelpText<"Force undefined symbol during linking">;
|
||||
|
|
@ -244,6 +262,9 @@ def version_script: S<"version-script">,
|
|||
def warn_common: F<"warn-common">,
|
||||
HelpText<"Warn about duplicate common symbols">;
|
||||
|
||||
def warn_unresolved_symbols: F<"warn-unresolved-symbols">,
|
||||
HelpText<"Report unresolved symbols as warnings">;
|
||||
|
||||
def whole_archive: F<"whole-archive">,
|
||||
HelpText<"Force load of all members in a static library">;
|
||||
|
||||
|
|
@ -267,6 +288,7 @@ def alias_define_common_dp: F<"dp">, Alias<define_common>;
|
|||
def alias_discard_all_x: Flag<["-"], "x">, Alias<discard_all>;
|
||||
def alias_discard_locals_X: Flag<["-"], "X">, Alias<discard_locals>;
|
||||
def alias_dynamic_list: J<"dynamic-list=">, Alias<dynamic_list>;
|
||||
def alias_emit_relocs: Flag<["-"], "q">, Alias<emit_relocs>;
|
||||
def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias<entry>;
|
||||
def alias_entry_entry: J<"entry=">, Alias<entry>;
|
||||
def alias_error_limit: J<"error-limit=">, Alias<error_limit>;
|
||||
|
|
@ -278,10 +300,12 @@ def alias_format_b: S<"b">, Alias<format>;
|
|||
def alias_hash_style_hash_style: J<"hash-style=">, Alias<hash_style>;
|
||||
def alias_init_init: J<"init=">, Alias<init>;
|
||||
def alias_l__library: J<"library=">, Alias<l>;
|
||||
def alias_Map_eq: J<"Map=">, Alias<Map>;
|
||||
def alias_omagic: Flag<["-"], "N">, Alias<omagic>;
|
||||
def alias_o_output: Joined<["--"], "output=">, Alias<o>;
|
||||
def alias_o_output2 : Separate<["--"], "output">, Alias<o>;
|
||||
def alias_pie_pic_executable: F<"pic-executable">, Alias<pie>;
|
||||
def alias_print_map_M: Flag<["-"], "M">, Alias<print_map>;
|
||||
def alias_relocatable_r: Flag<["-"], "r">, Alias<relocatable>;
|
||||
def alias_retain_symbols_file: S<"retain-symbols-file">, Alias<retain_symbols_file>;
|
||||
def alias_rpath_R: JoinedOrSeparate<["-"], "R">, Alias<rpath>;
|
||||
|
|
@ -297,6 +321,7 @@ def alias_strip_debug_S: Flag<["-"], "S">, Alias<strip_debug>;
|
|||
def alias_Tbss: J<"Tbss=">, Alias<Tbss>;
|
||||
def alias_Tdata: J<"Tdata=">, Alias<Tdata>;
|
||||
def alias_trace: Flag<["-"], "t">, Alias<trace>;
|
||||
def trace_trace_symbol_eq : J<"trace-symbol=">, Alias<trace_symbol>;
|
||||
def alias_trace_symbol_y : JoinedOrSeparate<["-"], "y">, Alias<trace_symbol>;
|
||||
def alias_Ttext: J<"Ttext=">, Alias<Ttext>;
|
||||
def alias_Ttext_segment: S<"Ttext-segment">, Alias<Ttext>;
|
||||
|
|
@ -329,17 +354,12 @@ def plugin_opt_eq: J<"plugin-opt=">;
|
|||
// Options listed below are silently ignored for now for compatibility.
|
||||
def allow_shlib_undefined: F<"allow-shlib-undefined">;
|
||||
def cref: Flag<["--"], "cref">;
|
||||
def demangle: F<"demangle">;
|
||||
def detect_odr_violations: F<"detect-odr-violations">;
|
||||
def g: Flag<["-"], "g">;
|
||||
def M: Flag<["-"], "M">;
|
||||
def Map: JS<"Map">;
|
||||
def no_add_needed: F<"no-add-needed">;
|
||||
def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">;
|
||||
def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">,
|
||||
Alias<no_add_needed>;
|
||||
def no_dynamic_linker: F<"no-dynamic-linker">;
|
||||
def no_fatal_warnings: F<"no-fatal-warnings">;
|
||||
def no_mmap_output_file: F<"no-mmap-output-file">;
|
||||
def no_warn_common: F<"no-warn-common">;
|
||||
def no_warn_mismatch: F<"no-warn-mismatch">;
|
||||
|
|
@ -355,7 +375,6 @@ def G: JoinedOrSeparate<["-"], "G">;
|
|||
def Qy : F<"Qy">;
|
||||
|
||||
// Aliases for ignored options
|
||||
def alias_Map_eq: J<"Map=">, Alias<Map>;
|
||||
def alias_version_script_version_script: J<"version-script=">,
|
||||
Alias<version_script>;
|
||||
|
||||
|
|
@ -368,5 +387,13 @@ def lto_partitions: J<"lto-partitions=">,
|
|||
HelpText<"Number of LTO codegen partitions">;
|
||||
def disable_verify: F<"disable-verify">;
|
||||
def mllvm: S<"mllvm">;
|
||||
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
|
||||
HelpText<"YAML output file for optimization remarks">;
|
||||
def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
|
||||
HelpText<"Include hotness informations in the optimization remarks file">;
|
||||
def save_temps: F<"save-temps">;
|
||||
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
|
||||
HelpText<"Path to ThinLTO cached object file directory">;
|
||||
def thinlto_cache_policy: S<"thinlto-cache-policy">,
|
||||
HelpText<"Pruning policy for the ThinLTO cache">;
|
||||
def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "OutputSections.h"
|
||||
#include "Config.h"
|
||||
#include "EhFrame.h"
|
||||
#include "LinkerScript.h"
|
||||
#include "Memory.h"
|
||||
#include "Strings.h"
|
||||
|
|
@ -31,15 +30,18 @@ using namespace llvm::ELF;
|
|||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
OutputSectionBase::OutputSectionBase(StringRef Name, uint32_t Type,
|
||||
uint64_t Flags)
|
||||
: Name(Name) {
|
||||
this->Type = Type;
|
||||
this->Flags = Flags;
|
||||
this->Addralign = 1;
|
||||
}
|
||||
uint8_t Out::First;
|
||||
OutputSection *Out::Opd;
|
||||
uint8_t *Out::OpdBuf;
|
||||
PhdrEntry *Out::TlsPhdr;
|
||||
OutputSection *Out::DebugInfo;
|
||||
OutputSection *Out::ElfHeader;
|
||||
OutputSection *Out::ProgramHeaders;
|
||||
OutputSection *Out::PreinitArray;
|
||||
OutputSection *Out::InitArray;
|
||||
OutputSection *Out::FiniArray;
|
||||
|
||||
uint32_t OutputSectionBase::getPhdrFlags() const {
|
||||
uint32_t OutputSection::getPhdrFlags() const {
|
||||
uint32_t Ret = PF_R;
|
||||
if (Flags & SHF_WRITE)
|
||||
Ret |= PF_W;
|
||||
|
|
@ -49,9 +51,9 @@ uint32_t OutputSectionBase::getPhdrFlags() const {
|
|||
}
|
||||
|
||||
template <class ELFT>
|
||||
void OutputSectionBase::writeHeaderTo(typename ELFT::Shdr *Shdr) {
|
||||
void OutputSection::writeHeaderTo(typename ELFT::Shdr *Shdr) {
|
||||
Shdr->sh_entsize = Entsize;
|
||||
Shdr->sh_addralign = Addralign;
|
||||
Shdr->sh_addralign = Alignment;
|
||||
Shdr->sh_type = Type;
|
||||
Shdr->sh_offset = Offset;
|
||||
Shdr->sh_flags = Flags;
|
||||
|
|
@ -62,49 +64,28 @@ void OutputSectionBase::writeHeaderTo(typename ELFT::Shdr *Shdr) {
|
|||
Shdr->sh_name = ShName;
|
||||
}
|
||||
|
||||
template <class ELFT> static uint64_t getEntsize(uint32_t Type) {
|
||||
switch (Type) {
|
||||
case SHT_RELA:
|
||||
return sizeof(typename ELFT::Rela);
|
||||
case SHT_REL:
|
||||
return sizeof(typename ELFT::Rel);
|
||||
case SHT_MIPS_REGINFO:
|
||||
return sizeof(Elf_Mips_RegInfo<ELFT>);
|
||||
case SHT_MIPS_OPTIONS:
|
||||
return sizeof(Elf_Mips_Options<ELFT>) + sizeof(Elf_Mips_RegInfo<ELFT>);
|
||||
case SHT_MIPS_ABIFLAGS:
|
||||
return sizeof(Elf_Mips_ABIFlags<ELFT>);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
OutputSection::OutputSection(StringRef Name, uint32_t Type, uint64_t Flags)
|
||||
: SectionBase(Output, Name, Flags, /*Entsize*/ 0, /*Alignment*/ 1, Type,
|
||||
/*Info*/ 0,
|
||||
/*Link*/ 0) {}
|
||||
|
||||
template <class ELFT>
|
||||
OutputSection<ELFT>::OutputSection(StringRef Name, uint32_t Type, uintX_t Flags)
|
||||
: OutputSectionBase(Name, Type, Flags) {
|
||||
this->Entsize = getEntsize<ELFT>(Type);
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
static bool compareByFilePosition(InputSection<ELFT> *A,
|
||||
InputSection<ELFT> *B) {
|
||||
static bool compareByFilePosition(InputSection *A, InputSection *B) {
|
||||
// Synthetic doesn't have link order dependecy, stable_sort will keep it last
|
||||
if (A->kind() == InputSectionData::Synthetic ||
|
||||
B->kind() == InputSectionData::Synthetic)
|
||||
if (A->kind() == InputSectionBase::Synthetic ||
|
||||
B->kind() == InputSectionBase::Synthetic)
|
||||
return false;
|
||||
auto *LA = cast<InputSection<ELFT>>(A->getLinkOrderDep());
|
||||
auto *LB = cast<InputSection<ELFT>>(B->getLinkOrderDep());
|
||||
OutputSectionBase *AOut = LA->OutSec;
|
||||
OutputSectionBase *BOut = LB->OutSec;
|
||||
auto *LA = cast<InputSection>(A->getLinkOrderDep());
|
||||
auto *LB = cast<InputSection>(B->getLinkOrderDep());
|
||||
OutputSection *AOut = LA->OutSec;
|
||||
OutputSection *BOut = LB->OutSec;
|
||||
if (AOut != BOut)
|
||||
return AOut->SectionIndex < BOut->SectionIndex;
|
||||
return LA->OutSecOff < LB->OutSecOff;
|
||||
}
|
||||
|
||||
template <class ELFT> void OutputSection<ELFT>::finalize() {
|
||||
template <class ELFT> void OutputSection::finalize() {
|
||||
if ((this->Flags & SHF_LINK_ORDER) && !this->Sections.empty()) {
|
||||
std::sort(Sections.begin(), Sections.end(), compareByFilePosition<ELFT>);
|
||||
Size = 0;
|
||||
std::sort(Sections.begin(), Sections.end(), compareByFilePosition);
|
||||
assignOffsets();
|
||||
|
||||
// We must preserve the link order dependency of sections with the
|
||||
|
|
@ -116,34 +97,41 @@ template <class ELFT> void OutputSection<ELFT>::finalize() {
|
|||
}
|
||||
|
||||
uint32_t Type = this->Type;
|
||||
if (!Config->Relocatable || (Type != SHT_RELA && Type != SHT_REL))
|
||||
if (!Config->CopyRelocs || (Type != SHT_RELA && Type != SHT_REL))
|
||||
return;
|
||||
|
||||
InputSection *First = Sections[0];
|
||||
if (isa<SyntheticSection>(First))
|
||||
return;
|
||||
|
||||
this->Link = In<ELFT>::SymTab->OutSec->SectionIndex;
|
||||
// sh_info for SHT_REL[A] sections should contain the section header index of
|
||||
// the section to which the relocation applies.
|
||||
InputSectionBase<ELFT> *S = Sections[0]->getRelocatedSection();
|
||||
InputSectionBase *S = First->getRelocatedSection();
|
||||
this->Info = S->OutSec->SectionIndex;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void OutputSection<ELFT>::addSection(InputSectionData *C) {
|
||||
assert(C->Live);
|
||||
auto *S = cast<InputSection<ELFT>>(C);
|
||||
void OutputSection::addSection(InputSection *S) {
|
||||
assert(S->Live);
|
||||
Sections.push_back(S);
|
||||
S->OutSec = this;
|
||||
this->updateAlignment(S->Alignment);
|
||||
// Keep sh_entsize value of the input section to be able to perform merging
|
||||
// later during a final linking using the generated relocatable object.
|
||||
if (Config->Relocatable && (S->Flags & SHF_MERGE))
|
||||
this->Entsize = S->Entsize;
|
||||
|
||||
// If this section contains a table of fixed-size entries, sh_entsize
|
||||
// holds the element size. Consequently, if this contains two or more
|
||||
// input sections, all of them must have the same sh_entsize. However,
|
||||
// you can put different types of input sections into one output
|
||||
// sectin by using linker scripts. I don't know what to do here.
|
||||
// Probably we sholuld handle that as an error. But for now we just
|
||||
// pick the largest sh_entsize.
|
||||
this->Entsize = std::max(this->Entsize, S->Entsize);
|
||||
}
|
||||
|
||||
// This function is called after we sort input sections
|
||||
// and scan relocations to setup sections' offsets.
|
||||
template <class ELFT> void OutputSection<ELFT>::assignOffsets() {
|
||||
uintX_t Off = this->Size;
|
||||
for (InputSection<ELFT> *S : Sections) {
|
||||
void OutputSection::assignOffsets() {
|
||||
uint64_t Off = 0;
|
||||
for (InputSection *S : Sections) {
|
||||
Off = alignTo(Off, S->Alignment);
|
||||
S->OutSecOff = Off;
|
||||
Off += S->getSize();
|
||||
|
|
@ -151,14 +139,12 @@ template <class ELFT> void OutputSection<ELFT>::assignOffsets() {
|
|||
this->Size = Off;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void OutputSection<ELFT>::sort(
|
||||
std::function<int(InputSection<ELFT> *S)> Order) {
|
||||
typedef std::pair<unsigned, InputSection<ELFT> *> Pair;
|
||||
void OutputSection::sort(std::function<int(InputSectionBase *S)> Order) {
|
||||
typedef std::pair<unsigned, InputSection *> Pair;
|
||||
auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; };
|
||||
|
||||
std::vector<Pair> V;
|
||||
for (InputSection<ELFT> *S : Sections)
|
||||
for (InputSection *S : Sections)
|
||||
V.push_back({Order(S), S});
|
||||
std::stable_sort(V.begin(), V.end(), Comp);
|
||||
Sections.clear();
|
||||
|
|
@ -172,9 +158,9 @@ void OutputSection<ELFT>::sort(
|
|||
// because the compiler keeps the original initialization order in a
|
||||
// translation unit and we need to respect that.
|
||||
// For more detail, read the section of the GCC's manual about init_priority.
|
||||
template <class ELFT> void OutputSection<ELFT>::sortInitFini() {
|
||||
void OutputSection::sortInitFini() {
|
||||
// Sort sections by priority.
|
||||
sort([](InputSection<ELFT> *S) { return getPriority(S->Name); });
|
||||
sort([](InputSectionBase *S) { return getPriority(S->Name); });
|
||||
}
|
||||
|
||||
// Returns true if S matches /Filename.?\.o$/.
|
||||
|
|
@ -208,15 +194,13 @@ static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); }
|
|||
// .ctors are duplicate features (and .init_array is newer.) However, there
|
||||
// are too many real-world use cases of .ctors, so we had no choice to
|
||||
// support that with this rather ad-hoc semantics.
|
||||
template <class ELFT>
|
||||
static bool compCtors(const InputSection<ELFT> *A,
|
||||
const InputSection<ELFT> *B) {
|
||||
bool BeginA = isCrtbegin(A->getFile()->getName());
|
||||
bool BeginB = isCrtbegin(B->getFile()->getName());
|
||||
static bool compCtors(const InputSection *A, const InputSection *B) {
|
||||
bool BeginA = isCrtbegin(A->File->getName());
|
||||
bool BeginB = isCrtbegin(B->File->getName());
|
||||
if (BeginA != BeginB)
|
||||
return BeginA;
|
||||
bool EndA = isCrtend(A->getFile()->getName());
|
||||
bool EndB = isCrtend(B->getFile()->getName());
|
||||
bool EndA = isCrtend(A->File->getName());
|
||||
bool EndB = isCrtend(B->File->getName());
|
||||
if (EndA != EndB)
|
||||
return EndB;
|
||||
StringRef X = A->Name;
|
||||
|
|
@ -233,319 +217,65 @@ static bool compCtors(const InputSection<ELFT> *A,
|
|||
// Sorts input sections by the special rules for .ctors and .dtors.
|
||||
// Unfortunately, the rules are different from the one for .{init,fini}_array.
|
||||
// Read the comment above.
|
||||
template <class ELFT> void OutputSection<ELFT>::sortCtorsDtors() {
|
||||
std::stable_sort(Sections.begin(), Sections.end(), compCtors<ELFT>);
|
||||
void OutputSection::sortCtorsDtors() {
|
||||
std::stable_sort(Sections.begin(), Sections.end(), compCtors);
|
||||
}
|
||||
|
||||
// Fill [Buf, Buf + Size) with Filler. Filler is written in big
|
||||
// endian order. This is used for linker script "=fillexp" command.
|
||||
void fill(uint8_t *Buf, size_t Size, uint32_t Filler) {
|
||||
uint8_t V[4];
|
||||
write32be(V, Filler);
|
||||
// Fill [Buf, Buf + Size) with Filler.
|
||||
// This is used for linker script "=fillexp" command.
|
||||
static void fill(uint8_t *Buf, size_t Size, uint32_t Filler) {
|
||||
size_t I = 0;
|
||||
for (; I + 4 < Size; I += 4)
|
||||
memcpy(Buf + I, V, 4);
|
||||
memcpy(Buf + I, V, Size - I);
|
||||
memcpy(Buf + I, &Filler, 4);
|
||||
memcpy(Buf + I, &Filler, Size - I);
|
||||
}
|
||||
|
||||
template <class ELFT> void OutputSection<ELFT>::writeTo(uint8_t *Buf) {
|
||||
Loc = Buf;
|
||||
if (uint32_t Filler = Script<ELFT>::X->getFiller(this->Name))
|
||||
fill(Buf, this->Size, Filler);
|
||||
uint32_t OutputSection::getFiller() {
|
||||
// Determine what to fill gaps between InputSections with, as specified by the
|
||||
// linker script. If nothing is specified and this is an executable section,
|
||||
// fall back to trap instructions to prevent bad diassembly and detect invalid
|
||||
// jumps to padding.
|
||||
if (Optional<uint32_t> Filler = Script->getFiller(Name))
|
||||
return *Filler;
|
||||
if (Flags & SHF_EXECINSTR)
|
||||
return Target->TrapInstr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto Fn = [=](InputSection<ELFT> *IS) { IS->writeTo(Buf); };
|
||||
forEach(Sections.begin(), Sections.end(), Fn);
|
||||
template <class ELFT> void OutputSection::writeTo(uint8_t *Buf) {
|
||||
Loc = Buf;
|
||||
|
||||
// Write leading padding.
|
||||
uint32_t Filler = getFiller();
|
||||
if (Filler)
|
||||
fill(Buf, Sections.empty() ? Size : Sections[0]->OutSecOff, Filler);
|
||||
|
||||
parallelFor(0, Sections.size(), [=](size_t I) {
|
||||
InputSection *Sec = Sections[I];
|
||||
Sec->writeTo<ELFT>(Buf);
|
||||
|
||||
// Fill gaps between sections.
|
||||
if (Filler) {
|
||||
uint8_t *Start = Buf + Sec->OutSecOff + Sec->getSize();
|
||||
uint8_t *End;
|
||||
if (I + 1 == Sections.size())
|
||||
End = Buf + Size;
|
||||
else
|
||||
End = Buf + Sections[I + 1]->OutSecOff;
|
||||
fill(Start, End - Start, Filler);
|
||||
}
|
||||
});
|
||||
|
||||
// Linker scripts may have BYTE()-family commands with which you
|
||||
// can write arbitrary bytes to the output. Process them if any.
|
||||
Script<ELFT>::X->writeDataBytes(this->Name, Buf);
|
||||
Script->writeDataBytes(Name, Buf);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
EhOutputSection<ELFT>::EhOutputSection()
|
||||
: OutputSectionBase(".eh_frame", SHT_PROGBITS, SHF_ALLOC) {}
|
||||
|
||||
// Search for an existing CIE record or create a new one.
|
||||
// CIE records from input object files are uniquified by their contents
|
||||
// and where their relocations point to.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
CieRecord *EhOutputSection<ELFT>::addCie(EhSectionPiece &Piece,
|
||||
ArrayRef<RelTy> Rels) {
|
||||
auto *Sec = cast<EhInputSection<ELFT>>(Piece.ID);
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
if (read32<E>(Piece.data().data() + 4) != 0)
|
||||
fatal(toString(Sec) + ": CIE expected at beginning of .eh_frame");
|
||||
|
||||
SymbolBody *Personality = nullptr;
|
||||
unsigned FirstRelI = Piece.FirstRelocation;
|
||||
if (FirstRelI != (unsigned)-1)
|
||||
Personality = &Sec->getFile()->getRelocTargetSym(Rels[FirstRelI]);
|
||||
|
||||
// Search for an existing CIE by CIE contents/relocation target pair.
|
||||
CieRecord *Cie = &CieMap[{Piece.data(), Personality}];
|
||||
|
||||
// If not found, create a new one.
|
||||
if (Cie->Piece == nullptr) {
|
||||
Cie->Piece = &Piece;
|
||||
Cies.push_back(Cie);
|
||||
}
|
||||
return Cie;
|
||||
}
|
||||
|
||||
// There is one FDE per function. Returns true if a given FDE
|
||||
// points to a live function.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
bool EhOutputSection<ELFT>::isFdeLive(EhSectionPiece &Piece,
|
||||
ArrayRef<RelTy> Rels) {
|
||||
auto *Sec = cast<EhInputSection<ELFT>>(Piece.ID);
|
||||
unsigned FirstRelI = Piece.FirstRelocation;
|
||||
if (FirstRelI == (unsigned)-1)
|
||||
fatal(toString(Sec) + ": FDE doesn't reference another section");
|
||||
const RelTy &Rel = Rels[FirstRelI];
|
||||
SymbolBody &B = Sec->getFile()->getRelocTargetSym(Rel);
|
||||
auto *D = dyn_cast<DefinedRegular<ELFT>>(&B);
|
||||
if (!D || !D->Section)
|
||||
return false;
|
||||
InputSectionBase<ELFT> *Target = D->Section->Repl;
|
||||
return Target && Target->Live;
|
||||
}
|
||||
|
||||
// .eh_frame is a sequence of CIE or FDE records. In general, there
|
||||
// is one CIE record per input object file which is followed by
|
||||
// a list of FDEs. This function searches an existing CIE or create a new
|
||||
// one and associates FDEs to the CIE.
|
||||
template <class ELFT>
|
||||
template <class RelTy>
|
||||
void EhOutputSection<ELFT>::addSectionAux(EhInputSection<ELFT> *Sec,
|
||||
ArrayRef<RelTy> Rels) {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
|
||||
DenseMap<size_t, CieRecord *> OffsetToCie;
|
||||
for (EhSectionPiece &Piece : Sec->Pieces) {
|
||||
// The empty record is the end marker.
|
||||
if (Piece.size() == 4)
|
||||
return;
|
||||
|
||||
size_t Offset = Piece.InputOff;
|
||||
uint32_t ID = read32<E>(Piece.data().data() + 4);
|
||||
if (ID == 0) {
|
||||
OffsetToCie[Offset] = addCie(Piece, Rels);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t CieOffset = Offset + 4 - ID;
|
||||
CieRecord *Cie = OffsetToCie[CieOffset];
|
||||
if (!Cie)
|
||||
fatal(toString(Sec) + ": invalid CIE reference");
|
||||
|
||||
if (!isFdeLive(Piece, Rels))
|
||||
continue;
|
||||
Cie->FdePieces.push_back(&Piece);
|
||||
NumFdes++;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void EhOutputSection<ELFT>::addSection(InputSectionData *C) {
|
||||
auto *Sec = cast<EhInputSection<ELFT>>(C);
|
||||
Sec->OutSec = this;
|
||||
this->updateAlignment(Sec->Alignment);
|
||||
Sections.push_back(Sec);
|
||||
|
||||
// .eh_frame is a sequence of CIE or FDE records. This function
|
||||
// splits it into pieces so that we can call
|
||||
// SplitInputSection::getSectionPiece on the section.
|
||||
Sec->split();
|
||||
if (Sec->Pieces.empty())
|
||||
return;
|
||||
|
||||
if (Sec->NumRelocations) {
|
||||
if (Sec->AreRelocsRela)
|
||||
addSectionAux(Sec, Sec->relas());
|
||||
else
|
||||
addSectionAux(Sec, Sec->rels());
|
||||
return;
|
||||
}
|
||||
addSectionAux(Sec, makeArrayRef<Elf_Rela>(nullptr, nullptr));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void writeCieFde(uint8_t *Buf, ArrayRef<uint8_t> D) {
|
||||
memcpy(Buf, D.data(), D.size());
|
||||
|
||||
// Fix the size field. -4 since size does not include the size field itself.
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
write32<E>(Buf, alignTo(D.size(), sizeof(typename ELFT::uint)) - 4);
|
||||
}
|
||||
|
||||
template <class ELFT> void EhOutputSection<ELFT>::finalize() {
|
||||
if (this->Size)
|
||||
return; // Already finalized.
|
||||
|
||||
size_t Off = 0;
|
||||
for (CieRecord *Cie : Cies) {
|
||||
Cie->Piece->OutputOff = Off;
|
||||
Off += alignTo(Cie->Piece->size(), sizeof(uintX_t));
|
||||
|
||||
for (EhSectionPiece *Fde : Cie->FdePieces) {
|
||||
Fde->OutputOff = Off;
|
||||
Off += alignTo(Fde->size(), sizeof(uintX_t));
|
||||
}
|
||||
}
|
||||
this->Size = Off;
|
||||
}
|
||||
|
||||
template <class ELFT> static uint64_t readFdeAddr(uint8_t *Buf, int Size) {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
switch (Size) {
|
||||
case DW_EH_PE_udata2:
|
||||
return read16<E>(Buf);
|
||||
case DW_EH_PE_udata4:
|
||||
return read32<E>(Buf);
|
||||
case DW_EH_PE_udata8:
|
||||
return read64<E>(Buf);
|
||||
case DW_EH_PE_absptr:
|
||||
if (ELFT::Is64Bits)
|
||||
return read64<E>(Buf);
|
||||
return read32<E>(Buf);
|
||||
}
|
||||
fatal("unknown FDE size encoding");
|
||||
}
|
||||
|
||||
// Returns the VA to which a given FDE (on a mmap'ed buffer) is applied to.
|
||||
// We need it to create .eh_frame_hdr section.
|
||||
template <class ELFT>
|
||||
typename ELFT::uint EhOutputSection<ELFT>::getFdePc(uint8_t *Buf, size_t FdeOff,
|
||||
uint8_t Enc) {
|
||||
// The starting address to which this FDE applies is
|
||||
// stored at FDE + 8 byte.
|
||||
size_t Off = FdeOff + 8;
|
||||
uint64_t Addr = readFdeAddr<ELFT>(Buf + Off, Enc & 0x7);
|
||||
if ((Enc & 0x70) == DW_EH_PE_absptr)
|
||||
return Addr;
|
||||
if ((Enc & 0x70) == DW_EH_PE_pcrel)
|
||||
return Addr + this->Addr + Off;
|
||||
fatal("unknown FDE size relative encoding");
|
||||
}
|
||||
|
||||
template <class ELFT> void EhOutputSection<ELFT>::writeTo(uint8_t *Buf) {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
for (CieRecord *Cie : Cies) {
|
||||
size_t CieOffset = Cie->Piece->OutputOff;
|
||||
writeCieFde<ELFT>(Buf + CieOffset, Cie->Piece->data());
|
||||
|
||||
for (EhSectionPiece *Fde : Cie->FdePieces) {
|
||||
size_t Off = Fde->OutputOff;
|
||||
writeCieFde<ELFT>(Buf + Off, Fde->data());
|
||||
|
||||
// FDE's second word should have the offset to an associated CIE.
|
||||
// Write it.
|
||||
write32<E>(Buf + Off + 4, Off + 4 - CieOffset);
|
||||
}
|
||||
}
|
||||
|
||||
for (EhInputSection<ELFT> *S : Sections)
|
||||
S->relocate(Buf, nullptr);
|
||||
|
||||
// Construct .eh_frame_hdr. .eh_frame_hdr is a binary search table
|
||||
// to get a FDE from an address to which FDE is applied. So here
|
||||
// we obtain two addresses and pass them to EhFrameHdr object.
|
||||
if (In<ELFT>::EhFrameHdr) {
|
||||
for (CieRecord *Cie : Cies) {
|
||||
uint8_t Enc = getFdeEncoding<ELFT>(Cie->Piece);
|
||||
for (SectionPiece *Fde : Cie->FdePieces) {
|
||||
uintX_t Pc = getFdePc(Buf, Fde->OutputOff, Enc);
|
||||
uintX_t FdeVA = this->Addr + Fde->OutputOff;
|
||||
In<ELFT>::EhFrameHdr->addFde(Pc, FdeVA);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
MergeOutputSection<ELFT>::MergeOutputSection(StringRef Name, uint32_t Type,
|
||||
uintX_t Flags, uintX_t Alignment)
|
||||
: OutputSectionBase(Name, Type, Flags),
|
||||
Builder(StringTableBuilder::RAW, Alignment) {}
|
||||
|
||||
template <class ELFT> void MergeOutputSection<ELFT>::writeTo(uint8_t *Buf) {
|
||||
Builder.write(Buf);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MergeOutputSection<ELFT>::addSection(InputSectionData *C) {
|
||||
auto *Sec = cast<MergeInputSection<ELFT>>(C);
|
||||
Sec->OutSec = this;
|
||||
this->updateAlignment(Sec->Alignment);
|
||||
this->Entsize = Sec->Entsize;
|
||||
Sections.push_back(Sec);
|
||||
}
|
||||
|
||||
template <class ELFT> bool MergeOutputSection<ELFT>::shouldTailMerge() const {
|
||||
return (this->Flags & SHF_STRINGS) && Config->Optimize >= 2;
|
||||
}
|
||||
|
||||
template <class ELFT> void MergeOutputSection<ELFT>::finalizeTailMerge() {
|
||||
// Add all string pieces to the string table builder to create section
|
||||
// contents.
|
||||
for (MergeInputSection<ELFT> *Sec : Sections)
|
||||
for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
|
||||
if (Sec->Pieces[I].Live)
|
||||
Builder.add(Sec->getData(I));
|
||||
|
||||
// Fix the string table content. After this, the contents will never change.
|
||||
Builder.finalize();
|
||||
this->Size = Builder.getSize();
|
||||
|
||||
// finalize() fixed tail-optimized strings, so we can now get
|
||||
// offsets of strings. Get an offset for each string and save it
|
||||
// to a corresponding StringPiece for easy access.
|
||||
for (MergeInputSection<ELFT> *Sec : Sections)
|
||||
for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
|
||||
if (Sec->Pieces[I].Live)
|
||||
Sec->Pieces[I].OutputOff = Builder.getOffset(Sec->getData(I));
|
||||
}
|
||||
|
||||
template <class ELFT> void MergeOutputSection<ELFT>::finalizeNoTailMerge() {
|
||||
// Add all string pieces to the string table builder to create section
|
||||
// contents. Because we are not tail-optimizing, offsets of strings are
|
||||
// fixed when they are added to the builder (string table builder contains
|
||||
// a hash table from strings to offsets).
|
||||
for (MergeInputSection<ELFT> *Sec : Sections)
|
||||
for (size_t I = 0, E = Sec->Pieces.size(); I != E; ++I)
|
||||
if (Sec->Pieces[I].Live)
|
||||
Sec->Pieces[I].OutputOff = Builder.add(Sec->getData(I));
|
||||
|
||||
Builder.finalizeInOrder();
|
||||
this->Size = Builder.getSize();
|
||||
}
|
||||
|
||||
template <class ELFT> void MergeOutputSection<ELFT>::finalize() {
|
||||
if (shouldTailMerge())
|
||||
finalizeTailMerge();
|
||||
else
|
||||
finalizeNoTailMerge();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static typename ELFT::uint getOutFlags(InputSectionBase<ELFT> *S) {
|
||||
static uint64_t getOutFlags(InputSectionBase *S) {
|
||||
return S->Flags & ~SHF_GROUP & ~SHF_COMPRESSED;
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
template <> struct DenseMapInfo<lld::elf::SectionKey> {
|
||||
static lld::elf::SectionKey getEmptyKey();
|
||||
static lld::elf::SectionKey getTombstoneKey();
|
||||
static unsigned getHashValue(const lld::elf::SectionKey &Val);
|
||||
static bool isEqual(const lld::elf::SectionKey &LHS,
|
||||
const lld::elf::SectionKey &RHS);
|
||||
};
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static SectionKey createKey(InputSectionBase<ELFT> *C, StringRef OutsecName) {
|
||||
static SectionKey createKey(InputSectionBase *C, StringRef OutsecName) {
|
||||
// The ELF spec just says
|
||||
// ----------------------------------------------------------------
|
||||
// In the first phase, input sections that match in name, type and
|
||||
|
|
@ -588,81 +318,76 @@ static SectionKey createKey(InputSectionBase<ELFT> *C, StringRef OutsecName) {
|
|||
//
|
||||
// Given the above issues, we instead merge sections by name and error on
|
||||
// incompatible types and flags.
|
||||
//
|
||||
// The exception being SHF_MERGE, where we create different output sections
|
||||
// for each alignment. This makes each output section simple. In case of
|
||||
// relocatable object generation we do not try to perform merging and treat
|
||||
// SHF_MERGE sections as regular ones, but also create different output
|
||||
// sections for them to allow merging at final linking stage.
|
||||
//
|
||||
// Fortunately, creating symbols in the middle of a merge section is not
|
||||
// supported by bfd or gold, so the SHF_MERGE exception should not cause
|
||||
// problems with most linker scripts.
|
||||
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
uintX_t Flags = C->Flags & (SHF_MERGE | SHF_STRINGS);
|
||||
|
||||
uintX_t Alignment = 0;
|
||||
if (isa<MergeInputSection<ELFT>>(C) ||
|
||||
(Config->Relocatable && (C->Flags & SHF_MERGE)))
|
||||
Alignment = std::max<uintX_t>(C->Alignment, C->Entsize);
|
||||
uint32_t Alignment = 0;
|
||||
uint64_t Flags = 0;
|
||||
if (Config->Relocatable && (C->Flags & SHF_MERGE)) {
|
||||
Alignment = std::max<uint64_t>(C->Alignment, C->Entsize);
|
||||
Flags = C->Flags & (SHF_MERGE | SHF_STRINGS);
|
||||
}
|
||||
|
||||
return SectionKey{OutsecName, Flags, Alignment};
|
||||
}
|
||||
|
||||
template <class ELFT> OutputSectionFactory<ELFT>::OutputSectionFactory() {}
|
||||
|
||||
template <class ELFT> OutputSectionFactory<ELFT>::~OutputSectionFactory() {}
|
||||
|
||||
template <class ELFT>
|
||||
std::pair<OutputSectionBase *, bool>
|
||||
OutputSectionFactory<ELFT>::create(InputSectionBase<ELFT> *C,
|
||||
StringRef OutsecName) {
|
||||
SectionKey Key = createKey(C, OutsecName);
|
||||
return create(Key, C);
|
||||
}
|
||||
OutputSectionFactory::OutputSectionFactory(
|
||||
std::vector<OutputSection *> &OutputSections)
|
||||
: OutputSections(OutputSections) {}
|
||||
|
||||
static uint64_t getIncompatibleFlags(uint64_t Flags) {
|
||||
return Flags & (SHF_ALLOC | SHF_TLS);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
std::pair<OutputSectionBase *, bool>
|
||||
OutputSectionFactory<ELFT>::create(const SectionKey &Key,
|
||||
InputSectionBase<ELFT> *C) {
|
||||
uintX_t Flags = getOutFlags(C);
|
||||
OutputSectionBase *&Sec = Map[Key];
|
||||
if (Sec) {
|
||||
if (getIncompatibleFlags(Sec->Flags) != getIncompatibleFlags(C->Flags))
|
||||
error("Section has flags incompatible with others with the same name " +
|
||||
toString(C));
|
||||
// Convert notbits to progbits if they are mixed. This happens is some
|
||||
// linker scripts.
|
||||
if (Sec->Type == SHT_NOBITS && C->Type == SHT_PROGBITS)
|
||||
Sec->Type = SHT_PROGBITS;
|
||||
if (Sec->Type != C->Type &&
|
||||
!(Sec->Type == SHT_PROGBITS && C->Type == SHT_NOBITS))
|
||||
error("Section has different type from others with the same name " +
|
||||
toString(C));
|
||||
Sec->Flags |= Flags;
|
||||
return {Sec, false};
|
||||
// We allow sections of types listed below to merged into a
|
||||
// single progbits section. This is typically done by linker
|
||||
// scripts. Merging nobits and progbits will force disk space
|
||||
// to be allocated for nobits sections. Other ones don't require
|
||||
// any special treatment on top of progbits, so there doesn't
|
||||
// seem to be a harm in merging them.
|
||||
static bool canMergeToProgbits(unsigned Type) {
|
||||
return Type == SHT_NOBITS || Type == SHT_PROGBITS || Type == SHT_INIT_ARRAY ||
|
||||
Type == SHT_PREINIT_ARRAY || Type == SHT_FINI_ARRAY ||
|
||||
Type == SHT_NOTE;
|
||||
}
|
||||
|
||||
static void reportDiscarded(InputSectionBase *IS) {
|
||||
if (!Config->PrintGcSections)
|
||||
return;
|
||||
message("removing unused section from '" + IS->Name + "' in file '" +
|
||||
IS->File->getName());
|
||||
}
|
||||
|
||||
void OutputSectionFactory::addInputSec(InputSectionBase *IS,
|
||||
StringRef OutsecName) {
|
||||
if (!IS->Live) {
|
||||
reportDiscarded(IS);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t Type = C->Type;
|
||||
switch (C->kind()) {
|
||||
case InputSectionBase<ELFT>::Regular:
|
||||
case InputSectionBase<ELFT>::Synthetic:
|
||||
Sec = make<OutputSection<ELFT>>(Key.Name, Type, Flags);
|
||||
break;
|
||||
case InputSectionBase<ELFT>::EHFrame:
|
||||
return {Out<ELFT>::EhFrame, false};
|
||||
case InputSectionBase<ELFT>::Merge:
|
||||
Sec = make<MergeOutputSection<ELFT>>(Key.Name, Type, Flags, Key.Alignment);
|
||||
break;
|
||||
SectionKey Key = createKey(IS, OutsecName);
|
||||
uint64_t Flags = getOutFlags(IS);
|
||||
OutputSection *&Sec = Map[Key];
|
||||
if (Sec) {
|
||||
if (getIncompatibleFlags(Sec->Flags) != getIncompatibleFlags(IS->Flags))
|
||||
error("Section has flags incompatible with others with the same name " +
|
||||
toString(IS));
|
||||
if (Sec->Type != IS->Type) {
|
||||
if (canMergeToProgbits(Sec->Type) && canMergeToProgbits(IS->Type))
|
||||
Sec->Type = SHT_PROGBITS;
|
||||
else
|
||||
error("Section has different type from others with the same name " +
|
||||
toString(IS));
|
||||
}
|
||||
Sec->Flags |= Flags;
|
||||
} else {
|
||||
Sec = make<OutputSection>(Key.Name, IS->Type, Flags);
|
||||
OutputSections.push_back(Sec);
|
||||
}
|
||||
return {Sec, true};
|
||||
|
||||
Sec->addSection(cast<InputSection>(IS));
|
||||
}
|
||||
|
||||
OutputSectionFactory::~OutputSectionFactory() {}
|
||||
|
||||
SectionKey DenseMapInfo<SectionKey>::getEmptyKey() {
|
||||
return SectionKey{DenseMapInfo<StringRef>::getEmptyKey(), 0, 0};
|
||||
}
|
||||
|
|
@ -681,32 +406,23 @@ bool DenseMapInfo<SectionKey>::isEqual(const SectionKey &LHS,
|
|||
LHS.Flags == RHS.Flags && LHS.Alignment == RHS.Alignment;
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
template void OutputSectionBase::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
|
||||
template void OutputSectionBase::writeHeaderTo<ELF32BE>(ELF32BE::Shdr *Shdr);
|
||||
template void OutputSectionBase::writeHeaderTo<ELF64LE>(ELF64LE::Shdr *Shdr);
|
||||
template void OutputSectionBase::writeHeaderTo<ELF64BE>(ELF64BE::Shdr *Shdr);
|
||||
|
||||
template class OutputSection<ELF32LE>;
|
||||
template class OutputSection<ELF32BE>;
|
||||
template class OutputSection<ELF64LE>;
|
||||
template class OutputSection<ELF64BE>;
|
||||
|
||||
template class EhOutputSection<ELF32LE>;
|
||||
template class EhOutputSection<ELF32BE>;
|
||||
template class EhOutputSection<ELF64LE>;
|
||||
template class EhOutputSection<ELF64BE>;
|
||||
|
||||
template class MergeOutputSection<ELF32LE>;
|
||||
template class MergeOutputSection<ELF32BE>;
|
||||
template class MergeOutputSection<ELF64LE>;
|
||||
template class MergeOutputSection<ELF64BE>;
|
||||
|
||||
template class OutputSectionFactory<ELF32LE>;
|
||||
template class OutputSectionFactory<ELF32BE>;
|
||||
template class OutputSectionFactory<ELF64LE>;
|
||||
template class OutputSectionFactory<ELF64BE>;
|
||||
}
|
||||
uint64_t elf::getHeaderSize() {
|
||||
if (Config->OFormatBinary)
|
||||
return 0;
|
||||
return Out::ElfHeader->Size + Out::ProgramHeaders->Size;
|
||||
}
|
||||
|
||||
template void OutputSection::writeHeaderTo<ELF32LE>(ELF32LE::Shdr *Shdr);
|
||||
template void OutputSection::writeHeaderTo<ELF32BE>(ELF32BE::Shdr *Shdr);
|
||||
template void OutputSection::writeHeaderTo<ELF64LE>(ELF64LE::Shdr *Shdr);
|
||||
template void OutputSection::writeHeaderTo<ELF64BE>(ELF64BE::Shdr *Shdr);
|
||||
|
||||
template void OutputSection::finalize<ELF32LE>();
|
||||
template void OutputSection::finalize<ELF32BE>();
|
||||
template void OutputSection::finalize<ELF64LE>();
|
||||
template void OutputSection::finalize<ELF64BE>();
|
||||
|
||||
template void OutputSection::writeTo<ELF32LE>(uint8_t *Buf);
|
||||
template void OutputSection::writeTo<ELF32BE>(uint8_t *Buf);
|
||||
template void OutputSection::writeTo<ELF64LE>(uint8_t *Buf);
|
||||
template void OutputSection::writeTo<ELF64BE>(uint8_t *Buf);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#define LLD_ELF_OUTPUT_SECTIONS_H
|
||||
|
||||
#include "Config.h"
|
||||
#include "InputSection.h"
|
||||
#include "Relocations.h"
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
|
|
@ -23,49 +24,38 @@ namespace elf {
|
|||
struct PhdrEntry;
|
||||
class SymbolBody;
|
||||
struct EhSectionPiece;
|
||||
template <class ELFT> class EhInputSection;
|
||||
template <class ELFT> class InputSection;
|
||||
template <class ELFT> class InputSectionBase;
|
||||
template <class ELFT> class MergeInputSection;
|
||||
template <class ELFT> class OutputSection;
|
||||
class EhInputSection;
|
||||
class InputSection;
|
||||
class InputSectionBase;
|
||||
class MergeInputSection;
|
||||
class OutputSection;
|
||||
template <class ELFT> class ObjectFile;
|
||||
template <class ELFT> class SharedFile;
|
||||
template <class ELFT> class SharedSymbol;
|
||||
template <class ELFT> class DefinedRegular;
|
||||
class SharedSymbol;
|
||||
class DefinedRegular;
|
||||
|
||||
// This represents a section in an output file.
|
||||
// Different sub classes represent different types of sections. Some contain
|
||||
// input sections, others are created by the linker.
|
||||
// It is composed of multiple InputSections.
|
||||
// The writer creates multiple OutputSections and assign them unique,
|
||||
// non-overlapping file offsets and VAs.
|
||||
class OutputSectionBase {
|
||||
class OutputSection final : public SectionBase {
|
||||
public:
|
||||
enum Kind {
|
||||
Base,
|
||||
EHFrame,
|
||||
Merge,
|
||||
Regular,
|
||||
};
|
||||
OutputSection(StringRef Name, uint32_t Type, uint64_t Flags);
|
||||
|
||||
static bool classof(const SectionBase *S) {
|
||||
return S->kind() == SectionBase::Output;
|
||||
}
|
||||
|
||||
OutputSectionBase(StringRef Name, uint32_t Type, uint64_t Flags);
|
||||
void setLMAOffset(uint64_t LMAOff) { LMAOffset = LMAOff; }
|
||||
uint64_t getLMA() const { return Addr + LMAOffset; }
|
||||
template <typename ELFT> void writeHeaderTo(typename ELFT::Shdr *SHdr);
|
||||
StringRef getName() const { return Name; }
|
||||
|
||||
virtual void addSection(InputSectionData *C) {}
|
||||
virtual Kind getKind() const { return Base; }
|
||||
static bool classof(const OutputSectionBase *B) {
|
||||
return B->getKind() == Base;
|
||||
}
|
||||
|
||||
unsigned SectionIndex;
|
||||
|
||||
uint32_t getPhdrFlags() const;
|
||||
|
||||
void updateAlignment(uint64_t Alignment) {
|
||||
if (Alignment > Addralign)
|
||||
Addralign = Alignment;
|
||||
void updateAlignment(uint32_t Val) {
|
||||
if (Val > Alignment)
|
||||
Alignment = Val;
|
||||
}
|
||||
|
||||
// If true, this section will be page aligned on disk.
|
||||
|
|
@ -78,191 +68,82 @@ public:
|
|||
// between their file offsets should be equal to difference between their
|
||||
// virtual addresses. To compute some section offset we use the following
|
||||
// formula: Off = Off_first + VA - VA_first.
|
||||
OutputSectionBase *FirstInPtLoad = nullptr;
|
||||
|
||||
virtual void finalize() {}
|
||||
virtual void assignOffsets() {}
|
||||
virtual void writeTo(uint8_t *Buf) {}
|
||||
virtual ~OutputSectionBase() = default;
|
||||
|
||||
StringRef Name;
|
||||
OutputSection *FirstInPtLoad = nullptr;
|
||||
|
||||
// The following fields correspond to Elf_Shdr members.
|
||||
uint64_t Size = 0;
|
||||
uint64_t Entsize = 0;
|
||||
uint64_t Addralign = 0;
|
||||
uint64_t Offset = 0;
|
||||
uint64_t Flags = 0;
|
||||
uint64_t LMAOffset = 0;
|
||||
uint64_t Addr = 0;
|
||||
uint32_t ShName = 0;
|
||||
uint32_t Type = 0;
|
||||
uint32_t Info = 0;
|
||||
uint32_t Link = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> class OutputSection final : public OutputSectionBase {
|
||||
|
||||
public:
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
OutputSection(StringRef Name, uint32_t Type, uintX_t Flags);
|
||||
void addSection(InputSectionData *C) override;
|
||||
void sort(std::function<int(InputSection<ELFT> *S)> Order);
|
||||
void addSection(InputSection *S);
|
||||
void sort(std::function<int(InputSectionBase *S)> Order);
|
||||
void sortInitFini();
|
||||
void sortCtorsDtors();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalize() override;
|
||||
void assignOffsets() override;
|
||||
Kind getKind() const override { return Regular; }
|
||||
static bool classof(const OutputSectionBase *B) {
|
||||
return B->getKind() == Regular;
|
||||
}
|
||||
std::vector<InputSection<ELFT> *> Sections;
|
||||
uint32_t getFiller();
|
||||
template <class ELFT> void writeTo(uint8_t *Buf);
|
||||
template <class ELFT> void finalize();
|
||||
void assignOffsets();
|
||||
std::vector<InputSection *> Sections;
|
||||
|
||||
// Location in the output buffer.
|
||||
uint8_t *Loc = nullptr;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class MergeOutputSection final : public OutputSectionBase {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
public:
|
||||
MergeOutputSection(StringRef Name, uint32_t Type, uintX_t Flags,
|
||||
uintX_t Alignment);
|
||||
void addSection(InputSectionData *S) override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalize() override;
|
||||
bool shouldTailMerge() const;
|
||||
Kind getKind() const override { return Merge; }
|
||||
static bool classof(const OutputSectionBase *B) {
|
||||
return B->getKind() == Merge;
|
||||
}
|
||||
|
||||
private:
|
||||
void finalizeTailMerge();
|
||||
void finalizeNoTailMerge();
|
||||
|
||||
llvm::StringTableBuilder Builder;
|
||||
std::vector<MergeInputSection<ELFT> *> Sections;
|
||||
};
|
||||
|
||||
struct CieRecord {
|
||||
EhSectionPiece *Piece = nullptr;
|
||||
std::vector<EhSectionPiece *> FdePieces;
|
||||
};
|
||||
|
||||
// Output section for .eh_frame.
|
||||
template <class ELFT> class EhOutputSection final : public OutputSectionBase {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
|
||||
public:
|
||||
EhOutputSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalize() override;
|
||||
bool empty() const { return Sections.empty(); }
|
||||
|
||||
void addSection(InputSectionData *S) override;
|
||||
Kind getKind() const override { return EHFrame; }
|
||||
static bool classof(const OutputSectionBase *B) {
|
||||
return B->getKind() == EHFrame;
|
||||
}
|
||||
|
||||
size_t NumFdes = 0;
|
||||
|
||||
private:
|
||||
template <class RelTy>
|
||||
void addSectionAux(EhInputSection<ELFT> *S, llvm::ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
CieRecord *addCie(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
bool isFdeLive(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
|
||||
|
||||
uintX_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc);
|
||||
|
||||
std::vector<EhInputSection<ELFT> *> Sections;
|
||||
std::vector<CieRecord *> Cies;
|
||||
|
||||
// CIE records are uniquified by their contents and personality functions.
|
||||
llvm::DenseMap<std::pair<ArrayRef<uint8_t>, SymbolBody *>, CieRecord> CieMap;
|
||||
};
|
||||
|
||||
// All output sections that are hadnled by the linker specially are
|
||||
// All output sections that are handled by the linker specially are
|
||||
// globally accessible. Writer initializes them, so don't use them
|
||||
// until Writer is initialized.
|
||||
template <class ELFT> struct Out {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
typedef typename ELFT::Phdr Elf_Phdr;
|
||||
|
||||
struct Out {
|
||||
static uint8_t First;
|
||||
static EhOutputSection<ELFT> *EhFrame;
|
||||
static OutputSection<ELFT> *Bss;
|
||||
static OutputSection<ELFT> *BssRelRo;
|
||||
static OutputSectionBase *Opd;
|
||||
static OutputSection *Opd;
|
||||
static uint8_t *OpdBuf;
|
||||
static PhdrEntry *TlsPhdr;
|
||||
static OutputSectionBase *DebugInfo;
|
||||
static OutputSectionBase *ElfHeader;
|
||||
static OutputSectionBase *ProgramHeaders;
|
||||
static OutputSectionBase *PreinitArray;
|
||||
static OutputSectionBase *InitArray;
|
||||
static OutputSectionBase *FiniArray;
|
||||
static OutputSection *DebugInfo;
|
||||
static OutputSection *ElfHeader;
|
||||
static OutputSection *ProgramHeaders;
|
||||
static OutputSection *PreinitArray;
|
||||
static OutputSection *InitArray;
|
||||
static OutputSection *FiniArray;
|
||||
};
|
||||
|
||||
struct SectionKey {
|
||||
StringRef Name;
|
||||
uint64_t Flags;
|
||||
uint64_t Alignment;
|
||||
uint32_t Alignment;
|
||||
};
|
||||
}
|
||||
}
|
||||
namespace llvm {
|
||||
template <> struct DenseMapInfo<lld::elf::SectionKey> {
|
||||
static lld::elf::SectionKey getEmptyKey();
|
||||
static lld::elf::SectionKey getTombstoneKey();
|
||||
static unsigned getHashValue(const lld::elf::SectionKey &Val);
|
||||
static bool isEqual(const lld::elf::SectionKey &LHS,
|
||||
const lld::elf::SectionKey &RHS);
|
||||
};
|
||||
}
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
// This class knows how to create an output section for a given
|
||||
// input section. Output section type is determined by various
|
||||
// factors, including input section's sh_flags, sh_type and
|
||||
// linker scripts.
|
||||
template <class ELFT> class OutputSectionFactory {
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class OutputSectionFactory {
|
||||
public:
|
||||
OutputSectionFactory();
|
||||
OutputSectionFactory(std::vector<OutputSection *> &OutputSections);
|
||||
~OutputSectionFactory();
|
||||
std::pair<OutputSectionBase *, bool> create(InputSectionBase<ELFT> *C,
|
||||
StringRef OutsecName);
|
||||
std::pair<OutputSectionBase *, bool> create(const SectionKey &Key,
|
||||
InputSectionBase<ELFT> *C);
|
||||
|
||||
void addInputSec(InputSectionBase *IS, StringRef OutsecName);
|
||||
|
||||
private:
|
||||
llvm::SmallDenseMap<SectionKey, OutputSectionBase *> Map;
|
||||
llvm::SmallDenseMap<SectionKey, OutputSection *> Map;
|
||||
std::vector<OutputSection *> &OutputSections;
|
||||
};
|
||||
|
||||
template <class ELFT> uint64_t getHeaderSize() {
|
||||
if (Config->OFormatBinary)
|
||||
return 0;
|
||||
return Out<ELFT>::ElfHeader->Size + Out<ELFT>::ProgramHeaders->Size;
|
||||
}
|
||||
uint64_t getHeaderSize();
|
||||
|
||||
template <class ELFT> uint8_t Out<ELFT>::First;
|
||||
template <class ELFT> EhOutputSection<ELFT> *Out<ELFT>::EhFrame;
|
||||
template <class ELFT> OutputSection<ELFT> *Out<ELFT>::Bss;
|
||||
template <class ELFT> OutputSection<ELFT> *Out<ELFT>::BssRelRo;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::Opd;
|
||||
template <class ELFT> uint8_t *Out<ELFT>::OpdBuf;
|
||||
template <class ELFT> PhdrEntry *Out<ELFT>::TlsPhdr;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::DebugInfo;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::ElfHeader;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::ProgramHeaders;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::PreinitArray;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::InitArray;
|
||||
template <class ELFT> OutputSectionBase *Out<ELFT>::FiniArray;
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
|
|
|
|||
1042
ELF/Relocations.cpp
1042
ELF/Relocations.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -11,13 +11,16 @@
|
|||
#define LLD_ELF_RELOCATIONS_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class SymbolBody;
|
||||
class InputSectionData;
|
||||
template <class ELFT> class InputSection;
|
||||
template <class ELFT> class InputSectionBase;
|
||||
class InputSection;
|
||||
class InputSectionBase;
|
||||
class OutputSection;
|
||||
|
||||
// List of target-independent relocation types. Relocations read
|
||||
// from files are converted to these types so that the main code
|
||||
|
|
@ -34,39 +37,39 @@ enum RelExpr {
|
|||
R_GOT_PAGE_PC,
|
||||
R_GOT_PC,
|
||||
R_HINT,
|
||||
R_MIPS_GOTREL,
|
||||
R_MIPS_GOT_GP,
|
||||
R_MIPS_GOT_GP_PC,
|
||||
R_MIPS_GOT_LOCAL_PAGE,
|
||||
R_MIPS_GOT_OFF,
|
||||
R_MIPS_GOT_OFF32,
|
||||
R_MIPS_GOTREL,
|
||||
R_MIPS_TLSGD,
|
||||
R_MIPS_TLSLD,
|
||||
R_NEG_TLS,
|
||||
R_NONE,
|
||||
R_PAGE_PC,
|
||||
R_PC,
|
||||
R_PLT,
|
||||
R_PLT_PC,
|
||||
R_PLT_PAGE_PC,
|
||||
R_PLT_PC,
|
||||
R_PPC_OPD,
|
||||
R_PPC_PLT_OPD,
|
||||
R_PPC_TOC,
|
||||
R_RELAX_GOT_PC,
|
||||
R_RELAX_GOT_PC_NOPIC,
|
||||
R_RELAX_TLS_GD_TO_IE,
|
||||
R_RELAX_TLS_GD_TO_IE_END,
|
||||
R_RELAX_TLS_GD_TO_IE_ABS,
|
||||
R_RELAX_TLS_GD_TO_IE_END,
|
||||
R_RELAX_TLS_GD_TO_IE_PAGE_PC,
|
||||
R_RELAX_TLS_GD_TO_LE,
|
||||
R_RELAX_TLS_GD_TO_LE_NEG,
|
||||
R_RELAX_TLS_IE_TO_LE,
|
||||
R_RELAX_TLS_LD_TO_LE,
|
||||
R_SIZE,
|
||||
R_THUNK_ABS,
|
||||
R_THUNK_PC,
|
||||
R_THUNK_PLT_PC,
|
||||
R_TLS,
|
||||
R_TLSDESC,
|
||||
R_TLSDESC_PAGE,
|
||||
R_TLSDESC_CALL,
|
||||
R_TLSDESC_PAGE,
|
||||
R_TLSGD,
|
||||
R_TLSGD_PC,
|
||||
R_TLSLD,
|
||||
|
|
@ -107,21 +110,44 @@ struct Relocation {
|
|||
RelExpr Expr;
|
||||
uint32_t Type;
|
||||
uint64_t Offset;
|
||||
uint64_t Addend;
|
||||
int64_t Addend;
|
||||
SymbolBody *Sym;
|
||||
};
|
||||
|
||||
template <class ELFT> void scanRelocations(InputSectionBase<ELFT> &);
|
||||
template <class ELFT> void scanRelocations(InputSectionBase &);
|
||||
|
||||
template <class ELFT> void createThunks(InputSectionBase<ELFT> &);
|
||||
class ThunkSection;
|
||||
class Thunk;
|
||||
|
||||
template <class ELFT> class ThunkCreator {
|
||||
public:
|
||||
// Return true if Thunks have been added to OutputSections
|
||||
bool createThunks(ArrayRef<OutputSection *> OutputSections);
|
||||
|
||||
private:
|
||||
void mergeThunks(OutputSection *OS, std::vector<ThunkSection *> &Thunks);
|
||||
ThunkSection *getOSThunkSec(ThunkSection *&TS, OutputSection *OS);
|
||||
ThunkSection *getISThunkSec(InputSection *IS, OutputSection *OS);
|
||||
std::pair<Thunk *, bool> getThunk(SymbolBody &Body, uint32_t Type);
|
||||
|
||||
// Track Symbols that already have a Thunk
|
||||
llvm::DenseMap<SymbolBody *, Thunk *> ThunkedSymbols;
|
||||
|
||||
// Track InputSections that have a ThunkSection placed in front
|
||||
llvm::DenseMap<InputSection *, ThunkSection *> ThunkedSections;
|
||||
|
||||
// Track the ThunksSections that need to be inserted into an OutputSection
|
||||
std::map<OutputSection *, std::vector<ThunkSection *>> ThunkSections;
|
||||
};
|
||||
|
||||
// Return a int64_t to make sure we get the sign extension out of the way as
|
||||
// early as possible.
|
||||
template <class ELFT>
|
||||
static inline typename ELFT::uint getAddend(const typename ELFT::Rel &Rel) {
|
||||
static inline int64_t getAddend(const typename ELFT::Rel &Rel) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static inline typename ELFT::uint getAddend(const typename ELFT::Rela &Rel) {
|
||||
static inline int64_t getAddend(const typename ELFT::Rela &Rel) {
|
||||
return Rel.r_addend;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
285
ELF/ScriptLexer.cpp
Normal file
285
ELF/ScriptLexer.cpp
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
//===- ScriptLexer.cpp ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines a lexer for the linker script.
|
||||
//
|
||||
// The linker script's grammar is not complex but ambiguous due to the
|
||||
// lack of the formal specification of the language. What we are trying to
|
||||
// do in this and other files in LLD is to make a "reasonable" linker
|
||||
// script processor.
|
||||
//
|
||||
// Among simplicity, compatibility and efficiency, we put the most
|
||||
// emphasis on simplicity when we wrote this lexer. Compatibility with the
|
||||
// GNU linkers is important, but we did not try to clone every tiny corner
|
||||
// case of their lexers, as even ld.bfd and ld.gold are subtly different
|
||||
// in various corner cases. We do not care much about efficiency because
|
||||
// the time spent in parsing linker scripts is usually negligible.
|
||||
//
|
||||
// Our grammar of the linker script is LL(2), meaning that it needs at
|
||||
// most two-token lookahead to parse. The only place we need two-token
|
||||
// lookahead is labels in version scripts, where we need to parse "local :"
|
||||
// as if "local:".
|
||||
//
|
||||
// Overall, this lexer works fine for most linker scripts. There might
|
||||
// be room for improving compatibility, but that's probably not at the
|
||||
// top of our todo list.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ScriptLexer.h"
|
||||
#include "Error.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
// Returns a whole line containing the current token.
|
||||
StringRef ScriptLexer::getLine() {
|
||||
StringRef S = getCurrentMB().getBuffer();
|
||||
StringRef Tok = Tokens[Pos - 1];
|
||||
|
||||
size_t Pos = S.rfind('\n', Tok.data() - S.data());
|
||||
if (Pos != StringRef::npos)
|
||||
S = S.substr(Pos + 1);
|
||||
return S.substr(0, S.find_first_of("\r\n"));
|
||||
}
|
||||
|
||||
// Returns 1-based line number of the current token.
|
||||
size_t ScriptLexer::getLineNumber() {
|
||||
StringRef S = getCurrentMB().getBuffer();
|
||||
StringRef Tok = Tokens[Pos - 1];
|
||||
return S.substr(0, Tok.data() - S.data()).count('\n') + 1;
|
||||
}
|
||||
|
||||
// Returns 0-based column number of the current token.
|
||||
size_t ScriptLexer::getColumnNumber() {
|
||||
StringRef Tok = Tokens[Pos - 1];
|
||||
return Tok.data() - getLine().data();
|
||||
}
|
||||
|
||||
std::string ScriptLexer::getCurrentLocation() {
|
||||
std::string Filename = getCurrentMB().getBufferIdentifier();
|
||||
if (!Pos)
|
||||
return Filename;
|
||||
return (Filename + ":" + Twine(getLineNumber())).str();
|
||||
}
|
||||
|
||||
ScriptLexer::ScriptLexer(MemoryBufferRef MB) { tokenize(MB); }
|
||||
|
||||
// We don't want to record cascading errors. Keep only the first one.
|
||||
void ScriptLexer::setError(const Twine &Msg) {
|
||||
if (Error)
|
||||
return;
|
||||
Error = true;
|
||||
|
||||
if (!Pos) {
|
||||
error(getCurrentLocation() + ": " + Msg);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string S = getCurrentLocation() + ": ";
|
||||
error(S + Msg);
|
||||
error(S + getLine());
|
||||
error(S + std::string(getColumnNumber(), ' ') + "^");
|
||||
}
|
||||
|
||||
// Split S into linker script tokens.
|
||||
void ScriptLexer::tokenize(MemoryBufferRef MB) {
|
||||
std::vector<StringRef> Vec;
|
||||
MBs.push_back(MB);
|
||||
StringRef S = MB.getBuffer();
|
||||
StringRef Begin = S;
|
||||
|
||||
for (;;) {
|
||||
S = skipSpace(S);
|
||||
if (S.empty())
|
||||
break;
|
||||
|
||||
// Quoted token. Note that double-quote characters are parts of a token
|
||||
// because, in a glob match context, only unquoted tokens are interpreted
|
||||
// as glob patterns. Double-quoted tokens are literal patterns in that
|
||||
// context.
|
||||
if (S.startswith("\"")) {
|
||||
size_t E = S.find("\"", 1);
|
||||
if (E == StringRef::npos) {
|
||||
StringRef Filename = MB.getBufferIdentifier();
|
||||
size_t Lineno = Begin.substr(0, S.data() - Begin.data()).count('\n');
|
||||
error(Filename + ":" + Twine(Lineno + 1) + ": unclosed quote");
|
||||
return;
|
||||
}
|
||||
|
||||
Vec.push_back(S.take_front(E + 1));
|
||||
S = S.substr(E + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unquoted token. This is more relaxed than tokens in C-like language,
|
||||
// so that you can write "file-name.cpp" as one bare token, for example.
|
||||
size_t Pos = S.find_first_not_of(
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789_.$/\\~=+[]*?-!<>^:");
|
||||
|
||||
// A character that cannot start a word (which is usually a
|
||||
// punctuation) forms a single character token.
|
||||
if (Pos == 0)
|
||||
Pos = 1;
|
||||
Vec.push_back(S.substr(0, Pos));
|
||||
S = S.substr(Pos);
|
||||
}
|
||||
|
||||
Tokens.insert(Tokens.begin() + Pos, Vec.begin(), Vec.end());
|
||||
}
|
||||
|
||||
// Skip leading whitespace characters or comments.
|
||||
StringRef ScriptLexer::skipSpace(StringRef S) {
|
||||
for (;;) {
|
||||
if (S.startswith("/*")) {
|
||||
size_t E = S.find("*/", 2);
|
||||
if (E == StringRef::npos) {
|
||||
error("unclosed comment in a linker script");
|
||||
return "";
|
||||
}
|
||||
S = S.substr(E + 2);
|
||||
continue;
|
||||
}
|
||||
if (S.startswith("#")) {
|
||||
size_t E = S.find('\n', 1);
|
||||
if (E == StringRef::npos)
|
||||
E = S.size() - 1;
|
||||
S = S.substr(E + 1);
|
||||
continue;
|
||||
}
|
||||
size_t Size = S.size();
|
||||
S = S.ltrim();
|
||||
if (S.size() == Size)
|
||||
return S;
|
||||
}
|
||||
}
|
||||
|
||||
// An erroneous token is handled as if it were the last token before EOF.
|
||||
bool ScriptLexer::atEOF() { return Error || Tokens.size() == Pos; }
|
||||
|
||||
// Split a given string as an expression.
|
||||
// This function returns "3", "*" and "5" for "3*5" for example.
|
||||
static std::vector<StringRef> tokenizeExpr(StringRef S) {
|
||||
StringRef Ops = "+-*/:"; // List of operators
|
||||
|
||||
// Quoted strings are literal strings, so we don't want to split it.
|
||||
if (S.startswith("\""))
|
||||
return {S};
|
||||
|
||||
// Split S with +-*/ as separators.
|
||||
std::vector<StringRef> Ret;
|
||||
while (!S.empty()) {
|
||||
size_t E = S.find_first_of(Ops);
|
||||
|
||||
// No need to split if there is no operator.
|
||||
if (E == StringRef::npos) {
|
||||
Ret.push_back(S);
|
||||
break;
|
||||
}
|
||||
|
||||
// Get a token before the opreator.
|
||||
if (E != 0)
|
||||
Ret.push_back(S.substr(0, E));
|
||||
|
||||
// Get the operator as a token.
|
||||
Ret.push_back(S.substr(E, 1));
|
||||
S = S.substr(E + 1);
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// In contexts where expressions are expected, the lexer should apply
|
||||
// different tokenization rules than the default one. By default,
|
||||
// arithmetic operator characters are regular characters, but in the
|
||||
// expression context, they should be independent tokens.
|
||||
//
|
||||
// For example, "foo*3" should be tokenized to "foo", "*" and "3" only
|
||||
// in the expression context.
|
||||
//
|
||||
// This function may split the current token into multiple tokens.
|
||||
void ScriptLexer::maybeSplitExpr() {
|
||||
if (!InExpr || Error || atEOF())
|
||||
return;
|
||||
|
||||
std::vector<StringRef> V = tokenizeExpr(Tokens[Pos]);
|
||||
if (V.size() == 1)
|
||||
return;
|
||||
Tokens.erase(Tokens.begin() + Pos);
|
||||
Tokens.insert(Tokens.begin() + Pos, V.begin(), V.end());
|
||||
}
|
||||
|
||||
StringRef ScriptLexer::next() {
|
||||
maybeSplitExpr();
|
||||
|
||||
if (Error)
|
||||
return "";
|
||||
if (atEOF()) {
|
||||
setError("unexpected EOF");
|
||||
return "";
|
||||
}
|
||||
return Tokens[Pos++];
|
||||
}
|
||||
|
||||
StringRef ScriptLexer::peek() {
|
||||
StringRef Tok = next();
|
||||
if (Error)
|
||||
return "";
|
||||
Pos = Pos - 1;
|
||||
return Tok;
|
||||
}
|
||||
|
||||
bool ScriptLexer::consume(StringRef Tok) {
|
||||
if (peek() == Tok) {
|
||||
skip();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Consumes Tok followed by ":". Space is allowed between Tok and ":".
|
||||
bool ScriptLexer::consumeLabel(StringRef Tok) {
|
||||
if (consume((Tok + ":").str()))
|
||||
return true;
|
||||
if (Tokens.size() >= Pos + 2 && Tokens[Pos] == Tok &&
|
||||
Tokens[Pos + 1] == ":") {
|
||||
Pos += 2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptLexer::skip() { (void)next(); }
|
||||
|
||||
void ScriptLexer::expect(StringRef Expect) {
|
||||
if (Error)
|
||||
return;
|
||||
StringRef Tok = next();
|
||||
if (Tok != Expect)
|
||||
setError(Expect + " expected, but got " + Tok);
|
||||
}
|
||||
|
||||
// Returns true if S encloses T.
|
||||
static bool encloses(StringRef S, StringRef T) {
|
||||
return S.bytes_begin() <= T.bytes_begin() && T.bytes_end() <= S.bytes_end();
|
||||
}
|
||||
|
||||
MemoryBufferRef ScriptLexer::getCurrentMB() {
|
||||
// Find input buffer containing the current token.
|
||||
assert(!MBs.empty());
|
||||
if (!Pos)
|
||||
return MBs[0];
|
||||
|
||||
for (MemoryBufferRef MB : MBs)
|
||||
if (encloses(MB.getBuffer(), Tokens[Pos - 1]))
|
||||
return MB;
|
||||
llvm_unreachable("getCurrentMB: failed to find a token");
|
||||
}
|
||||
56
ELF/ScriptLexer.h
Normal file
56
ELF/ScriptLexer.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//===- ScriptLexer.h --------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_SCRIPT_LEXER_H
|
||||
#define LLD_ELF_SCRIPT_LEXER_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class ScriptLexer {
|
||||
public:
|
||||
explicit ScriptLexer(MemoryBufferRef MB);
|
||||
|
||||
void setError(const Twine &Msg);
|
||||
void tokenize(MemoryBufferRef MB);
|
||||
static StringRef skipSpace(StringRef S);
|
||||
bool atEOF();
|
||||
StringRef next();
|
||||
StringRef peek();
|
||||
void skip();
|
||||
bool consume(StringRef Tok);
|
||||
void expect(StringRef Expect);
|
||||
bool consumeLabel(StringRef Tok);
|
||||
std::string getCurrentLocation();
|
||||
|
||||
std::vector<MemoryBufferRef> MBs;
|
||||
std::vector<StringRef> Tokens;
|
||||
bool InExpr = false;
|
||||
size_t Pos = 0;
|
||||
bool Error = false;
|
||||
|
||||
private:
|
||||
void maybeSplitExpr();
|
||||
StringRef getLine();
|
||||
size_t getLineNumber();
|
||||
size_t getColumnNumber();
|
||||
|
||||
MemoryBufferRef getCurrentMB();
|
||||
};
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
||||
#endif
|
||||
1297
ELF/ScriptParser.cpp
1297
ELF/ScriptParser.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -11,41 +11,19 @@
|
|||
#define LLD_ELF_SCRIPT_PARSER_H
|
||||
|
||||
#include "lld/Core/LLVM.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/MemoryBuffer.h"
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
class ScriptParserBase {
|
||||
public:
|
||||
explicit ScriptParserBase(MemoryBufferRef MB);
|
||||
// Parses a linker script. Calling this function updates
|
||||
// Config and ScriptConfig.
|
||||
void readLinkerScript(MemoryBufferRef MB);
|
||||
|
||||
void setError(const Twine &Msg);
|
||||
void tokenize(MemoryBufferRef MB);
|
||||
static StringRef skipSpace(StringRef S);
|
||||
bool atEOF();
|
||||
StringRef next();
|
||||
StringRef peek();
|
||||
void skip();
|
||||
bool consume(StringRef Tok);
|
||||
void expect(StringRef Expect);
|
||||
std::string getCurrentLocation();
|
||||
// Parses a version script.
|
||||
void readVersionScript(MemoryBufferRef MB);
|
||||
|
||||
std::vector<MemoryBufferRef> MBs;
|
||||
std::vector<StringRef> Tokens;
|
||||
size_t Pos = 0;
|
||||
bool Error = false;
|
||||
|
||||
private:
|
||||
StringRef getLine();
|
||||
size_t getLineNumber();
|
||||
size_t getColumnNumber();
|
||||
|
||||
MemoryBufferRef getCurrentMB();
|
||||
};
|
||||
void readDynamicList(MemoryBufferRef MB);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
|
|
|||
|
|
@ -91,9 +91,9 @@ bool elf::isValidCIdentifier(StringRef S) {
|
|||
|
||||
// Returns the demangled C++ symbol name for Name.
|
||||
Optional<std::string> elf::demangle(StringRef Name) {
|
||||
// __cxa_demangle 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
|
||||
// either a C or C++ symbol. Don't call __cxa_demangle if the name
|
||||
// either a C or C++ symbol. Don't call itaniumDemangle if the name
|
||||
// does not look like a C++ symbol name to avoid getting unexpected
|
||||
// result for a C symbol that happens to match a mangled type name.
|
||||
if (!Name.startswith("_Z"))
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ template <class ELFT> void SymbolTable<ELFT>::addFile(InputFile *File) {
|
|||
}
|
||||
|
||||
if (Config->Trace)
|
||||
outs() << toString(File) << "\n";
|
||||
message(toString(File));
|
||||
|
||||
// .so file
|
||||
if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) {
|
||||
|
|
@ -115,7 +115,7 @@ template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() {
|
|||
// Compile bitcode files and replace bitcode symbols.
|
||||
LTO.reset(new BitcodeCompiler);
|
||||
for (BitcodeFile *F : BitcodeFiles)
|
||||
LTO->add<ELFT>(*F);
|
||||
LTO->add(*F);
|
||||
|
||||
for (InputFile *File : LTO->compile()) {
|
||||
ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(File);
|
||||
|
|
@ -126,19 +126,19 @@ template <class ELFT> void SymbolTable<ELFT>::addCombinedLTOObject() {
|
|||
}
|
||||
|
||||
template <class ELFT>
|
||||
DefinedRegular<ELFT> *SymbolTable<ELFT>::addAbsolute(StringRef Name,
|
||||
uint8_t Visibility,
|
||||
uint8_t Binding) {
|
||||
DefinedRegular *SymbolTable<ELFT>::addAbsolute(StringRef Name,
|
||||
uint8_t Visibility,
|
||||
uint8_t Binding) {
|
||||
Symbol *Sym =
|
||||
addRegular(Name, Visibility, STT_NOTYPE, 0, 0, Binding, nullptr, nullptr);
|
||||
return cast<DefinedRegular<ELFT>>(Sym->body());
|
||||
return cast<DefinedRegular>(Sym->body());
|
||||
}
|
||||
|
||||
// Add Name as an "ignored" symbol. An ignored symbol is a regular
|
||||
// linker-synthesized defined symbol, but is only defined if needed.
|
||||
template <class ELFT>
|
||||
DefinedRegular<ELFT> *SymbolTable<ELFT>::addIgnored(StringRef Name,
|
||||
uint8_t Visibility) {
|
||||
DefinedRegular *SymbolTable<ELFT>::addIgnored(StringRef Name,
|
||||
uint8_t Visibility) {
|
||||
SymbolBody *S = find(Name);
|
||||
if (!S || S->isInCurrentDSO())
|
||||
return nullptr;
|
||||
|
|
@ -191,7 +191,7 @@ std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
|
|||
|
||||
Symbol *Sym;
|
||||
if (IsNew) {
|
||||
Sym = new (BAlloc) Symbol;
|
||||
Sym = make<Symbol>();
|
||||
Sym->InVersionScript = false;
|
||||
Sym->Binding = STB_WEAK;
|
||||
Sym->Visibility = STV_DEFAULT;
|
||||
|
|
@ -206,13 +206,6 @@ std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
|
|||
return {Sym, IsNew};
|
||||
}
|
||||
|
||||
// Construct a string in the form of "Sym in File1 and File2".
|
||||
// Used to construct an error message.
|
||||
static std::string conflictMsg(SymbolBody *Existing, InputFile *NewFile) {
|
||||
return "'" + toString(*Existing) + "' in " + toString(Existing->File) +
|
||||
" and " + toString(NewFile);
|
||||
}
|
||||
|
||||
// Find an existing symbol or create and insert a new one, then apply the given
|
||||
// attributes.
|
||||
template <class ELFT>
|
||||
|
|
@ -226,13 +219,19 @@ SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility,
|
|||
|
||||
// Merge in the new symbol's visibility.
|
||||
S->Visibility = getMinVisibility(S->Visibility, Visibility);
|
||||
|
||||
if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic))
|
||||
S->ExportDynamic = true;
|
||||
|
||||
if (IsUsedInRegularObj)
|
||||
S->IsUsedInRegularObj = true;
|
||||
|
||||
if (!WasInserted && S->body()->Type != SymbolBody::UnknownType &&
|
||||
((Type == STT_TLS) != S->body()->isTls()))
|
||||
error("TLS attribute mismatch for symbol " + conflictMsg(S->body(), File));
|
||||
((Type == STT_TLS) != S->body()->isTls())) {
|
||||
error("TLS attribute mismatch: " + toString(*S->body()) +
|
||||
"\n>>> defined in " + toString(S->body()->File) +
|
||||
"\n>>> defined in " + toString(File));
|
||||
}
|
||||
|
||||
return {S, WasInserted};
|
||||
}
|
||||
|
|
@ -252,18 +251,22 @@ Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, bool IsLocal,
|
|||
InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
uint8_t Visibility = getVisibility(StOther);
|
||||
std::tie(S, WasInserted) =
|
||||
insert(Name, Type, getVisibility(StOther), CanOmitFromDynSym, File);
|
||||
if (WasInserted) {
|
||||
insert(Name, Type, Visibility, CanOmitFromDynSym, File);
|
||||
// An undefined symbol with non default visibility must be satisfied
|
||||
// in the same DSO.
|
||||
if (WasInserted ||
|
||||
(isa<SharedSymbol>(S->body()) && Visibility != STV_DEFAULT)) {
|
||||
S->Binding = Binding;
|
||||
replaceBody<Undefined<ELFT>>(S, Name, IsLocal, StOther, Type, File);
|
||||
replaceBody<Undefined>(S, Name, IsLocal, StOther, Type, File);
|
||||
return S;
|
||||
}
|
||||
if (Binding != STB_WEAK) {
|
||||
if (S->body()->isShared() || S->body()->isLazy())
|
||||
S->Binding = Binding;
|
||||
if (auto *SS = dyn_cast<SharedSymbol<ELFT>>(S->body()))
|
||||
SS->file()->IsUsed = true;
|
||||
if (auto *SS = dyn_cast<SharedSymbol>(S->body()))
|
||||
cast<SharedFile<ELFT>>(SS->File)->IsUsed = true;
|
||||
}
|
||||
if (auto *L = dyn_cast<Lazy>(S->body())) {
|
||||
// An undefined weak will not fetch archive members, but we have to remember
|
||||
|
|
@ -309,7 +312,7 @@ static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
|
|||
if (Config->WarnCommon)
|
||||
warn("common " + S->body()->getName() + " is overridden");
|
||||
return 1;
|
||||
} else if (auto *R = dyn_cast<DefinedRegular<ELFT>>(B)) {
|
||||
} else if (auto *R = dyn_cast<DefinedRegular>(B)) {
|
||||
if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute &&
|
||||
R->Value == Value)
|
||||
return -1;
|
||||
|
|
@ -319,7 +322,7 @@ static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding,
|
|||
|
||||
template <class ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
|
||||
uint64_t Alignment, uint8_t Binding,
|
||||
uint32_t Alignment, uint8_t Binding,
|
||||
uint8_t StOther, uint8_t Type,
|
||||
InputFile *File) {
|
||||
Symbol *S;
|
||||
|
|
@ -349,40 +352,56 @@ Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size,
|
|||
return S;
|
||||
}
|
||||
|
||||
static void print(const Twine &Msg) {
|
||||
static void warnOrError(const Twine &Msg) {
|
||||
if (Config->AllowMultipleDefinition)
|
||||
warn(Msg);
|
||||
else
|
||||
error(Msg);
|
||||
}
|
||||
|
||||
static void reportDuplicate(SymbolBody *Existing, InputFile *NewFile) {
|
||||
print("duplicate symbol " + conflictMsg(Existing, NewFile));
|
||||
static void reportDuplicate(SymbolBody *Sym, InputFile *NewFile) {
|
||||
warnOrError("duplicate symbol: " + toString(*Sym) +
|
||||
"\n>>> defined in " + toString(Sym->File) +
|
||||
"\n>>> defined in " + toString(NewFile));
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void reportDuplicate(SymbolBody *Existing,
|
||||
InputSectionBase<ELFT> *ErrSec,
|
||||
static void reportDuplicate(SymbolBody *Sym, InputSectionBase *ErrSec,
|
||||
typename ELFT::uint ErrOffset) {
|
||||
DefinedRegular<ELFT> *D = dyn_cast<DefinedRegular<ELFT>>(Existing);
|
||||
DefinedRegular *D = dyn_cast<DefinedRegular>(Sym);
|
||||
if (!D || !D->Section || !ErrSec) {
|
||||
reportDuplicate(Existing, ErrSec ? ErrSec->getFile() : nullptr);
|
||||
reportDuplicate(Sym, ErrSec ? ErrSec->getFile<ELFT>() : nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string OldLoc = D->Section->getLocation(D->Value);
|
||||
std::string NewLoc = ErrSec->getLocation(ErrOffset);
|
||||
// Construct and print an error message in the form of:
|
||||
//
|
||||
// ld.lld: error: duplicate symbol: foo
|
||||
// >>> defined at bar.c:30
|
||||
// >>> bar.o (/home/alice/src/bar.o)
|
||||
// >>> defined at baz.c:563
|
||||
// >>> baz.o in archive libbaz.a
|
||||
auto *Sec1 = cast<InputSectionBase>(D->Section);
|
||||
std::string Src1 = Sec1->getSrcMsg<ELFT>(D->Value);
|
||||
std::string Obj1 = Sec1->getObjMsg<ELFT>(D->Value);
|
||||
std::string Src2 = ErrSec->getSrcMsg<ELFT>(ErrOffset);
|
||||
std::string Obj2 = ErrSec->getObjMsg<ELFT>(ErrOffset);
|
||||
|
||||
print(NewLoc + ": duplicate symbol '" + toString(*Existing) + "'");
|
||||
print(OldLoc + ": previous definition was here");
|
||||
std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at ";
|
||||
if (!Src1.empty())
|
||||
Msg += Src1 + "\n>>> ";
|
||||
Msg += Obj1 + "\n>>> defined at ";
|
||||
if (!Src2.empty())
|
||||
Msg += Src2 + "\n>>> ";
|
||||
Msg += Obj2;
|
||||
warnOrError(Msg);
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t StOther,
|
||||
uint8_t Type, uintX_t Value, uintX_t Size,
|
||||
uint8_t Binding,
|
||||
InputSectionBase<ELFT> *Section,
|
||||
InputFile *File) {
|
||||
uint8_t Type, uint64_t Value,
|
||||
uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(Name, Type, getVisibility(StOther),
|
||||
|
|
@ -390,32 +409,16 @@ Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t StOther,
|
|||
int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
|
||||
Section == nullptr, Value);
|
||||
if (Cmp > 0)
|
||||
replaceBody<DefinedRegular<ELFT>>(S, Name, /*IsLocal=*/false, StOther, Type,
|
||||
Value, Size, Section, File);
|
||||
replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type,
|
||||
Value, Size, Section, File);
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate(S->body(), Section, Value);
|
||||
reportDuplicate<ELFT>(S->body(),
|
||||
dyn_cast_or_null<InputSectionBase>(Section), Value);
|
||||
return S;
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
Symbol *SymbolTable<ELFT>::addSynthetic(StringRef N,
|
||||
const OutputSectionBase *Section,
|
||||
uintX_t Value, uint8_t StOther) {
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) = insert(N, STT_NOTYPE, getVisibility(StOther),
|
||||
/*CanOmitFromDynSym*/ false, nullptr);
|
||||
int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, STB_GLOBAL,
|
||||
/*IsAbsolute*/ false, /*Value*/ 0);
|
||||
if (Cmp > 0)
|
||||
replaceBody<DefinedSynthetic>(S, N, Value, Section);
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate(S->body(), nullptr);
|
||||
return S;
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *F, StringRef Name,
|
||||
void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *File, StringRef Name,
|
||||
const Elf_Sym &Sym,
|
||||
const typename ELFT::Verdef *Verdef) {
|
||||
// DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT
|
||||
|
|
@ -423,15 +426,21 @@ void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *F, StringRef Name,
|
|||
// unchanged.
|
||||
Symbol *S;
|
||||
bool WasInserted;
|
||||
std::tie(S, WasInserted) =
|
||||
insert(Name, Sym.getType(), STV_DEFAULT, /*CanOmitFromDynSym*/ true, F);
|
||||
std::tie(S, WasInserted) = insert(Name, Sym.getType(), STV_DEFAULT,
|
||||
/*CanOmitFromDynSym*/ true, File);
|
||||
// Make sure we preempt DSO symbols with default visibility.
|
||||
if (Sym.getVisibility() == STV_DEFAULT)
|
||||
S->ExportDynamic = true;
|
||||
if (WasInserted || isa<Undefined<ELFT>>(S->body())) {
|
||||
replaceBody<SharedSymbol<ELFT>>(S, F, Name, Sym, Verdef);
|
||||
|
||||
SymbolBody *Body = S->body();
|
||||
// An undefined symbol with non default visibility must be satisfied
|
||||
// in the same DSO.
|
||||
if (WasInserted ||
|
||||
(isa<Undefined>(Body) && Body->getVisibility() == STV_DEFAULT)) {
|
||||
replaceBody<SharedSymbol>(S, File, Name, Sym.st_other, Sym.getType(), &Sym,
|
||||
Verdef);
|
||||
if (!S->isWeak())
|
||||
F->IsUsed = true;
|
||||
File->IsUsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -446,8 +455,8 @@ Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, uint8_t Binding,
|
|||
int Cmp = compareDefinedNonCommon<ELFT>(S, WasInserted, Binding,
|
||||
/*IsAbs*/ false, /*Value*/ 0);
|
||||
if (Cmp > 0)
|
||||
replaceBody<DefinedRegular<ELFT>>(S, Name, /*IsLocal=*/false, StOther, Type,
|
||||
0, 0, nullptr, F);
|
||||
replaceBody<DefinedRegular>(S, Name, /*IsLocal=*/false, StOther, Type, 0, 0,
|
||||
nullptr, F);
|
||||
else if (Cmp == 0)
|
||||
reportDuplicate(S->body(), F);
|
||||
return S;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
namespace lld {
|
||||
namespace elf {
|
||||
class Lazy;
|
||||
class OutputSectionBase;
|
||||
struct Symbol;
|
||||
|
||||
// SymbolTable is a bucket of all known symbols, including defined,
|
||||
|
|
@ -36,7 +35,6 @@ struct Symbol;
|
|||
// is one add* function per symbol type.
|
||||
template <class ELFT> class SymbolTable {
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
public:
|
||||
void addFile(InputFile *File);
|
||||
|
|
@ -47,11 +45,11 @@ public:
|
|||
ArrayRef<BinaryFile *> getBinaryFiles() const { return BinaryFiles; }
|
||||
ArrayRef<SharedFile<ELFT> *> getSharedFiles() const { return SharedFiles; }
|
||||
|
||||
DefinedRegular<ELFT> *addAbsolute(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN,
|
||||
uint8_t Binding = llvm::ELF::STB_GLOBAL);
|
||||
DefinedRegular<ELFT> *addIgnored(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
|
||||
DefinedRegular *addAbsolute(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN,
|
||||
uint8_t Binding = llvm::ELF::STB_GLOBAL);
|
||||
DefinedRegular *addIgnored(StringRef Name,
|
||||
uint8_t Visibility = llvm::ELF::STV_HIDDEN);
|
||||
|
||||
Symbol *addUndefined(StringRef Name);
|
||||
Symbol *addUndefined(StringRef Name, bool IsLocal, uint8_t Binding,
|
||||
|
|
@ -59,11 +57,8 @@ public:
|
|||
InputFile *File);
|
||||
|
||||
Symbol *addRegular(StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
uintX_t Value, uintX_t Size, uint8_t Binding,
|
||||
InputSectionBase<ELFT> *Section, InputFile *File);
|
||||
|
||||
Symbol *addSynthetic(StringRef N, const OutputSectionBase *Section,
|
||||
uintX_t Value, uint8_t StOther);
|
||||
uint64_t Value, uint64_t Size, uint8_t Binding,
|
||||
SectionBase *Section, InputFile *File);
|
||||
|
||||
void addShared(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
|
||||
const typename ELFT::Verdef *Verdef);
|
||||
|
|
@ -73,10 +68,15 @@ public:
|
|||
Symbol *addBitcode(StringRef Name, uint8_t Binding, uint8_t StOther,
|
||||
uint8_t Type, bool CanOmitFromDynSym, BitcodeFile *File);
|
||||
|
||||
Symbol *addCommon(StringRef N, uint64_t Size, uint64_t Alignment,
|
||||
Symbol *addCommon(StringRef N, uint64_t Size, uint32_t Alignment,
|
||||
uint8_t Binding, uint8_t StOther, uint8_t Type,
|
||||
InputFile *File);
|
||||
|
||||
std::pair<Symbol *, bool> insert(StringRef Name);
|
||||
std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Type,
|
||||
uint8_t Visibility, bool CanOmitFromDynSym,
|
||||
InputFile *File);
|
||||
|
||||
void scanUndefinedFlags();
|
||||
void scanShlibUndefined();
|
||||
void scanVersionScript();
|
||||
|
|
@ -87,14 +87,7 @@ public:
|
|||
void trace(StringRef Name);
|
||||
void wrap(StringRef Name);
|
||||
|
||||
std::vector<InputSectionBase<ELFT> *> Sections;
|
||||
|
||||
private:
|
||||
std::pair<Symbol *, bool> insert(StringRef Name);
|
||||
std::pair<Symbol *, bool> insert(StringRef Name, uint8_t Type,
|
||||
uint8_t Visibility, bool CanOmitFromDynSym,
|
||||
InputFile *File);
|
||||
|
||||
std::vector<SymbolBody *> findByVersion(SymbolVersion Ver);
|
||||
std::vector<SymbolBody *> findAllByVersion(SymbolVersion Ver);
|
||||
|
||||
|
|
|
|||
258
ELF/Symbols.cpp
258
ELF/Symbols.cpp
|
|
@ -28,62 +28,89 @@ using namespace llvm::ELF;
|
|||
using namespace lld;
|
||||
using namespace lld::elf;
|
||||
|
||||
template <class ELFT>
|
||||
static typename ELFT::uint getSymVA(const SymbolBody &Body,
|
||||
typename ELFT::uint &Addend) {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
DefinedRegular *ElfSym::Bss;
|
||||
DefinedRegular *ElfSym::Etext1;
|
||||
DefinedRegular *ElfSym::Etext2;
|
||||
DefinedRegular *ElfSym::Edata1;
|
||||
DefinedRegular *ElfSym::Edata2;
|
||||
DefinedRegular *ElfSym::End1;
|
||||
DefinedRegular *ElfSym::End2;
|
||||
DefinedRegular *ElfSym::MipsGp;
|
||||
DefinedRegular *ElfSym::MipsGpDisp;
|
||||
DefinedRegular *ElfSym::MipsLocalGp;
|
||||
|
||||
static uint64_t getSymVA(const SymbolBody &Body, int64_t &Addend) {
|
||||
switch (Body.kind()) {
|
||||
case SymbolBody::DefinedSyntheticKind: {
|
||||
auto &D = cast<DefinedSynthetic>(Body);
|
||||
const OutputSectionBase *Sec = D.Section;
|
||||
if (!Sec)
|
||||
return D.Value;
|
||||
if (D.Value == uintX_t(-1))
|
||||
return Sec->Addr + Sec->Size;
|
||||
return Sec->Addr + D.Value;
|
||||
}
|
||||
case SymbolBody::DefinedRegularKind: {
|
||||
auto &D = cast<DefinedRegular<ELFT>>(Body);
|
||||
InputSectionBase<ELFT> *IS = D.Section;
|
||||
auto &D = cast<DefinedRegular>(Body);
|
||||
SectionBase *IS = D.Section;
|
||||
if (auto *ISB = dyn_cast_or_null<InputSectionBase>(IS))
|
||||
IS = ISB->Repl;
|
||||
|
||||
// According to the ELF spec reference to a local symbol from outside
|
||||
// the group are not allowed. Unfortunately .eh_frame breaks that rule
|
||||
// and must be treated specially. For now we just replace the symbol with
|
||||
// 0.
|
||||
if (IS == &InputSection<ELFT>::Discarded)
|
||||
if (IS == &InputSection::Discarded)
|
||||
return 0;
|
||||
|
||||
// This is an absolute symbol.
|
||||
if (!IS)
|
||||
return D.Value;
|
||||
|
||||
uintX_t Offset = D.Value;
|
||||
uint64_t Offset = D.Value;
|
||||
|
||||
// An object in an SHF_MERGE section might be referenced via a
|
||||
// section symbol (as a hack for reducing the number of local
|
||||
// symbols).
|
||||
// Depending on the addend, the reference via a section symbol
|
||||
// refers to a different object in the merge section.
|
||||
// Since the objects in the merge section are not necessarily
|
||||
// contiguous in the output, the addend can thus affect the final
|
||||
// VA in a non-linear way.
|
||||
// To make this work, we incorporate the addend into the section
|
||||
// offset (and zero out the addend for later processing) so that
|
||||
// we find the right object in the section.
|
||||
if (D.isSection()) {
|
||||
Offset += Addend;
|
||||
Addend = 0;
|
||||
}
|
||||
uintX_t VA = (IS->OutSec ? IS->OutSec->Addr : 0) + IS->getOffset(Offset);
|
||||
|
||||
const OutputSection *OutSec = IS->getOutputSection();
|
||||
|
||||
// In the typical case, this is actually very simple and boils
|
||||
// down to adding together 3 numbers:
|
||||
// 1. The address of the output section.
|
||||
// 2. The offset of the input section within the output section.
|
||||
// 3. The offset within the input section (this addition happens
|
||||
// inside InputSection::getOffset).
|
||||
//
|
||||
// If you understand the data structures involved with this next
|
||||
// line (and how they get built), then you have a pretty good
|
||||
// understanding of the linker.
|
||||
uint64_t VA = (OutSec ? OutSec->Addr : 0) + IS->getOffset(Offset);
|
||||
|
||||
if (D.isTls() && !Config->Relocatable) {
|
||||
if (!Out<ELFT>::TlsPhdr)
|
||||
if (!Out::TlsPhdr)
|
||||
fatal(toString(D.File) +
|
||||
" has a STT_TLS symbol but doesn't have a PT_TLS section");
|
||||
return VA - Out<ELFT>::TlsPhdr->p_vaddr;
|
||||
return VA - Out::TlsPhdr->p_vaddr;
|
||||
}
|
||||
return VA;
|
||||
}
|
||||
case SymbolBody::DefinedCommonKind:
|
||||
if (!Config->DefineCommon)
|
||||
return 0;
|
||||
return In<ELFT>::Common->OutSec->Addr + In<ELFT>::Common->OutSecOff +
|
||||
return InX::Common->OutSec->Addr + InX::Common->OutSecOff +
|
||||
cast<DefinedCommon>(Body).Offset;
|
||||
case SymbolBody::SharedKind: {
|
||||
auto &SS = cast<SharedSymbol<ELFT>>(Body);
|
||||
if (!SS.NeedsCopyOrPltAddr)
|
||||
return 0;
|
||||
if (SS.isFunc())
|
||||
return Body.getPltVA<ELFT>();
|
||||
return SS.getBssSectionForCopy()->Addr + SS.CopyOffset;
|
||||
auto &SS = cast<SharedSymbol>(Body);
|
||||
if (SS.NeedsCopy)
|
||||
return SS.CopyRelSec->OutSec->Addr + SS.CopyRelSec->OutSecOff +
|
||||
SS.CopyRelSecOff;
|
||||
if (SS.NeedsPltAddr)
|
||||
return Body.getPltVA();
|
||||
return 0;
|
||||
}
|
||||
case SymbolBody::UndefinedKind:
|
||||
return 0;
|
||||
|
|
@ -97,10 +124,9 @@ static typename ELFT::uint getSymVA(const SymbolBody &Body,
|
|||
|
||||
SymbolBody::SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type)
|
||||
: SymbolKind(K), NeedsCopyOrPltAddr(false), IsLocal(IsLocal),
|
||||
: SymbolKind(K), NeedsCopy(false), NeedsPltAddr(false), IsLocal(IsLocal),
|
||||
IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
|
||||
IsInIgot(false), CopyIsInBssRelRo(false), Type(Type), StOther(StOther),
|
||||
Name(Name) {}
|
||||
IsInIgot(false), Type(Type), StOther(StOther), Name(Name) {}
|
||||
|
||||
// Returns true if a symbol can be replaced at load-time by a symbol
|
||||
// with the same name defined in other ELF executable or DSO.
|
||||
|
|
@ -112,7 +138,7 @@ bool SymbolBody::isPreemptible() const {
|
|||
// symbols with copy relocations (which resolve to .bss) or preempt plt
|
||||
// entries (which resolve to that plt entry).
|
||||
if (isShared())
|
||||
return !NeedsCopyOrPltAddr;
|
||||
return !NeedsCopy && !NeedsPltAddr;
|
||||
|
||||
// That's all that can be preempted in a non-DSO.
|
||||
if (!Config->Shared)
|
||||
|
|
@ -132,65 +158,68 @@ bool SymbolBody::isPreemptible() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
template <class ELFT> bool SymbolBody::hasThunk() const {
|
||||
if (auto *DR = dyn_cast<DefinedRegular<ELFT>>(this))
|
||||
return DR->ThunkData != nullptr;
|
||||
if (auto *S = dyn_cast<SharedSymbol<ELFT>>(this))
|
||||
return S->ThunkData != nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
typename ELFT::uint SymbolBody::getVA(typename ELFT::uint Addend) const {
|
||||
typename ELFT::uint OutVA = getSymVA<ELFT>(*this, Addend);
|
||||
uint64_t SymbolBody::getVA(int64_t Addend) const {
|
||||
uint64_t OutVA = getSymVA(*this, Addend);
|
||||
return OutVA + Addend;
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getGotVA() const {
|
||||
return In<ELFT>::Got->getVA() + getGotOffset<ELFT>();
|
||||
return In<ELFT>::Got->getVA() + getGotOffset();
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getGotOffset() const {
|
||||
uint64_t SymbolBody::getGotOffset() const {
|
||||
return GotIndex * Target->GotEntrySize;
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getGotPltVA() const {
|
||||
uint64_t SymbolBody::getGotPltVA() const {
|
||||
if (this->IsInIgot)
|
||||
return In<ELFT>::IgotPlt->getVA() + getGotPltOffset<ELFT>();
|
||||
return In<ELFT>::GotPlt->getVA() + getGotPltOffset<ELFT>();
|
||||
return InX::IgotPlt->getVA() + getGotPltOffset();
|
||||
return InX::GotPlt->getVA() + getGotPltOffset();
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getGotPltOffset() const {
|
||||
uint64_t SymbolBody::getGotPltOffset() const {
|
||||
return GotPltIndex * Target->GotPltEntrySize;
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getPltVA() const {
|
||||
uint64_t SymbolBody::getPltVA() const {
|
||||
if (this->IsInIplt)
|
||||
return In<ELFT>::Iplt->getVA() + PltIndex * Target->PltEntrySize;
|
||||
return In<ELFT>::Plt->getVA() + Target->PltHeaderSize +
|
||||
return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize;
|
||||
return InX::Plt->getVA() + Target->PltHeaderSize +
|
||||
PltIndex * Target->PltEntrySize;
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getThunkVA() const {
|
||||
if (const auto *DR = dyn_cast<DefinedRegular<ELFT>>(this))
|
||||
return DR->ThunkData->getVA();
|
||||
if (const auto *S = dyn_cast<SharedSymbol<ELFT>>(this))
|
||||
return S->ThunkData->getVA();
|
||||
if (const auto *S = dyn_cast<Undefined<ELFT>>(this))
|
||||
return S->ThunkData->getVA();
|
||||
fatal("getThunkVA() not supported for Symbol class\n");
|
||||
}
|
||||
|
||||
template <class ELFT> typename ELFT::uint SymbolBody::getSize() const {
|
||||
if (const auto *C = dyn_cast<DefinedCommon>(this))
|
||||
return C->Size;
|
||||
if (const auto *DR = dyn_cast<DefinedRegular<ELFT>>(this))
|
||||
if (const auto *DR = dyn_cast<DefinedRegular>(this))
|
||||
return DR->Size;
|
||||
if (const auto *S = dyn_cast<SharedSymbol<ELFT>>(this))
|
||||
return S->Sym.st_size;
|
||||
if (const auto *S = dyn_cast<SharedSymbol>(this))
|
||||
return S->getSize<ELFT>();
|
||||
return 0;
|
||||
}
|
||||
|
||||
OutputSection *SymbolBody::getOutputSection() const {
|
||||
if (auto *S = dyn_cast<DefinedRegular>(this)) {
|
||||
if (S->Section)
|
||||
return S->Section->getOutputSection();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto *S = dyn_cast<SharedSymbol>(this)) {
|
||||
if (S->NeedsCopy)
|
||||
return S->CopyRelSec->OutSec;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (isa<DefinedCommon>(this)) {
|
||||
if (Config->DefineCommon)
|
||||
return InX::Common->OutSec;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If a symbol name contains '@', the characters after that is
|
||||
// a symbol version name. This function parses that.
|
||||
void SymbolBody::parseSymbolVersion() {
|
||||
|
|
@ -234,27 +263,25 @@ Defined::Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
|
|||
uint8_t Type)
|
||||
: SymbolBody(K, Name, IsLocal, StOther, Type) {}
|
||||
|
||||
template <class ELFT> bool DefinedRegular<ELFT>::isMipsPIC() const {
|
||||
template <class ELFT> bool DefinedRegular::isMipsPIC() const {
|
||||
if (!Section || !isFunc())
|
||||
return false;
|
||||
return (this->StOther & STO_MIPS_MIPS16) == STO_MIPS_PIC ||
|
||||
(Section->getFile()->getObj().getHeader()->e_flags & EF_MIPS_PIC);
|
||||
(cast<InputSectionBase>(Section)
|
||||
->template getFile<ELFT>()
|
||||
->getObj()
|
||||
.getHeader()
|
||||
->e_flags &
|
||||
EF_MIPS_PIC);
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
Undefined<ELFT>::Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type, InputFile *File)
|
||||
Undefined::Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther,
|
||||
uint8_t Type, InputFile *File)
|
||||
: SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type) {
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
template <typename ELFT>
|
||||
OutputSection<ELFT> *SharedSymbol<ELFT>::getBssSectionForCopy() const {
|
||||
assert(needsCopy());
|
||||
return CopyIsInBssRelRo ? Out<ELFT>::BssRelRo : Out<ELFT>::Bss;
|
||||
}
|
||||
|
||||
DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint64_t Alignment,
|
||||
DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint32_t Alignment,
|
||||
uint8_t StOther, uint8_t Type, InputFile *File)
|
||||
: Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther,
|
||||
Type),
|
||||
|
|
@ -262,6 +289,17 @@ DefinedCommon::DefinedCommon(StringRef Name, uint64_t Size, uint64_t Alignment,
|
|||
this->File = File;
|
||||
}
|
||||
|
||||
// If a shared symbol is referred via a copy relocation, its alignment
|
||||
// becomes part of the ABI. This function returns a symbol alignment.
|
||||
// Because symbols don't have alignment attributes, we need to infer that.
|
||||
template <class ELFT> uint32_t SharedSymbol::getAlignment() const {
|
||||
auto *File = cast<SharedFile<ELFT>>(this->File);
|
||||
uint32_t SecAlign = File->getSection(getSym<ELFT>())->sh_addralign;
|
||||
uint64_t SymValue = getSym<ELFT>().st_value;
|
||||
uint32_t SymAlign = uint32_t(1) << countTrailingZeros(SymValue);
|
||||
return std::min(SecAlign, SymAlign);
|
||||
}
|
||||
|
||||
InputFile *Lazy::fetch() {
|
||||
if (auto *S = dyn_cast<LazyArchive>(this))
|
||||
return S->fetch();
|
||||
|
|
@ -319,15 +357,15 @@ bool Symbol::includeInDynsym() const {
|
|||
// Print out a log message for --trace-symbol.
|
||||
void elf::printTraceSymbol(Symbol *Sym) {
|
||||
SymbolBody *B = Sym->body();
|
||||
outs() << toString(B->File);
|
||||
|
||||
std::string S;
|
||||
if (B->isUndefined())
|
||||
outs() << ": reference to ";
|
||||
S = ": reference to ";
|
||||
else if (B->isCommon())
|
||||
outs() << ": common definition of ";
|
||||
S = ": common definition of ";
|
||||
else
|
||||
outs() << ": definition of ";
|
||||
outs() << B->getName() << "\n";
|
||||
S = ": definition of ";
|
||||
|
||||
message(toString(B->File) + S + B->getName());
|
||||
}
|
||||
|
||||
// Returns a symbol for an error message.
|
||||
|
|
@ -338,62 +376,22 @@ std::string lld::toString(const SymbolBody &B) {
|
|||
return B.getName();
|
||||
}
|
||||
|
||||
template bool SymbolBody::hasThunk<ELF32LE>() const;
|
||||
template bool SymbolBody::hasThunk<ELF32BE>() const;
|
||||
template bool SymbolBody::hasThunk<ELF64LE>() const;
|
||||
template bool SymbolBody::hasThunk<ELF64BE>() const;
|
||||
|
||||
template uint32_t SymbolBody::template getVA<ELF32LE>(uint32_t) const;
|
||||
template uint32_t SymbolBody::template getVA<ELF32BE>(uint32_t) const;
|
||||
template uint64_t SymbolBody::template getVA<ELF64LE>(uint64_t) const;
|
||||
template uint64_t SymbolBody::template getVA<ELF64BE>(uint64_t) const;
|
||||
|
||||
template uint32_t SymbolBody::template getGotVA<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getGotVA<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getGotVA<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getGotVA<ELF64BE>() const;
|
||||
|
||||
template uint32_t SymbolBody::template getGotOffset<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getGotOffset<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getGotOffset<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getGotOffset<ELF64BE>() const;
|
||||
|
||||
template uint32_t SymbolBody::template getGotPltVA<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getGotPltVA<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getGotPltVA<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getGotPltVA<ELF64BE>() const;
|
||||
|
||||
template uint32_t SymbolBody::template getThunkVA<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getThunkVA<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getThunkVA<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getThunkVA<ELF64BE>() const;
|
||||
|
||||
template uint32_t SymbolBody::template getGotPltOffset<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getGotPltOffset<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getGotPltOffset<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getGotPltOffset<ELF64BE>() const;
|
||||
|
||||
template uint32_t SymbolBody::template getPltVA<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getPltVA<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getPltVA<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getPltVA<ELF64BE>() const;
|
||||
|
||||
template uint32_t SymbolBody::template getSize<ELF32LE>() const;
|
||||
template uint32_t SymbolBody::template getSize<ELF32BE>() const;
|
||||
template uint64_t SymbolBody::template getSize<ELF64LE>() const;
|
||||
template uint64_t SymbolBody::template getSize<ELF64BE>() const;
|
||||
|
||||
template class elf::Undefined<ELF32LE>;
|
||||
template class elf::Undefined<ELF32BE>;
|
||||
template class elf::Undefined<ELF64LE>;
|
||||
template class elf::Undefined<ELF64BE>;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF32LE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF32BE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF64LE>() const;
|
||||
template bool DefinedRegular::template isMipsPIC<ELF64BE>() const;
|
||||
|
||||
template class elf::SharedSymbol<ELF32LE>;
|
||||
template class elf::SharedSymbol<ELF32BE>;
|
||||
template class elf::SharedSymbol<ELF64LE>;
|
||||
template class elf::SharedSymbol<ELF64BE>;
|
||||
|
||||
template class elf::DefinedRegular<ELF32LE>;
|
||||
template class elf::DefinedRegular<ELF32BE>;
|
||||
template class elf::DefinedRegular<ELF64LE>;
|
||||
template class elf::DefinedRegular<ELF64BE>;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF32LE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF32BE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF64LE>() const;
|
||||
template uint32_t SharedSymbol::template getAlignment<ELF64BE>() const;
|
||||
|
|
|
|||
204
ELF/Symbols.h
204
ELF/Symbols.h
|
|
@ -30,8 +30,7 @@ class BitcodeFile;
|
|||
class InputFile;
|
||||
class LazyObjectFile;
|
||||
template <class ELFT> class ObjectFile;
|
||||
template <class ELFT> class OutputSection;
|
||||
class OutputSectionBase;
|
||||
class OutputSection;
|
||||
template <class ELFT> class SharedFile;
|
||||
|
||||
struct Symbol;
|
||||
|
|
@ -44,8 +43,7 @@ public:
|
|||
DefinedRegularKind = DefinedFirst,
|
||||
SharedKind,
|
||||
DefinedCommonKind,
|
||||
DefinedSyntheticKind,
|
||||
DefinedLast = DefinedSyntheticKind,
|
||||
DefinedLast = DefinedCommonKind,
|
||||
UndefinedKind,
|
||||
LazyArchiveKind,
|
||||
LazyObjectKind,
|
||||
|
|
@ -76,18 +74,16 @@ public:
|
|||
|
||||
bool isInGot() const { return GotIndex != -1U; }
|
||||
bool isInPlt() const { return PltIndex != -1U; }
|
||||
template <class ELFT> bool hasThunk() const;
|
||||
|
||||
template <class ELFT>
|
||||
typename ELFT::uint getVA(typename ELFT::uint Addend = 0) const;
|
||||
uint64_t getVA(int64_t Addend = 0) const;
|
||||
|
||||
template <class ELFT> typename ELFT::uint getGotOffset() const;
|
||||
uint64_t getGotOffset() const;
|
||||
template <class ELFT> typename ELFT::uint getGotVA() const;
|
||||
template <class ELFT> typename ELFT::uint getGotPltOffset() const;
|
||||
template <class ELFT> typename ELFT::uint getGotPltVA() const;
|
||||
template <class ELFT> typename ELFT::uint getPltVA() const;
|
||||
template <class ELFT> typename ELFT::uint getThunkVA() const;
|
||||
uint64_t getGotPltOffset() const;
|
||||
uint64_t getGotPltVA() const;
|
||||
uint64_t getPltVA() const;
|
||||
template <class ELFT> typename ELFT::uint getSize() const;
|
||||
OutputSection *getOutputSection() const;
|
||||
|
||||
// The file from which this symbol was created.
|
||||
InputFile *File = nullptr;
|
||||
|
|
@ -105,9 +101,13 @@ protected:
|
|||
const unsigned SymbolKind : 8;
|
||||
|
||||
public:
|
||||
// True if the linker has to generate a copy relocation for this shared
|
||||
// symbol or if the symbol should point to its plt entry.
|
||||
unsigned NeedsCopyOrPltAddr : 1;
|
||||
// True if the linker has to generate a copy relocation.
|
||||
// For SharedSymbol only.
|
||||
unsigned NeedsCopy : 1;
|
||||
|
||||
// True the symbol should point to its PLT entry.
|
||||
// For SharedSymbol only.
|
||||
unsigned NeedsPltAddr : 1;
|
||||
|
||||
// True if this is a local symbol.
|
||||
unsigned IsLocal : 1;
|
||||
|
|
@ -124,11 +124,6 @@ public:
|
|||
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
|
||||
unsigned IsInIgot : 1;
|
||||
|
||||
// True if this is a shared symbol in a read-only segment which requires a
|
||||
// copy relocation. This causes space for the symbol to be allocated in the
|
||||
// .bss.rel.ro section.
|
||||
unsigned CopyIsInBssRelRo : 1;
|
||||
|
||||
// The following fields have the same meaning as the ELF symbol attributes.
|
||||
uint8_t Type; // symbol type
|
||||
uint8_t StOther; // st_other field value
|
||||
|
|
@ -160,7 +155,7 @@ public:
|
|||
|
||||
class DefinedCommon : public Defined {
|
||||
public:
|
||||
DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, uint8_t StOther,
|
||||
DefinedCommon(StringRef N, uint64_t Size, uint32_t Alignment, uint8_t StOther,
|
||||
uint8_t Type, InputFile *File);
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
|
|
@ -172,77 +167,35 @@ public:
|
|||
uint64_t Offset;
|
||||
|
||||
// The maximum alignment we have seen for this symbol.
|
||||
uint64_t Alignment;
|
||||
uint32_t Alignment;
|
||||
|
||||
uint64_t Size;
|
||||
};
|
||||
|
||||
// Regular defined symbols read from object file symbol tables.
|
||||
template <class ELFT> class DefinedRegular : public Defined {
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class DefinedRegular : public Defined {
|
||||
public:
|
||||
DefinedRegular(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
|
||||
uintX_t Value, uintX_t Size, InputSectionBase<ELFT> *Section,
|
||||
uint64_t Value, uint64_t Size, SectionBase *Section,
|
||||
InputFile *File)
|
||||
: Defined(SymbolBody::DefinedRegularKind, Name, IsLocal, StOther, Type),
|
||||
Value(Value), Size(Size),
|
||||
Section(Section ? Section->Repl : NullInputSection) {
|
||||
Value(Value), Size(Size), Section(Section) {
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
// Return true if the symbol is a PIC function.
|
||||
bool isMipsPIC() const;
|
||||
template <class ELFT> bool isMipsPIC() const;
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::DefinedRegularKind;
|
||||
}
|
||||
|
||||
uintX_t Value;
|
||||
uintX_t Size;
|
||||
|
||||
// The input section this symbol belongs to. Notice that this is
|
||||
// a reference to a pointer. We are using two levels of indirections
|
||||
// because of ICF. If ICF decides two sections need to be merged, it
|
||||
// manipulates this Section pointers so that they point to the same
|
||||
// section. This is a bit tricky, so be careful to not be confused.
|
||||
// If this is null, the symbol is an absolute symbol.
|
||||
InputSectionBase<ELFT> *&Section;
|
||||
|
||||
// If non-null the symbol has a Thunk that may be used as an alternative
|
||||
// destination for callers of this Symbol.
|
||||
Thunk<ELFT> *ThunkData = nullptr;
|
||||
|
||||
private:
|
||||
static InputSectionBase<ELFT> *NullInputSection;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
InputSectionBase<ELFT> *DefinedRegular<ELFT>::NullInputSection;
|
||||
|
||||
// DefinedSynthetic is a class to represent linker-generated ELF symbols.
|
||||
// The difference from the regular symbol is that DefinedSynthetic symbols
|
||||
// don't belong to any input files or sections. Thus, its constructor
|
||||
// takes an output section to calculate output VA, etc.
|
||||
// If Section is null, this symbol is relative to the image base.
|
||||
class DefinedSynthetic : public Defined {
|
||||
public:
|
||||
DefinedSynthetic(StringRef Name, uint64_t Value,
|
||||
const OutputSectionBase *Section)
|
||||
: Defined(SymbolBody::DefinedSyntheticKind, Name, /*IsLocal=*/false,
|
||||
llvm::ELF::STV_HIDDEN, 0 /* Type */),
|
||||
Value(Value), Section(Section) {}
|
||||
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::DefinedSyntheticKind;
|
||||
}
|
||||
|
||||
uint64_t Value;
|
||||
const OutputSectionBase *Section;
|
||||
uint64_t Size;
|
||||
SectionBase *Section;
|
||||
};
|
||||
|
||||
template <class ELFT> class Undefined : public SymbolBody {
|
||||
class Undefined : public SymbolBody {
|
||||
public:
|
||||
Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
|
||||
InputFile *F);
|
||||
|
|
@ -250,53 +203,51 @@ public:
|
|||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == UndefinedKind;
|
||||
}
|
||||
|
||||
// If non-null the symbol has a Thunk that may be used as an alternative
|
||||
// destination for callers of this Symbol. When linking a DSO undefined
|
||||
// symbols are implicitly imported, the symbol lookup will be performed by
|
||||
// the dynamic loader. A call to an undefined symbol will be given a PLT
|
||||
// entry and on ARM this may need a Thunk if the caller is in Thumb state.
|
||||
Thunk<ELFT> *ThunkData = nullptr;
|
||||
InputFile *file() { return this->File; }
|
||||
};
|
||||
|
||||
template <class ELFT> class SharedSymbol : public Defined {
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::Verdef Elf_Verdef;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class SharedSymbol : public Defined {
|
||||
public:
|
||||
static bool classof(const SymbolBody *S) {
|
||||
return S->kind() == SymbolBody::SharedKind;
|
||||
}
|
||||
|
||||
SharedSymbol(SharedFile<ELFT> *F, StringRef Name, const Elf_Sym &Sym,
|
||||
const Elf_Verdef *Verdef)
|
||||
: Defined(SymbolBody::SharedKind, Name, /*IsLocal=*/false, Sym.st_other,
|
||||
Sym.getType()),
|
||||
Sym(Sym), Verdef(Verdef) {
|
||||
SharedSymbol(InputFile *File, StringRef Name, uint8_t StOther, uint8_t Type,
|
||||
const void *ElfSym, const void *Verdef)
|
||||
: Defined(SymbolBody::SharedKind, Name, /*IsLocal=*/false, StOther, Type),
|
||||
Verdef(Verdef), ElfSym(ElfSym) {
|
||||
// IFuncs defined in DSOs are treated as functions by the static linker.
|
||||
if (isGnuIFunc())
|
||||
Type = llvm::ELF::STT_FUNC;
|
||||
this->File = F;
|
||||
this->File = File;
|
||||
}
|
||||
|
||||
SharedFile<ELFT> *file() { return (SharedFile<ELFT> *)this->File; }
|
||||
template <class ELFT> uint64_t getShndx() const {
|
||||
return getSym<ELFT>().st_shndx;
|
||||
}
|
||||
|
||||
const Elf_Sym &Sym;
|
||||
template <class ELFT> uint64_t getValue() const {
|
||||
return getSym<ELFT>().st_value;
|
||||
}
|
||||
|
||||
template <class ELFT> uint64_t getSize() const {
|
||||
return getSym<ELFT>().st_size;
|
||||
}
|
||||
|
||||
template <class ELFT> uint32_t getAlignment() const;
|
||||
|
||||
// This field is a pointer to the symbol's version definition.
|
||||
const Elf_Verdef *Verdef;
|
||||
const void *Verdef;
|
||||
|
||||
// CopyOffset is significant only when needsCopy() is true.
|
||||
uintX_t CopyOffset = 0;
|
||||
// CopyRelSec and CopyRelSecOff are significant only when NeedsCopy is true.
|
||||
InputSection *CopyRelSec;
|
||||
uint64_t CopyRelSecOff;
|
||||
|
||||
// If non-null the symbol has a Thunk that may be used as an alternative
|
||||
// destination for callers of this Symbol.
|
||||
Thunk<ELFT> *ThunkData = nullptr;
|
||||
bool needsCopy() const { return this->NeedsCopyOrPltAddr && !this->isFunc(); }
|
||||
private:
|
||||
template <class ELFT> const typename ELFT::Sym &getSym() const {
|
||||
return *(const typename ELFT::Sym *)ElfSym;
|
||||
}
|
||||
|
||||
OutputSection<ELFT> *getBssSectionForCopy() const;
|
||||
const void *ElfSym;
|
||||
};
|
||||
|
||||
// This class represents a symbol defined in an archive file. It is
|
||||
|
|
@ -350,39 +301,28 @@ public:
|
|||
|
||||
// Some linker-generated symbols need to be created as
|
||||
// DefinedRegular symbols.
|
||||
template <class ELFT> struct ElfSym {
|
||||
// The content for __ehdr_start symbol.
|
||||
static DefinedRegular<ELFT> *EhdrStart;
|
||||
struct ElfSym {
|
||||
// __bss_start
|
||||
static DefinedRegular *Bss;
|
||||
|
||||
// The content for _etext and etext symbols.
|
||||
static DefinedRegular<ELFT> *Etext;
|
||||
static DefinedRegular<ELFT> *Etext2;
|
||||
// etext and _etext
|
||||
static DefinedRegular *Etext1;
|
||||
static DefinedRegular *Etext2;
|
||||
|
||||
// The content for _edata and edata symbols.
|
||||
static DefinedRegular<ELFT> *Edata;
|
||||
static DefinedRegular<ELFT> *Edata2;
|
||||
// edata and _edata
|
||||
static DefinedRegular *Edata1;
|
||||
static DefinedRegular *Edata2;
|
||||
|
||||
// The content for _end and end symbols.
|
||||
static DefinedRegular<ELFT> *End;
|
||||
static DefinedRegular<ELFT> *End2;
|
||||
// end and _end
|
||||
static DefinedRegular *End1;
|
||||
static DefinedRegular *End2;
|
||||
|
||||
// The content for _gp_disp/__gnu_local_gp symbols for MIPS target.
|
||||
static DefinedRegular<ELFT> *MipsGpDisp;
|
||||
static DefinedRegular<ELFT> *MipsLocalGp;
|
||||
static DefinedRegular<ELFT> *MipsGp;
|
||||
// _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS.
|
||||
static DefinedRegular *MipsGp;
|
||||
static DefinedRegular *MipsGpDisp;
|
||||
static DefinedRegular *MipsLocalGp;
|
||||
};
|
||||
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::EhdrStart;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Etext;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Etext2;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::Edata2;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::End2;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::MipsGpDisp;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::MipsLocalGp;
|
||||
template <class ELFT> DefinedRegular<ELFT> *ElfSym<ELFT>::MipsGp;
|
||||
|
||||
// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
|
||||
// always one Symbol for each symbol name. The resolver updates the SymbolBody
|
||||
// stored in the Body field of this object as it resolves symbols. Symbol also
|
||||
|
|
@ -426,13 +366,9 @@ struct Symbol {
|
|||
|
||||
// This field is used to store the Symbol's SymbolBody. This instantiation of
|
||||
// AlignedCharArrayUnion gives us a struct with a char array field that is
|
||||
// large and aligned enough to store any derived class of SymbolBody. We
|
||||
// assume that the size and alignment of ELF64LE symbols is sufficient for any
|
||||
// ELFT, and we verify this with the static_asserts in replaceBody.
|
||||
llvm::AlignedCharArrayUnion<
|
||||
DefinedCommon, DefinedRegular<llvm::object::ELF64LE>, DefinedSynthetic,
|
||||
Undefined<llvm::object::ELF64LE>, SharedSymbol<llvm::object::ELF64LE>,
|
||||
LazyArchive, LazyObject>
|
||||
// large and aligned enough to store any derived class of SymbolBody.
|
||||
llvm::AlignedCharArrayUnion<DefinedCommon, DefinedRegular, Undefined,
|
||||
SharedSymbol, LazyArchive, LazyObject>
|
||||
Body;
|
||||
|
||||
SymbolBody *body() { return reinterpret_cast<SymbolBody *>(Body.buffer); }
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -6,10 +6,22 @@
|
|||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Synthetic sections represent chunks of linker-created data. If you
|
||||
// need to create a chunk of data that to be included in some section
|
||||
// in the result, you probably want to create that as a synthetic section.
|
||||
//
|
||||
// Synthetic sections are designed as input sections as opposed to
|
||||
// output sections because we want to allow them to be manipulated
|
||||
// using linker scripts just like other input sections from regular
|
||||
// files.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLD_ELF_SYNTHETIC_SECTION_H
|
||||
#define LLD_ELF_SYNTHETIC_SECTION_H
|
||||
|
||||
#include "EhFrame.h"
|
||||
#include "GdbIndex.h"
|
||||
#include "InputSection.h"
|
||||
#include "llvm/ADT/MapVector.h"
|
||||
|
|
@ -18,49 +30,95 @@
|
|||
namespace lld {
|
||||
namespace elf {
|
||||
|
||||
template <class ELFT> class SyntheticSection : public InputSection<ELFT> {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class SyntheticSection : public InputSection {
|
||||
public:
|
||||
SyntheticSection(uintX_t Flags, uint32_t Type, uintX_t Addralign,
|
||||
SyntheticSection(uint64_t Flags, uint32_t Type, uint32_t Alignment,
|
||||
StringRef Name)
|
||||
: InputSection<ELFT>(Flags, Type, Addralign, ArrayRef<uint8_t>(), Name,
|
||||
InputSectionData::Synthetic) {
|
||||
: InputSection(Flags, Type, Alignment, {}, Name,
|
||||
InputSectionBase::Synthetic) {
|
||||
this->Live = true;
|
||||
}
|
||||
|
||||
virtual ~SyntheticSection() = default;
|
||||
virtual void writeTo(uint8_t *Buf) = 0;
|
||||
virtual size_t getSize() const = 0;
|
||||
virtual void finalize() {}
|
||||
virtual void finalizeContents() {}
|
||||
// If the section has the SHF_ALLOC flag and the size may be changed if
|
||||
// thunks are added, update the section size.
|
||||
virtual void updateAllocSize() {}
|
||||
// If any additional finalization of contents are needed post thunk creation.
|
||||
virtual void postThunkContents() {}
|
||||
virtual bool empty() const { return false; }
|
||||
uint64_t getVA() const;
|
||||
|
||||
uintX_t getVA() const {
|
||||
return this->OutSec ? this->OutSec->Addr + this->OutSecOff : 0;
|
||||
}
|
||||
|
||||
static bool classof(const InputSectionData *D) {
|
||||
return D->kind() == InputSectionData::Synthetic;
|
||||
static bool classof(const InputSectionBase *D) {
|
||||
return D->kind() == InputSectionBase::Synthetic;
|
||||
}
|
||||
};
|
||||
|
||||
template <class ELFT> class GotSection final : public SyntheticSection<ELFT> {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
struct CieRecord {
|
||||
EhSectionPiece *Piece = nullptr;
|
||||
std::vector<EhSectionPiece *> FdePieces;
|
||||
};
|
||||
|
||||
// Section for .eh_frame.
|
||||
template <class ELFT> class EhFrameSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
|
||||
void updateAlignment(uint64_t Val) {
|
||||
if (Val > this->Alignment)
|
||||
this->Alignment = Val;
|
||||
}
|
||||
|
||||
public:
|
||||
EhFrameSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalizeContents() override;
|
||||
bool empty() const override { return Sections.empty(); }
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
void addSection(InputSectionBase *S);
|
||||
|
||||
size_t NumFdes = 0;
|
||||
|
||||
std::vector<EhInputSection *> Sections;
|
||||
|
||||
private:
|
||||
uint64_t Size = 0;
|
||||
template <class RelTy>
|
||||
void addSectionAux(EhInputSection *S, llvm::ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
CieRecord *addCie(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
|
||||
|
||||
template <class RelTy>
|
||||
bool isFdeLive(EhSectionPiece &Piece, ArrayRef<RelTy> Rels);
|
||||
|
||||
uint64_t getFdePc(uint8_t *Buf, size_t Off, uint8_t Enc);
|
||||
|
||||
std::vector<CieRecord *> Cies;
|
||||
|
||||
// CIE records are uniquified by their contents and personality functions.
|
||||
llvm::DenseMap<std::pair<ArrayRef<uint8_t>, SymbolBody *>, CieRecord> CieMap;
|
||||
};
|
||||
|
||||
template <class ELFT> class GotSection final : public SyntheticSection {
|
||||
public:
|
||||
GotSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
void finalize() override;
|
||||
void finalizeContents() override;
|
||||
bool empty() const override;
|
||||
|
||||
void addEntry(SymbolBody &Sym);
|
||||
bool addDynTlsEntry(SymbolBody &Sym);
|
||||
bool addTlsIndex();
|
||||
uintX_t getGlobalDynAddr(const SymbolBody &B) const;
|
||||
uintX_t getGlobalDynOffset(const SymbolBody &B) const;
|
||||
uint64_t getGlobalDynAddr(const SymbolBody &B) const;
|
||||
uint64_t getGlobalDynOffset(const SymbolBody &B) const;
|
||||
|
||||
uintX_t getTlsIndexVA() { return this->getVA() + TlsIndexOff; }
|
||||
uint64_t getTlsIndexVA() { return this->getVA() + TlsIndexOff; }
|
||||
uint32_t getTlsIndexOff() const { return TlsIndexOff; }
|
||||
|
||||
// Flag to force GOT to be in output if we have relocations
|
||||
|
|
@ -70,11 +128,11 @@ public:
|
|||
private:
|
||||
size_t NumEntries = 0;
|
||||
uint32_t TlsIndexOff = -1;
|
||||
uintX_t Size = 0;
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
// .note.gnu.build-id section.
|
||||
template <class ELFT> class BuildIdSection : public SyntheticSection<ELFT> {
|
||||
class BuildIdSection : public SyntheticSection {
|
||||
// First 16 bytes are a header.
|
||||
static const unsigned HeaderSize = 16;
|
||||
|
||||
|
|
@ -92,22 +150,36 @@ private:
|
|||
uint8_t *HashBuf;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class MipsGotSection final : public SyntheticSection<ELFT> {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
// BssSection is used to reserve space for copy relocations and common symbols.
|
||||
// We create three instances of this class for .bss, .bss.rel.ro and "COMMON",
|
||||
// that are used for writable symbols, read-only symbols and common symbols,
|
||||
// respectively.
|
||||
class BssSection final : public SyntheticSection {
|
||||
public:
|
||||
BssSection(StringRef Name);
|
||||
void writeTo(uint8_t *) override {}
|
||||
bool empty() const override { return getSize() == 0; }
|
||||
size_t reserveSpace(uint64_t Size, uint32_t Alignment);
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
private:
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
class MipsGotSection final : public SyntheticSection {
|
||||
public:
|
||||
MipsGotSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
void finalize() override;
|
||||
void updateAllocSize() override;
|
||||
void finalizeContents() override;
|
||||
bool empty() const override;
|
||||
void addEntry(SymbolBody &Sym, uintX_t Addend, RelExpr Expr);
|
||||
void addEntry(SymbolBody &Sym, int64_t Addend, RelExpr Expr);
|
||||
bool addDynTlsEntry(SymbolBody &Sym);
|
||||
bool addTlsIndex();
|
||||
uintX_t getPageEntryOffset(const SymbolBody &B, uintX_t Addend) const;
|
||||
uintX_t getBodyEntryOffset(const SymbolBody &B, uintX_t Addend) const;
|
||||
uintX_t getGlobalDynOffset(const SymbolBody &B) const;
|
||||
uint64_t getPageEntryOffset(const SymbolBody &B, int64_t Addend) const;
|
||||
uint64_t getBodyEntryOffset(const SymbolBody &B, int64_t Addend) const;
|
||||
uint64_t getGlobalDynOffset(const SymbolBody &B) const;
|
||||
|
||||
// Returns the symbol which corresponds to the first entry of the global part
|
||||
// of GOT on MIPS platform. It is required to fill up MIPS-specific dynamic
|
||||
|
|
@ -121,11 +193,11 @@ public:
|
|||
|
||||
// Returns offset of TLS part of the MIPS GOT table. This part goes
|
||||
// after 'local' and 'global' entries.
|
||||
uintX_t getTlsOffset() const;
|
||||
uint64_t getTlsOffset() const;
|
||||
|
||||
uint32_t getTlsIndexOff() const { return TlsIndexOff; }
|
||||
|
||||
uintX_t getGp() const;
|
||||
uint64_t getGp() const;
|
||||
|
||||
private:
|
||||
// MIPS GOT consists of three parts: local, global and tls. Each part
|
||||
|
|
@ -171,9 +243,9 @@ private:
|
|||
uint32_t PageEntriesNum = 0;
|
||||
// Map output sections referenced by MIPS GOT relocations
|
||||
// to the first index of "Page" entries allocated for this section.
|
||||
llvm::SmallMapVector<const OutputSectionBase *, size_t, 16> PageIndexMap;
|
||||
llvm::SmallMapVector<const OutputSection *, size_t, 16> PageIndexMap;
|
||||
|
||||
typedef std::pair<const SymbolBody *, uintX_t> GotEntry;
|
||||
typedef std::pair<const SymbolBody *, uint64_t> GotEntry;
|
||||
typedef std::vector<GotEntry> GotEntries;
|
||||
// Map from Symbol-Addend pair to the GOT index.
|
||||
llvm::DenseMap<GotEntry, size_t> EntryIndexMap;
|
||||
|
|
@ -189,13 +261,10 @@ private:
|
|||
std::vector<const SymbolBody *> TlsEntries;
|
||||
|
||||
uint32_t TlsIndexOff = -1;
|
||||
uintX_t Size = 0;
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class GotPltSection final : public SyntheticSection<ELFT> {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class GotPltSection final : public SyntheticSection {
|
||||
public:
|
||||
GotPltSection();
|
||||
void addEntry(SymbolBody &Sym);
|
||||
|
|
@ -207,14 +276,11 @@ private:
|
|||
std::vector<const SymbolBody *> Entries;
|
||||
};
|
||||
|
||||
// The IgotPltSection is a Got associated with the IpltSection for GNU Ifunc
|
||||
// The IgotPltSection is a Got associated with the PltSection for GNU Ifunc
|
||||
// Symbols that will be relocated by Target->IRelativeRel.
|
||||
// On most Targets the IgotPltSection will immediately follow the GotPltSection
|
||||
// on ARM the IgotPltSection will immediately follow the GotSection.
|
||||
template <class ELFT>
|
||||
class IgotPltSection final : public SyntheticSection<ELFT> {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class IgotPltSection final : public SyntheticSection {
|
||||
public:
|
||||
IgotPltSection();
|
||||
void addEntry(SymbolBody &Sym);
|
||||
|
|
@ -226,10 +292,8 @@ private:
|
|||
std::vector<const SymbolBody *> Entries;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class StringTableSection final : public SyntheticSection<ELFT> {
|
||||
class StringTableSection final : public SyntheticSection {
|
||||
public:
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
StringTableSection(StringRef Name, bool Dynamic);
|
||||
unsigned addString(StringRef S, bool HashIt = true);
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
|
@ -239,54 +303,41 @@ public:
|
|||
private:
|
||||
const bool Dynamic;
|
||||
|
||||
// ELF string tables start with a NUL byte, so 1.
|
||||
uintX_t Size = 1;
|
||||
uint64_t Size = 0;
|
||||
|
||||
llvm::DenseMap<StringRef, unsigned> StringMap;
|
||||
std::vector<StringRef> Strings;
|
||||
};
|
||||
|
||||
template <class ELFT> class DynamicReloc {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class DynamicReloc {
|
||||
public:
|
||||
DynamicReloc(uint32_t Type, const InputSectionBase<ELFT> *InputSec,
|
||||
uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
|
||||
uintX_t Addend)
|
||||
DynamicReloc(uint32_t Type, const InputSectionBase *InputSec,
|
||||
uint64_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
|
||||
int64_t Addend)
|
||||
: Type(Type), Sym(Sym), InputSec(InputSec), OffsetInSec(OffsetInSec),
|
||||
UseSymVA(UseSymVA), Addend(Addend) {}
|
||||
|
||||
DynamicReloc(uint32_t Type, const OutputSectionBase *OutputSec,
|
||||
uintX_t OffsetInSec, bool UseSymVA, SymbolBody *Sym,
|
||||
uintX_t Addend)
|
||||
: Type(Type), Sym(Sym), OutputSec(OutputSec), OffsetInSec(OffsetInSec),
|
||||
UseSymVA(UseSymVA), Addend(Addend) {}
|
||||
|
||||
uintX_t getOffset() const;
|
||||
uintX_t getAddend() const;
|
||||
uint64_t getOffset() const;
|
||||
int64_t getAddend() const;
|
||||
uint32_t getSymIndex() const;
|
||||
const OutputSectionBase *getOutputSec() const { return OutputSec; }
|
||||
const InputSectionBase<ELFT> *getInputSec() const { return InputSec; }
|
||||
const InputSectionBase *getInputSec() const { return InputSec; }
|
||||
|
||||
uint32_t Type;
|
||||
|
||||
private:
|
||||
SymbolBody *Sym;
|
||||
const InputSectionBase<ELFT> *InputSec = nullptr;
|
||||
const OutputSectionBase *OutputSec = nullptr;
|
||||
uintX_t OffsetInSec;
|
||||
const InputSectionBase *InputSec = nullptr;
|
||||
uint64_t OffsetInSec;
|
||||
bool UseSymVA;
|
||||
uintX_t Addend;
|
||||
int64_t Addend;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class DynamicSection final : public SyntheticSection<ELFT> {
|
||||
template <class ELFT> class DynamicSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Dyn Elf_Dyn;
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
// The .dynamic section contains information for the dynamic linker.
|
||||
// The section consists of fixed size entries, which consist of
|
||||
|
|
@ -295,49 +346,45 @@ class DynamicSection final : public SyntheticSection<ELFT> {
|
|||
struct Entry {
|
||||
int32_t Tag;
|
||||
union {
|
||||
OutputSectionBase *OutSec;
|
||||
InputSection<ELFT> *InSec;
|
||||
OutputSection *OutSec;
|
||||
InputSection *InSec;
|
||||
uint64_t Val;
|
||||
const SymbolBody *Sym;
|
||||
};
|
||||
enum KindT { SecAddr, SecSize, SymAddr, PlainInt, InSecAddr } Kind;
|
||||
Entry(int32_t Tag, OutputSectionBase *OutSec, KindT Kind = SecAddr)
|
||||
Entry(int32_t Tag, OutputSection *OutSec, KindT Kind = SecAddr)
|
||||
: Tag(Tag), OutSec(OutSec), Kind(Kind) {}
|
||||
Entry(int32_t Tag, InputSection<ELFT> *Sec)
|
||||
Entry(int32_t Tag, InputSection *Sec)
|
||||
: Tag(Tag), InSec(Sec), Kind(InSecAddr) {}
|
||||
Entry(int32_t Tag, uint64_t Val) : Tag(Tag), Val(Val), Kind(PlainInt) {}
|
||||
Entry(int32_t Tag, const SymbolBody *Sym)
|
||||
: Tag(Tag), Sym(Sym), Kind(SymAddr) {}
|
||||
};
|
||||
|
||||
// finalize() fills this vector with the section contents. finalize()
|
||||
// cannot directly create final section contents because when the
|
||||
// function is called, symbol or section addresses are not fixed yet.
|
||||
// finalizeContents() fills this vector with the section contents.
|
||||
std::vector<Entry> Entries;
|
||||
|
||||
public:
|
||||
DynamicSection();
|
||||
void finalize() override;
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
private:
|
||||
void addEntries();
|
||||
void add(Entry E) { Entries.push_back(E); }
|
||||
uintX_t Size = 0;
|
||||
uint64_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class RelocationSection final : public SyntheticSection<ELFT> {
|
||||
template <class ELFT> class RelocationSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Rel Elf_Rel;
|
||||
typedef typename ELFT::Rela Elf_Rela;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
public:
|
||||
RelocationSection(StringRef Name, bool Sort);
|
||||
void addReloc(const DynamicReloc<ELFT> &Reloc);
|
||||
void addReloc(const DynamicReloc &Reloc);
|
||||
unsigned getRelocOffset();
|
||||
void finalize() override;
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override { return Relocs.empty(); }
|
||||
size_t getSize() const override { return Relocs.size() * this->Entsize; }
|
||||
|
|
@ -346,7 +393,7 @@ public:
|
|||
private:
|
||||
bool Sort;
|
||||
size_t NumRelativeRelocs = 0;
|
||||
std::vector<DynamicReloc<ELFT>> Relocs;
|
||||
std::vector<DynamicReloc> Relocs;
|
||||
};
|
||||
|
||||
struct SymbolTableEntry {
|
||||
|
|
@ -354,125 +401,95 @@ struct SymbolTableEntry {
|
|||
size_t StrTabOffset;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class SymbolTableSection final : public SyntheticSection<ELFT> {
|
||||
template <class ELFT> class SymbolTableSection final : public SyntheticSection {
|
||||
public:
|
||||
typedef typename ELFT::Shdr Elf_Shdr;
|
||||
typedef typename ELFT::Sym Elf_Sym;
|
||||
typedef typename ELFT::SymRange Elf_Sym_Range;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
SymbolTableSection(StringTableSection<ELFT> &StrTabSec);
|
||||
|
||||
void finalize() override;
|
||||
SymbolTableSection(StringTableSection &StrTabSec);
|
||||
|
||||
void finalizeContents() override;
|
||||
void postThunkContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return getNumSymbols() * sizeof(Elf_Sym); }
|
||||
void addGlobal(SymbolBody *Body);
|
||||
void addLocal(SymbolBody *Body);
|
||||
StringTableSection<ELFT> &getStrTabSec() const { return StrTabSec; }
|
||||
void addSymbol(SymbolBody *Body);
|
||||
unsigned getNumSymbols() const { return Symbols.size() + 1; }
|
||||
size_t getSymbolIndex(SymbolBody *Body);
|
||||
|
||||
ArrayRef<SymbolTableEntry> getSymbols() const { return Symbols; }
|
||||
|
||||
static const OutputSectionBase *getOutputSection(SymbolBody *Sym);
|
||||
|
||||
private:
|
||||
void writeLocalSymbols(uint8_t *&Buf);
|
||||
void writeGlobalSymbols(uint8_t *Buf);
|
||||
|
||||
// A vector of symbols and their string table offsets.
|
||||
std::vector<SymbolTableEntry> Symbols;
|
||||
|
||||
StringTableSection<ELFT> &StrTabSec;
|
||||
|
||||
unsigned NumLocals = 0;
|
||||
StringTableSection &StrTabSec;
|
||||
};
|
||||
|
||||
// Outputs GNU Hash section. For detailed explanation see:
|
||||
// https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections
|
||||
template <class ELFT>
|
||||
class GnuHashTableSection final : public SyntheticSection<ELFT> {
|
||||
typedef typename ELFT::Off Elf_Off;
|
||||
typedef typename ELFT::Word Elf_Word;
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class GnuHashTableSection final : public SyntheticSection {
|
||||
public:
|
||||
GnuHashTableSection();
|
||||
void finalize() override;
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return this->Size; }
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
// Adds symbols to the hash table.
|
||||
// Sorts the input to satisfy GNU hash section requirements.
|
||||
void addSymbols(std::vector<SymbolTableEntry> &Symbols);
|
||||
|
||||
private:
|
||||
static unsigned calcNBuckets(unsigned NumHashed);
|
||||
static unsigned calcMaskWords(unsigned NumHashed);
|
||||
size_t getShift2() const { return Config->Is64 ? 6 : 5; }
|
||||
|
||||
void writeHeader(uint8_t *&Buf);
|
||||
void writeBloomFilter(uint8_t *&Buf);
|
||||
void writeBloomFilter(uint8_t *Buf);
|
||||
void writeHashTable(uint8_t *Buf);
|
||||
|
||||
struct SymbolData {
|
||||
struct Entry {
|
||||
SymbolBody *Body;
|
||||
size_t STName;
|
||||
size_t StrTabOffset;
|
||||
uint32_t Hash;
|
||||
};
|
||||
|
||||
std::vector<SymbolData> Symbols;
|
||||
|
||||
unsigned MaskWords;
|
||||
unsigned NBuckets;
|
||||
unsigned Shift2;
|
||||
uintX_t Size = 0;
|
||||
std::vector<Entry> Symbols;
|
||||
size_t MaskWords;
|
||||
size_t NBuckets = 0;
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class HashTableSection final : public SyntheticSection<ELFT> {
|
||||
typedef typename ELFT::Word Elf_Word;
|
||||
|
||||
template <class ELFT> class HashTableSection final : public SyntheticSection {
|
||||
public:
|
||||
HashTableSection();
|
||||
void finalize() override;
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override { return this->Size; }
|
||||
size_t getSize() const override { return Size; }
|
||||
|
||||
private:
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> class PltSection final : public SyntheticSection<ELFT> {
|
||||
// The PltSection is used for both the Plt and Iplt. The former always has a
|
||||
// header as its first entry that is used at run-time to resolve lazy binding.
|
||||
// The latter is used for GNU Ifunc symbols, that will be subject to a
|
||||
// Target->IRelativeRel.
|
||||
class PltSection : public SyntheticSection {
|
||||
public:
|
||||
PltSection();
|
||||
PltSection(size_t HeaderSize);
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
void addEntry(SymbolBody &Sym);
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
void addSymbols();
|
||||
|
||||
template <class ELFT> void addEntry(SymbolBody &Sym);
|
||||
|
||||
private:
|
||||
void writeHeader(uint8_t *Buf){};
|
||||
void addHeaderSymbols(){};
|
||||
unsigned getPltRelocOff() const;
|
||||
std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
|
||||
// Iplt always has HeaderSize of 0, the Plt HeaderSize is always non-zero
|
||||
size_t HeaderSize;
|
||||
};
|
||||
|
||||
// The IpltSection is a variant of Plt for recording entries for GNU Ifunc
|
||||
// symbols that will be subject to a Target->IRelativeRel
|
||||
// The IpltSection immediately follows the Plt section in the Output Section
|
||||
template <class ELFT> class IpltSection final : public SyntheticSection<ELFT> {
|
||||
public:
|
||||
IpltSection();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
void addEntry(SymbolBody &Sym);
|
||||
bool empty() const override { return Entries.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<std::pair<const SymbolBody *, unsigned>> Entries;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class GdbIndexSection final : public SyntheticSection<ELFT> {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
class GdbIndexSection final : public SyntheticSection {
|
||||
const unsigned OffsetTypeSize = 4;
|
||||
const unsigned CuListOffset = 6 * OffsetTypeSize;
|
||||
const unsigned CompilationUnitSize = 16;
|
||||
|
|
@ -481,13 +498,13 @@ class GdbIndexSection final : public SyntheticSection<ELFT> {
|
|||
|
||||
public:
|
||||
GdbIndexSection();
|
||||
void finalize() override;
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
bool empty() const override;
|
||||
|
||||
// Pairs of [CU Offset, CU length].
|
||||
std::vector<std::pair<uintX_t, uintX_t>> CompilationUnits;
|
||||
std::vector<std::pair<uint64_t, uint64_t>> CompilationUnits;
|
||||
|
||||
llvm::StringTableBuilder StringPool;
|
||||
|
||||
|
|
@ -496,11 +513,10 @@ public:
|
|||
// The CU vector portion of the constant pool.
|
||||
std::vector<std::vector<std::pair<uint32_t, uint8_t>>> CuVectors;
|
||||
|
||||
std::vector<AddressEntry<ELFT>> AddressArea;
|
||||
std::vector<AddressEntry> AddressArea;
|
||||
|
||||
private:
|
||||
void parseDebugSections();
|
||||
void readDwarf(InputSection<ELFT> *I);
|
||||
void readDwarf(InputSection *Sec);
|
||||
|
||||
uint32_t CuTypesOffset;
|
||||
uint32_t SymTabOffset;
|
||||
|
|
@ -522,10 +538,7 @@ private:
|
|||
// Detailed info about internals can be found in Ian Lance Taylor's blog:
|
||||
// http://www.airs.com/blog/archives/460 (".eh_frame")
|
||||
// http://www.airs.com/blog/archives/462 (".eh_frame_hdr")
|
||||
template <class ELFT>
|
||||
class EhFrameHeader final : public SyntheticSection<ELFT> {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
template <class ELFT> class EhFrameHeader final : public SyntheticSection {
|
||||
public:
|
||||
EhFrameHeader();
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
|
@ -551,13 +564,13 @@ private:
|
|||
// The section shall contain an array of Elf_Verdef structures, optionally
|
||||
// followed by an array of Elf_Verdaux structures.
|
||||
template <class ELFT>
|
||||
class VersionDefinitionSection final : public SyntheticSection<ELFT> {
|
||||
class VersionDefinitionSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Verdef Elf_Verdef;
|
||||
typedef typename ELFT::Verdaux Elf_Verdaux;
|
||||
|
||||
public:
|
||||
VersionDefinitionSection();
|
||||
void finalize() override;
|
||||
void finalizeContents() override;
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
|
||||
|
|
@ -574,12 +587,12 @@ private:
|
|||
// The values 0 and 1 are reserved. All other values are used for versions in
|
||||
// the own object or in any of the dependencies.
|
||||
template <class ELFT>
|
||||
class VersionTableSection final : public SyntheticSection<ELFT> {
|
||||
class VersionTableSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Versym Elf_Versym;
|
||||
|
||||
public:
|
||||
VersionTableSection();
|
||||
void finalize() override;
|
||||
void finalizeContents() override;
|
||||
size_t getSize() const override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
bool empty() const override;
|
||||
|
|
@ -590,8 +603,7 @@ public:
|
|||
// Elf_Verneed specifies the version requirements for a single DSO, and contains
|
||||
// a reference to a linked list of Elf_Vernaux data structures which define the
|
||||
// mapping from version identifiers to version names.
|
||||
template <class ELFT>
|
||||
class VersionNeedSection final : public SyntheticSection<ELFT> {
|
||||
template <class ELFT> class VersionNeedSection final : public SyntheticSection {
|
||||
typedef typename ELFT::Verneed Elf_Verneed;
|
||||
typedef typename ELFT::Vernaux Elf_Vernaux;
|
||||
|
||||
|
|
@ -604,17 +616,40 @@ class VersionNeedSection final : public SyntheticSection<ELFT> {
|
|||
|
||||
public:
|
||||
VersionNeedSection();
|
||||
void addSymbol(SharedSymbol<ELFT> *SS);
|
||||
void finalize() override;
|
||||
void addSymbol(SharedSymbol *SS);
|
||||
void finalizeContents() override;
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
size_t getSize() const override;
|
||||
size_t getNeedNum() const { return Needed.size(); }
|
||||
bool empty() const override;
|
||||
};
|
||||
|
||||
// MergeSyntheticSection is a class that allows us to put mergeable sections
|
||||
// with different attributes in a single output sections. To do that
|
||||
// we put them into MergeSyntheticSection synthetic input sections which are
|
||||
// attached to regular output sections.
|
||||
class MergeSyntheticSection final : public SyntheticSection {
|
||||
public:
|
||||
MergeSyntheticSection(StringRef Name, uint32_t Type, uint64_t Flags,
|
||||
uint32_t Alignment);
|
||||
void addSection(MergeInputSection *MS);
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
void finalizeContents() override;
|
||||
bool shouldTailMerge() const;
|
||||
size_t getSize() const override;
|
||||
|
||||
private:
|
||||
void finalizeTailMerge();
|
||||
void finalizeNoTailMerge();
|
||||
|
||||
bool Finalized = false;
|
||||
llvm::StringTableBuilder Builder;
|
||||
std::vector<MergeInputSection *> Sections;
|
||||
};
|
||||
|
||||
// .MIPS.abiflags section.
|
||||
template <class ELFT>
|
||||
class MipsAbiFlagsSection final : public SyntheticSection<ELFT> {
|
||||
class MipsAbiFlagsSection final : public SyntheticSection {
|
||||
typedef llvm::object::Elf_Mips_ABIFlags<ELFT> Elf_Mips_ABIFlags;
|
||||
|
||||
public:
|
||||
|
|
@ -629,8 +664,7 @@ private:
|
|||
};
|
||||
|
||||
// .MIPS.options section.
|
||||
template <class ELFT>
|
||||
class MipsOptionsSection final : public SyntheticSection<ELFT> {
|
||||
template <class ELFT> class MipsOptionsSection final : public SyntheticSection {
|
||||
typedef llvm::object::Elf_Mips_Options<ELFT> Elf_Mips_Options;
|
||||
typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
|
||||
|
||||
|
|
@ -649,8 +683,7 @@ private:
|
|||
};
|
||||
|
||||
// MIPS .reginfo section.
|
||||
template <class ELFT>
|
||||
class MipsReginfoSection final : public SyntheticSection<ELFT> {
|
||||
template <class ELFT> class MipsReginfoSection final : public SyntheticSection {
|
||||
typedef llvm::object::Elf_Mips_RegInfo<ELFT> Elf_Mips_RegInfo;
|
||||
|
||||
public:
|
||||
|
|
@ -668,78 +701,95 @@ private:
|
|||
// of executable file which is pointed to by the DT_MIPS_RLD_MAP entry.
|
||||
// See "Dynamic section" in Chapter 5 in the following document:
|
||||
// ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
||||
template <class ELFT> class MipsRldMapSection : public SyntheticSection<ELFT> {
|
||||
class MipsRldMapSection : public SyntheticSection {
|
||||
public:
|
||||
MipsRldMapSection();
|
||||
size_t getSize() const override { return sizeof(typename ELFT::uint); }
|
||||
size_t getSize() const override { return Config->Wordsize; }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
};
|
||||
|
||||
template <class ELFT> class ARMExidxSentinelSection : public SyntheticSection<ELFT> {
|
||||
class ARMExidxSentinelSection : public SyntheticSection {
|
||||
public:
|
||||
ARMExidxSentinelSection();
|
||||
size_t getSize() const override { return 8; }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
};
|
||||
|
||||
template <class ELFT> InputSection<ELFT> *createCommonSection();
|
||||
template <class ELFT> InputSection<ELFT> *createInterpSection();
|
||||
template <class ELFT> MergeInputSection<ELFT> *createCommentSection();
|
||||
// A container for one or more linker generated thunks. Instances of these
|
||||
// thunks including ARM interworking and Mips LA25 PI to non-PI thunks.
|
||||
class ThunkSection : public SyntheticSection {
|
||||
public:
|
||||
// ThunkSection in OS, with desired OutSecOff of Off
|
||||
ThunkSection(OutputSection *OS, uint64_t Off);
|
||||
|
||||
// Add a newly created Thunk to this container:
|
||||
// Thunk is given offset from start of this InputSection
|
||||
// Thunk defines a symbol in this InputSection that can be used as target
|
||||
// of a relocation
|
||||
void addThunk(Thunk *T);
|
||||
size_t getSize() const override { return Size; }
|
||||
void writeTo(uint8_t *Buf) override;
|
||||
InputSection *getTargetInputSection() const;
|
||||
|
||||
private:
|
||||
std::vector<const Thunk *> Thunks;
|
||||
size_t Size = 0;
|
||||
};
|
||||
|
||||
template <class ELFT> InputSection *createCommonSection();
|
||||
InputSection *createInterpSection();
|
||||
template <class ELFT> MergeInputSection *createCommentSection();
|
||||
template <class ELFT>
|
||||
SymbolBody *addSyntheticLocal(StringRef Name, uint8_t Type, uint64_t Value,
|
||||
uint64_t Size, InputSectionBase *Section);
|
||||
|
||||
// Linker generated sections which can be used as inputs.
|
||||
template <class ELFT> struct In {
|
||||
static InputSection<ELFT> *ARMAttributes;
|
||||
static BuildIdSection<ELFT> *BuildId;
|
||||
static InputSection<ELFT> *Common;
|
||||
struct InX {
|
||||
static InputSection *ARMAttributes;
|
||||
static BssSection *Bss;
|
||||
static BssSection *BssRelRo;
|
||||
static BuildIdSection *BuildId;
|
||||
static InputSection *Common;
|
||||
static StringTableSection *DynStrTab;
|
||||
static InputSection *Interp;
|
||||
static GdbIndexSection *GdbIndex;
|
||||
static GotPltSection *GotPlt;
|
||||
static IgotPltSection *IgotPlt;
|
||||
static MipsGotSection *MipsGot;
|
||||
static MipsRldMapSection *MipsRldMap;
|
||||
static PltSection *Plt;
|
||||
static PltSection *Iplt;
|
||||
static StringTableSection *ShStrTab;
|
||||
static StringTableSection *StrTab;
|
||||
};
|
||||
|
||||
template <class ELFT> struct In : public InX {
|
||||
static DynamicSection<ELFT> *Dynamic;
|
||||
static StringTableSection<ELFT> *DynStrTab;
|
||||
static SymbolTableSection<ELFT> *DynSymTab;
|
||||
static EhFrameHeader<ELFT> *EhFrameHdr;
|
||||
static GnuHashTableSection<ELFT> *GnuHashTab;
|
||||
static GdbIndexSection<ELFT> *GdbIndex;
|
||||
static GotSection<ELFT> *Got;
|
||||
static MipsGotSection<ELFT> *MipsGot;
|
||||
static GotPltSection<ELFT> *GotPlt;
|
||||
static IgotPltSection<ELFT> *IgotPlt;
|
||||
static EhFrameSection<ELFT> *EhFrame;
|
||||
static HashTableSection<ELFT> *HashTab;
|
||||
static InputSection<ELFT> *Interp;
|
||||
static MipsRldMapSection<ELFT> *MipsRldMap;
|
||||
static PltSection<ELFT> *Plt;
|
||||
static IpltSection<ELFT> *Iplt;
|
||||
static RelocationSection<ELFT> *RelaDyn;
|
||||
static RelocationSection<ELFT> *RelaPlt;
|
||||
static RelocationSection<ELFT> *RelaIplt;
|
||||
static StringTableSection<ELFT> *ShStrTab;
|
||||
static StringTableSection<ELFT> *StrTab;
|
||||
static SymbolTableSection<ELFT> *SymTab;
|
||||
static VersionDefinitionSection<ELFT> *VerDef;
|
||||
static VersionTableSection<ELFT> *VerSym;
|
||||
static VersionNeedSection<ELFT> *VerNeed;
|
||||
};
|
||||
|
||||
template <class ELFT> InputSection<ELFT> *In<ELFT>::ARMAttributes;
|
||||
template <class ELFT> BuildIdSection<ELFT> *In<ELFT>::BuildId;
|
||||
template <class ELFT> InputSection<ELFT> *In<ELFT>::Common;
|
||||
template <class ELFT> DynamicSection<ELFT> *In<ELFT>::Dynamic;
|
||||
template <class ELFT> StringTableSection<ELFT> *In<ELFT>::DynStrTab;
|
||||
template <class ELFT> SymbolTableSection<ELFT> *In<ELFT>::DynSymTab;
|
||||
template <class ELFT> EhFrameHeader<ELFT> *In<ELFT>::EhFrameHdr;
|
||||
template <class ELFT> GdbIndexSection<ELFT> *In<ELFT>::GdbIndex;
|
||||
template <class ELFT> GnuHashTableSection<ELFT> *In<ELFT>::GnuHashTab;
|
||||
template <class ELFT> GotSection<ELFT> *In<ELFT>::Got;
|
||||
template <class ELFT> MipsGotSection<ELFT> *In<ELFT>::MipsGot;
|
||||
template <class ELFT> GotPltSection<ELFT> *In<ELFT>::GotPlt;
|
||||
template <class ELFT> IgotPltSection<ELFT> *In<ELFT>::IgotPlt;
|
||||
template <class ELFT> EhFrameSection<ELFT> *In<ELFT>::EhFrame;
|
||||
template <class ELFT> HashTableSection<ELFT> *In<ELFT>::HashTab;
|
||||
template <class ELFT> InputSection<ELFT> *In<ELFT>::Interp;
|
||||
template <class ELFT> MipsRldMapSection<ELFT> *In<ELFT>::MipsRldMap;
|
||||
template <class ELFT> PltSection<ELFT> *In<ELFT>::Plt;
|
||||
template <class ELFT> IpltSection<ELFT> *In<ELFT>::Iplt;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaDyn;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaPlt;
|
||||
template <class ELFT> RelocationSection<ELFT> *In<ELFT>::RelaIplt;
|
||||
template <class ELFT> StringTableSection<ELFT> *In<ELFT>::ShStrTab;
|
||||
template <class ELFT> StringTableSection<ELFT> *In<ELFT>::StrTab;
|
||||
template <class ELFT> SymbolTableSection<ELFT> *In<ELFT>::SymTab;
|
||||
template <class ELFT> VersionDefinitionSection<ELFT> *In<ELFT>::VerDef;
|
||||
template <class ELFT> VersionTableSection<ELFT> *In<ELFT>::VerSym;
|
||||
|
|
|
|||
437
ELF/Target.cpp
437
ELF/Target.cpp
|
|
@ -45,7 +45,10 @@ using namespace llvm::support::endian;
|
|||
using namespace llvm::ELF;
|
||||
|
||||
std::string lld::toString(uint32_t Type) {
|
||||
return getELFRelocationTypeName(elf::Config->EMachine, Type);
|
||||
StringRef S = getELFRelocationTypeName(elf::Config->EMachine, Type);
|
||||
if (S == "Unknown")
|
||||
return ("Unknown (" + Twine(Type) + ")").str();
|
||||
return S;
|
||||
}
|
||||
|
||||
namespace lld {
|
||||
|
|
@ -56,20 +59,20 @@ TargetInfo *Target;
|
|||
static void or32le(uint8_t *P, int32_t V) { write32le(P, read32le(P) | V); }
|
||||
static void or32be(uint8_t *P, int32_t V) { write32be(P, read32be(P) | V); }
|
||||
|
||||
template <class ELFT> static std::string getErrorLoc(uint8_t *Loc) {
|
||||
for (InputSectionData *D : Symtab<ELFT>::X->Sections) {
|
||||
auto *IS = dyn_cast_or_null<InputSection<ELFT>>(D);
|
||||
template <class ELFT> static std::string getErrorLoc(const uint8_t *Loc) {
|
||||
for (InputSectionBase *D : InputSections) {
|
||||
auto *IS = dyn_cast_or_null<InputSection>(D);
|
||||
if (!IS || !IS->OutSec)
|
||||
continue;
|
||||
|
||||
uint8_t *ISLoc = cast<OutputSection<ELFT>>(IS->OutSec)->Loc + IS->OutSecOff;
|
||||
uint8_t *ISLoc = cast<OutputSection>(IS->OutSec)->Loc + IS->OutSecOff;
|
||||
if (ISLoc <= Loc && Loc < ISLoc + IS->getSize())
|
||||
return IS->getLocation(Loc - ISLoc) + ": ";
|
||||
return IS->template getLocation<ELFT>(Loc - ISLoc) + ": ";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string getErrorLocation(uint8_t *Loc) {
|
||||
static std::string getErrorLocation(const uint8_t *Loc) {
|
||||
switch (Config->EKind) {
|
||||
case ELF32LEKind:
|
||||
return getErrorLoc<ELF32LE>(Loc);
|
||||
|
|
@ -116,17 +119,17 @@ namespace {
|
|||
class X86TargetInfo final : public TargetInfo {
|
||||
public:
|
||||
X86TargetInfo();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
|
||||
uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
void writeGotPltHeader(uint8_t *Buf) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
bool isTlsLocalDynamicRel(uint32_t Type) const override;
|
||||
bool isTlsGlobalDynamicRel(uint32_t Type) const override;
|
||||
bool isTlsInitialExecRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
|
||||
|
|
@ -141,15 +144,15 @@ public:
|
|||
template <class ELFT> class X86_64TargetInfo final : public TargetInfo {
|
||||
public:
|
||||
X86_64TargetInfo();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
bool isTlsLocalDynamicRel(uint32_t Type) const override;
|
||||
bool isTlsGlobalDynamicRel(uint32_t Type) const override;
|
||||
bool isTlsInitialExecRel(uint32_t Type) const override;
|
||||
void writeGotPltHeader(uint8_t *Buf) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
|
||||
|
|
@ -170,14 +173,16 @@ class PPCTargetInfo final : public TargetInfo {
|
|||
public:
|
||||
PPCTargetInfo();
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
};
|
||||
|
||||
class PPC64TargetInfo final : public TargetInfo {
|
||||
public:
|
||||
PPC64TargetInfo();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
};
|
||||
|
|
@ -185,12 +190,13 @@ public:
|
|||
class AArch64TargetInfo final : public TargetInfo {
|
||||
public:
|
||||
AArch64TargetInfo();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
bool isTlsInitialExecRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
bool usesOnlyLowPageBits(uint32_t Type) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
|
|
@ -205,44 +211,47 @@ class AMDGPUTargetInfo final : public TargetInfo {
|
|||
public:
|
||||
AMDGPUTargetInfo();
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
};
|
||||
|
||||
class ARMTargetInfo final : public TargetInfo {
|
||||
public:
|
||||
ARMTargetInfo();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
bool isTlsLocalDynamicRel(uint32_t Type) const override;
|
||||
bool isTlsGlobalDynamicRel(uint32_t Type) const override;
|
||||
bool isTlsInitialExecRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType, const InputFile &File,
|
||||
const SymbolBody &S) const override;
|
||||
void addPltSymbols(InputSectionBase *IS, uint64_t Off) const override;
|
||||
void addPltHeaderSymbols(InputSectionBase *ISD) const override;
|
||||
bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
|
||||
const SymbolBody &S) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
};
|
||||
|
||||
template <class ELFT> class MipsTargetInfo final : public TargetInfo {
|
||||
public:
|
||||
MipsTargetInfo();
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override;
|
||||
uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const override;
|
||||
int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override;
|
||||
bool isPicRel(uint32_t Type) const override;
|
||||
uint32_t getDynRel(uint32_t Type) const override;
|
||||
bool isTlsLocalDynamicRel(uint32_t Type) const override;
|
||||
bool isTlsGlobalDynamicRel(uint32_t Type) const override;
|
||||
void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override;
|
||||
void writePltHeader(uint8_t *Buf) const override;
|
||||
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
|
||||
void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
|
||||
int32_t Index, unsigned RelOff) const override;
|
||||
RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType, const InputFile &File,
|
||||
const SymbolBody &S) const override;
|
||||
bool needsThunk(RelExpr Expr, uint32_t RelocType, const InputFile *File,
|
||||
const SymbolBody &S) const override;
|
||||
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
|
||||
bool usesOnlyLowPageBits(uint32_t Type) const override;
|
||||
};
|
||||
|
|
@ -286,25 +295,21 @@ TargetInfo *createTarget() {
|
|||
|
||||
TargetInfo::~TargetInfo() {}
|
||||
|
||||
uint64_t TargetInfo::getImplicitAddend(const uint8_t *Buf,
|
||||
uint32_t Type) const {
|
||||
int64_t TargetInfo::getImplicitAddend(const uint8_t *Buf, uint32_t Type) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TargetInfo::usesOnlyLowPageBits(uint32_t Type) const { return false; }
|
||||
|
||||
RelExpr TargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile &File,
|
||||
const SymbolBody &S) const {
|
||||
return Expr;
|
||||
bool TargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile *File, const SymbolBody &S) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return false; }
|
||||
|
||||
bool TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { return false; }
|
||||
|
||||
bool TargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const { return false; }
|
||||
|
||||
void TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
writeGotPlt(Buf, S);
|
||||
}
|
||||
|
|
@ -352,10 +357,14 @@ X86TargetInfo::X86TargetInfo() {
|
|||
PltEntrySize = 16;
|
||||
PltHeaderSize = 16;
|
||||
TlsGdRelaxSkip = 2;
|
||||
// 0xCC is the "int3" (call debug exception handler) instruction.
|
||||
TrapInstr = 0xcccccccc;
|
||||
}
|
||||
|
||||
RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
||||
RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_386_8:
|
||||
case R_386_16:
|
||||
case R_386_32:
|
||||
case R_386_TLS_LDO_32:
|
||||
|
|
@ -366,6 +375,7 @@ RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
|||
return R_TLSLD;
|
||||
case R_386_PLT32:
|
||||
return R_PLT_PC;
|
||||
case R_386_PC8:
|
||||
case R_386_PC16:
|
||||
case R_386_PC32:
|
||||
return R_PC;
|
||||
|
|
@ -375,6 +385,24 @@ RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
|||
return R_GOT;
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
// These relocations can be calculated in two different ways.
|
||||
// Usual calculation is G + A - GOT what means an offset in GOT table
|
||||
// (R_GOT_FROM_END). When instruction pointed by relocation has no base
|
||||
// register, then relocations can be used when PIC code is disabled. In that
|
||||
// case calculation is G + A, it resolves to an address of entry in GOT
|
||||
// (R_GOT) and not an offset.
|
||||
//
|
||||
// To check that instruction has no base register we scan ModR/M byte.
|
||||
// See "Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte"
|
||||
// (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/
|
||||
// 64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf)
|
||||
if ((Loc[-1] & 0xc7) != 0x5)
|
||||
return R_GOT_FROM_END;
|
||||
if (Config->Pic)
|
||||
error(toString(S.File) + ": relocation " + toString(Type) + " against '" +
|
||||
S.getName() +
|
||||
"' without base register can not be used when PIC enabled");
|
||||
return R_GOT;
|
||||
case R_386_TLS_GOTIE:
|
||||
return R_GOT_FROM_END;
|
||||
case R_386_GOTOFF:
|
||||
|
|
@ -384,10 +412,9 @@ RelExpr X86TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
|||
case R_386_TLS_LE_32:
|
||||
return R_NEG_TLS;
|
||||
case R_386_NONE:
|
||||
return R_HINT;
|
||||
return R_NONE;
|
||||
default:
|
||||
error("do not know how to handle relocation '" + toString(Type) + "' (" +
|
||||
Twine(Type) + ")");
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
}
|
||||
|
|
@ -411,12 +438,12 @@ void X86TargetInfo::writeGotPltHeader(uint8_t *Buf) const {
|
|||
void X86TargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
// Entries in .got.plt initially points back to the corresponding
|
||||
// PLT entries with a fixed offset to skip the first instruction.
|
||||
write32le(Buf, S.getPltVA<ELF32LE>() + 6);
|
||||
write32le(Buf, S.getPltVA() + 6);
|
||||
}
|
||||
|
||||
void X86TargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
// An x86 entry is the address of the ifunc resolver function.
|
||||
write32le(Buf, S.getVA<ELF32LE>());
|
||||
write32le(Buf, S.getVA());
|
||||
}
|
||||
|
||||
uint32_t X86TargetInfo::getDynRel(uint32_t Type) const {
|
||||
|
|
@ -427,10 +454,6 @@ uint32_t X86TargetInfo::getDynRel(uint32_t Type) const {
|
|||
return Type;
|
||||
}
|
||||
|
||||
bool X86TargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const {
|
||||
return Type == R_386_TLS_GD;
|
||||
}
|
||||
|
||||
bool X86TargetInfo::isTlsLocalDynamicRel(uint32_t Type) const {
|
||||
return Type == R_386_TLS_LDO_32 || Type == R_386_TLS_LDM;
|
||||
}
|
||||
|
|
@ -440,30 +463,33 @@ bool X86TargetInfo::isTlsInitialExecRel(uint32_t Type) const {
|
|||
}
|
||||
|
||||
void X86TargetInfo::writePltHeader(uint8_t *Buf) const {
|
||||
// Executable files and shared object files have
|
||||
// separate procedure linkage tables.
|
||||
if (Config->Pic) {
|
||||
const uint8_t V[] = {
|
||||
0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl 4(%ebx)
|
||||
0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *8(%ebx)
|
||||
0x90, 0x90, 0x90, 0x90 // nop; nop; nop; nop
|
||||
0xff, 0xb3, 0x04, 0x00, 0x00, 0x00, // pushl GOTPLT+4(%ebx)
|
||||
0xff, 0xa3, 0x08, 0x00, 0x00, 0x00, // jmp *GOTPLT+8(%ebx)
|
||||
0x90, 0x90, 0x90, 0x90 // nop
|
||||
};
|
||||
memcpy(Buf, V, sizeof(V));
|
||||
|
||||
uint32_t Ebx = In<ELF32LE>::Got->getVA() + In<ELF32LE>::Got->getSize();
|
||||
uint32_t GotPlt = In<ELF32LE>::GotPlt->getVA() - Ebx;
|
||||
write32le(Buf + 2, GotPlt + 4);
|
||||
write32le(Buf + 8, GotPlt + 8);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t PltData[] = {
|
||||
0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushl (GOT+4)
|
||||
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *(GOT+8)
|
||||
0x90, 0x90, 0x90, 0x90 // nop; nop; nop; nop
|
||||
0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushl (GOTPLT+4)
|
||||
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *(GOTPLT+8)
|
||||
0x90, 0x90, 0x90, 0x90 // nop
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint32_t Got = In<ELF32LE>::GotPlt->getVA();
|
||||
write32le(Buf + 2, Got + 4);
|
||||
write32le(Buf + 8, Got + 8);
|
||||
uint32_t GotPlt = In<ELF32LE>::GotPlt->getVA();
|
||||
write32le(Buf + 2, GotPlt + 4);
|
||||
write32le(Buf + 8, GotPlt + 8);
|
||||
}
|
||||
|
||||
void X86TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
||||
void X86TargetInfo::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
const uint8_t Inst[] = {
|
||||
|
|
@ -473,22 +499,32 @@ void X86TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
|||
};
|
||||
memcpy(Buf, Inst, sizeof(Inst));
|
||||
|
||||
// jmp *foo@GOT(%ebx) or jmp *foo_in_GOT
|
||||
Buf[1] = Config->Pic ? 0xa3 : 0x25;
|
||||
uint32_t Got = In<ELF32LE>::GotPlt->getVA();
|
||||
write32le(Buf + 2, Config->Shared ? GotEntryAddr - Got : GotEntryAddr);
|
||||
if (Config->Pic) {
|
||||
// jmp *foo@GOT(%ebx)
|
||||
uint32_t Ebx = In<ELF32LE>::Got->getVA() + In<ELF32LE>::Got->getSize();
|
||||
Buf[1] = 0xa3;
|
||||
write32le(Buf + 2, GotPltEntryAddr - Ebx);
|
||||
} else {
|
||||
// jmp *foo_in_GOT
|
||||
Buf[1] = 0x25;
|
||||
write32le(Buf + 2, GotPltEntryAddr);
|
||||
}
|
||||
|
||||
write32le(Buf + 7, RelOff);
|
||||
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
|
||||
}
|
||||
|
||||
uint64_t X86TargetInfo::getImplicitAddend(const uint8_t *Buf,
|
||||
uint32_t Type) const {
|
||||
int64_t X86TargetInfo::getImplicitAddend(const uint8_t *Buf,
|
||||
uint32_t Type) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return 0;
|
||||
case R_386_8:
|
||||
case R_386_PC8:
|
||||
return SignExtend64<8>(*Buf);
|
||||
case R_386_16:
|
||||
case R_386_PC16:
|
||||
return read16le(Buf);
|
||||
return SignExtend64<16>(read16le(Buf));
|
||||
case R_386_32:
|
||||
case R_386_GOT32:
|
||||
case R_386_GOT32X:
|
||||
|
|
@ -497,21 +533,36 @@ uint64_t X86TargetInfo::getImplicitAddend(const uint8_t *Buf,
|
|||
case R_386_PC32:
|
||||
case R_386_PLT32:
|
||||
case R_386_TLS_LE:
|
||||
return read32le(Buf);
|
||||
return SignExtend64<32>(read32le(Buf));
|
||||
}
|
||||
}
|
||||
|
||||
void X86TargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
|
||||
// R_386_PC16 and R_386_16 are not part of the current i386 psABI. They are
|
||||
// used by 16-bit x86 objects, like boot loaders.
|
||||
if (Type == R_386_16 || Type == R_386_PC16) {
|
||||
// R_386_{PC,}{8,16} are not part of the i386 psABI, but they are
|
||||
// being used for some 16-bit programs such as boot loaders, so
|
||||
// we want to support them.
|
||||
switch (Type) {
|
||||
case R_386_8:
|
||||
checkUInt<8>(Loc, Val, Type);
|
||||
*Loc = Val;
|
||||
break;
|
||||
case R_386_PC8:
|
||||
checkInt<8>(Loc, Val, Type);
|
||||
*Loc = Val;
|
||||
break;
|
||||
case R_386_16:
|
||||
checkUInt<16>(Loc, Val, Type);
|
||||
write16le(Loc, Val);
|
||||
return;
|
||||
break;
|
||||
case R_386_PC16:
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
write16le(Loc, Val);
|
||||
break;
|
||||
default:
|
||||
checkInt<32>(Loc, Val, Type);
|
||||
write32le(Loc, Val);
|
||||
}
|
||||
write32le(Loc, Val);
|
||||
}
|
||||
|
||||
void X86TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
|
||||
|
|
@ -527,7 +578,7 @@ void X86TargetInfo::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
|
|||
0x81, 0xe8, 0x00, 0x00, 0x00, 0x00 // subl 0(%ebx), %eax
|
||||
};
|
||||
memcpy(Loc - 3, Inst, sizeof(Inst));
|
||||
relocateOne(Loc + 5, R_386_32, Val);
|
||||
write32le(Loc + 5, Val);
|
||||
}
|
||||
|
||||
void X86TargetInfo::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
|
||||
|
|
@ -543,7 +594,7 @@ void X86TargetInfo::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
|
|||
0x03, 0x83, 0x00, 0x00, 0x00, 0x00 // addl 0(%ebx), %eax
|
||||
};
|
||||
memcpy(Loc - 3, Inst, sizeof(Inst));
|
||||
relocateOne(Loc + 5, R_386_32, Val);
|
||||
write32le(Loc + 5, Val);
|
||||
}
|
||||
|
||||
// In some conditions, relocations can be optimized to avoid using GOT.
|
||||
|
|
@ -583,13 +634,13 @@ void X86TargetInfo::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
|
|||
Loc[-1] = 0x80 | (Reg << 3) | Reg;
|
||||
}
|
||||
}
|
||||
relocateOne(Loc, R_386_TLS_LE, Val);
|
||||
write32le(Loc, Val);
|
||||
}
|
||||
|
||||
void X86TargetInfo::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
if (Type == R_386_TLS_LDO_32) {
|
||||
relocateOne(Loc, R_386_TLS_LE, Val);
|
||||
write32le(Loc, Val);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -625,12 +676,16 @@ template <class ELFT> X86_64TargetInfo<ELFT>::X86_64TargetInfo() {
|
|||
// Align to the large page size (known as a superpage or huge page).
|
||||
// FreeBSD automatically promotes large, superpage-aligned allocations.
|
||||
DefaultImageBase = 0x200000;
|
||||
// 0xCC is the "int3" (call debug exception handler) instruction.
|
||||
TrapInstr = 0xcccccccc;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr X86_64TargetInfo<ELFT>::getRelExpr(uint32_t Type,
|
||||
const SymbolBody &S) const {
|
||||
RelExpr X86_64TargetInfo<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_X86_64_8:
|
||||
case R_X86_64_16:
|
||||
case R_X86_64_32:
|
||||
case R_X86_64_32S:
|
||||
case R_X86_64_64:
|
||||
|
|
@ -660,10 +715,9 @@ RelExpr X86_64TargetInfo<ELFT>::getRelExpr(uint32_t Type,
|
|||
case R_X86_64_GOTTPOFF:
|
||||
return R_GOT_PC;
|
||||
case R_X86_64_NONE:
|
||||
return R_HINT;
|
||||
return R_NONE;
|
||||
default:
|
||||
error("do not know how to handle relocation '" + toString(Type) + "' (" +
|
||||
Twine(Type) + ")");
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
}
|
||||
|
|
@ -681,25 +735,25 @@ template <class ELFT>
|
|||
void X86_64TargetInfo<ELFT>::writeGotPlt(uint8_t *Buf,
|
||||
const SymbolBody &S) const {
|
||||
// See comments in X86TargetInfo::writeGotPlt.
|
||||
write32le(Buf, S.getPltVA<ELFT>() + 6);
|
||||
write32le(Buf, S.getPltVA() + 6);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64TargetInfo<ELFT>::writePltHeader(uint8_t *Buf) const {
|
||||
const uint8_t PltData[] = {
|
||||
0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushq GOT+8(%rip)
|
||||
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *GOT+16(%rip)
|
||||
0x0f, 0x1f, 0x40, 0x00 // nopl 0x0(rax)
|
||||
0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // pushq GOTPLT+8(%rip)
|
||||
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *GOTPLT+16(%rip)
|
||||
0x0f, 0x1f, 0x40, 0x00 // nop
|
||||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint64_t Got = In<ELFT>::GotPlt->getVA();
|
||||
uint64_t GotPlt = In<ELFT>::GotPlt->getVA();
|
||||
uint64_t Plt = In<ELFT>::Plt->getVA();
|
||||
write32le(Buf + 2, Got - Plt + 2); // GOT+8
|
||||
write32le(Buf + 8, Got - Plt + 4); // GOT+16
|
||||
write32le(Buf + 2, GotPlt - Plt + 2); // GOTPLT+8
|
||||
write32le(Buf + 8, GotPlt - Plt + 4); // GOTPLT+16
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void X86_64TargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
||||
void X86_64TargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
const uint8_t Inst[] = {
|
||||
|
|
@ -709,7 +763,7 @@ void X86_64TargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
|||
};
|
||||
memcpy(Buf, Inst, sizeof(Inst));
|
||||
|
||||
write32le(Buf + 2, GotEntryAddr - PltEntryAddr - 6);
|
||||
write32le(Buf + 2, GotPltEntryAddr - PltEntryAddr - 6);
|
||||
write32le(Buf + 7, Index);
|
||||
write32le(Buf + 12, -Index * PltEntrySize - PltHeaderSize - 16);
|
||||
}
|
||||
|
|
@ -724,11 +778,6 @@ bool X86_64TargetInfo<ELFT>::isTlsInitialExecRel(uint32_t Type) const {
|
|||
return Type == R_X86_64_GOTTPOFF;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool X86_64TargetInfo<ELFT>::isTlsGlobalDynamicRel(uint32_t Type) const {
|
||||
return Type == R_X86_64_TLSGD;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool X86_64TargetInfo<ELFT>::isTlsLocalDynamicRel(uint32_t Type) const {
|
||||
return Type == R_X86_64_DTPOFF32 || Type == R_X86_64_DTPOFF64 ||
|
||||
|
|
@ -752,9 +801,10 @@ void X86_64TargetInfo<ELFT>::relaxTlsGdToLe(uint8_t *Loc, uint32_t Type,
|
|||
0x48, 0x8d, 0x80, 0x00, 0x00, 0x00, 0x00 // lea x@tpoff,%rax
|
||||
};
|
||||
memcpy(Loc - 4, Inst, sizeof(Inst));
|
||||
|
||||
// The original code used a pc relative relocation and so we have to
|
||||
// compensate for the -4 in had in the addend.
|
||||
relocateOne(Loc + 8, R_X86_64_TPOFF32, Val + 4);
|
||||
write32le(Loc + 8, Val + 4);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
|
|
@ -774,9 +824,10 @@ void X86_64TargetInfo<ELFT>::relaxTlsGdToIe(uint8_t *Loc, uint32_t Type,
|
|||
0x48, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00 // addq x@tpoff,%rax
|
||||
};
|
||||
memcpy(Loc - 4, Inst, sizeof(Inst));
|
||||
|
||||
// Both code sequences are PC relatives, but since we are moving the constant
|
||||
// forward by 8 bytes we have to subtract the value by 8.
|
||||
relocateOne(Loc + 8, R_X86_64_PC32, Val - 8);
|
||||
write32le(Loc + 8, Val - 8);
|
||||
}
|
||||
|
||||
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
|
||||
|
|
@ -821,7 +872,7 @@ void X86_64TargetInfo<ELFT>::relaxTlsIeToLe(uint8_t *Loc, uint32_t Type,
|
|||
|
||||
// The original code used a PC relative relocation.
|
||||
// Need to compensate for the -4 it had in the addend.
|
||||
relocateOne(Loc, R_X86_64_TPOFF32, Val + 4);
|
||||
write32le(Loc, Val + 4);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
|
|
@ -841,7 +892,7 @@ void X86_64TargetInfo<ELFT>::relaxTlsLdToLe(uint8_t *Loc, uint32_t Type,
|
|||
return;
|
||||
}
|
||||
if (Type == R_X86_64_DTPOFF32) {
|
||||
relocateOne(Loc, R_X86_64_TPOFF32, Val);
|
||||
write32le(Loc, Val);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -857,6 +908,14 @@ template <class ELFT>
|
|||
void X86_64TargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
|
||||
uint64_t Val) const {
|
||||
switch (Type) {
|
||||
case R_X86_64_8:
|
||||
checkUInt<8>(Loc, Val, Type);
|
||||
*Loc = Val;
|
||||
break;
|
||||
case R_X86_64_16:
|
||||
checkUInt<16>(Loc, Val, Type);
|
||||
write16le(Loc, Val);
|
||||
break;
|
||||
case R_X86_64_32:
|
||||
checkUInt<32>(Loc, Val, Type);
|
||||
write32le(Loc, Val);
|
||||
|
|
@ -898,12 +957,14 @@ RelExpr X86_64TargetInfo<ELFT>::adjustRelaxExpr(uint32_t Type,
|
|||
return RelExpr;
|
||||
const uint8_t Op = Data[-2];
|
||||
const uint8_t ModRm = Data[-1];
|
||||
|
||||
// FIXME: When PIC is disabled and foo is defined locally in the
|
||||
// lower 32 bit address space, memory operand in mov can be converted into
|
||||
// immediate operand. Otherwise, mov must be changed to lea. We support only
|
||||
// latter relaxation at this moment.
|
||||
if (Op == 0x8b)
|
||||
return R_RELAX_GOT_PC;
|
||||
|
||||
// Relax call and jmp.
|
||||
if (Op == 0xff && (ModRm == 0x15 || ModRm == 0x25))
|
||||
return R_RELAX_GOT_PC;
|
||||
|
|
@ -961,7 +1022,7 @@ void X86_64TargetInfo<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val,
|
|||
// SIB.base field.
|
||||
// See "2.2.1.2 More on REX Prefix Fields " (2-8 Vol. 2A).
|
||||
Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2;
|
||||
relocateOne(Loc, R_X86_64_PC32, Val);
|
||||
write32le(Loc, Val);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -982,7 +1043,7 @@ void X86_64TargetInfo<ELFT>::relaxGotNoPic(uint8_t *Loc, uint64_t Val,
|
|||
// descriptions about each operation.
|
||||
Loc[-2] = 0x81;
|
||||
Loc[-3] = (Rex & ~0x4) | (Rex & 0x4) >> 2;
|
||||
relocateOne(Loc, R_X86_64_PC32, Val);
|
||||
write32le(Loc, Val);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
|
|
@ -993,7 +1054,7 @@ void X86_64TargetInfo<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const {
|
|||
// Convert "mov foo@GOTPCREL(%rip),%reg" to "lea foo(%rip),%reg".
|
||||
if (Op == 0x8b) {
|
||||
Loc[-2] = 0x8d;
|
||||
relocateOne(Loc, R_X86_64_PC32, Val);
|
||||
write32le(Loc, Val);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1012,7 +1073,7 @@ void X86_64TargetInfo<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const {
|
|||
// prefix. That makes result expression to be a single instruction.
|
||||
Loc[-2] = 0x67; // addr32 prefix
|
||||
Loc[-1] = 0xe8; // call
|
||||
relocateOne(Loc, R_X86_64_PC32, Val);
|
||||
write32le(Loc, Val);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1021,7 +1082,7 @@ void X86_64TargetInfo<ELFT>::relaxGot(uint8_t *Loc, uint64_t Val) const {
|
|||
assert(ModRm == 0x25);
|
||||
Loc[-2] = 0xe9; // jmp
|
||||
Loc[3] = 0x90; // nop
|
||||
relocateOne(Loc - 1, R_X86_64_PC32, Val + 1);
|
||||
write32le(Loc - 1, Val + 1);
|
||||
}
|
||||
|
||||
// Relocation masks following the #lo(value), #hi(value), #ha(value),
|
||||
|
|
@ -1059,7 +1120,8 @@ void PPCTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
|
|||
}
|
||||
}
|
||||
|
||||
RelExpr PPCTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
||||
RelExpr PPCTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_PPC_REL24:
|
||||
case R_PPC_REL32:
|
||||
|
|
@ -1108,7 +1170,8 @@ uint64_t getPPC64TocBase() {
|
|||
return TocVA + PPC64TocOffset;
|
||||
}
|
||||
|
||||
RelExpr PPC64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
||||
RelExpr PPC64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
|
|
@ -1126,10 +1189,10 @@ RelExpr PPC64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
|||
}
|
||||
}
|
||||
|
||||
void PPC64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
||||
void PPC64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
uint64_t Off = GotEntryAddr - getPPC64TocBase();
|
||||
uint64_t Off = GotPltEntryAddr - getPPC64TocBase();
|
||||
|
||||
// FIXME: What we should do, in theory, is get the offset of the function
|
||||
// descriptor in the .opd section, and use that as the offset from %r2 (the
|
||||
|
|
@ -1256,8 +1319,8 @@ AArch64TargetInfo::AArch64TargetInfo() {
|
|||
TcbSize = 16;
|
||||
}
|
||||
|
||||
RelExpr AArch64TargetInfo::getRelExpr(uint32_t Type,
|
||||
const SymbolBody &S) const {
|
||||
RelExpr AArch64TargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
|
|
@ -1289,6 +1352,8 @@ RelExpr AArch64TargetInfo::getRelExpr(uint32_t Type,
|
|||
case R_AARCH64_ADR_GOT_PAGE:
|
||||
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
|
||||
return R_GOT_PAGE_PC;
|
||||
case R_AARCH64_NONE:
|
||||
return R_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1361,7 +1426,7 @@ void AArch64TargetInfo::writePltHeader(uint8_t *Buf) const {
|
|||
relocateOne(Buf + 12, R_AARCH64_ADD_ABS_LO12_NC, Got + 16);
|
||||
}
|
||||
|
||||
void AArch64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
||||
void AArch64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
const uint8_t Inst[] = {
|
||||
|
|
@ -1373,9 +1438,9 @@ void AArch64TargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
|||
memcpy(Buf, Inst, sizeof(Inst));
|
||||
|
||||
relocateOne(Buf, R_AARCH64_ADR_PREL_PG_HI21,
|
||||
getAArch64Page(GotEntryAddr) - getAArch64Page(PltEntryAddr));
|
||||
relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotEntryAddr);
|
||||
relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotEntryAddr);
|
||||
getAArch64Page(GotPltEntryAddr) - getAArch64Page(PltEntryAddr));
|
||||
relocateOne(Buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, GotPltEntryAddr);
|
||||
relocateOne(Buf + 8, R_AARCH64_ADD_ABS_LO12_NC, GotPltEntryAddr);
|
||||
}
|
||||
|
||||
static void write32AArch64Addr(uint8_t *L, uint64_t Imm) {
|
||||
|
|
@ -1598,7 +1663,8 @@ void AMDGPUTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
|
|||
}
|
||||
}
|
||||
|
||||
RelExpr AMDGPUTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
||||
RelExpr AMDGPUTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
case R_AMDGPU_ABS32:
|
||||
case R_AMDGPU_ABS64:
|
||||
|
|
@ -1612,7 +1678,8 @@ RelExpr AMDGPUTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
|||
case R_AMDGPU_GOTPCREL32_HI:
|
||||
return R_GOT_PC;
|
||||
default:
|
||||
fatal("do not know how to handle relocation " + Twine(Type));
|
||||
error(toString(S.File) + ": unknown relocation type: " + toString(Type));
|
||||
return R_HINT;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1634,7 +1701,8 @@ ARMTargetInfo::ARMTargetInfo() {
|
|||
NeedsThunks = true;
|
||||
}
|
||||
|
||||
RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
||||
RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return R_ABS;
|
||||
|
|
@ -1683,7 +1751,7 @@ RelExpr ARMTargetInfo::getRelExpr(uint32_t Type, const SymbolBody &S) const {
|
|||
case R_ARM_THM_MOVT_PREL:
|
||||
return R_PC;
|
||||
case R_ARM_NONE:
|
||||
return R_HINT;
|
||||
return R_NONE;
|
||||
case R_ARM_TLS_LE32:
|
||||
return R_TLS;
|
||||
}
|
||||
|
|
@ -1709,7 +1777,7 @@ void ARMTargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
|
|||
|
||||
void ARMTargetInfo::writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const {
|
||||
// An ARM entry is the address of the ifunc resolver function.
|
||||
write32le(Buf, S.getVA<ELF32LE>());
|
||||
write32le(Buf, S.getVA());
|
||||
}
|
||||
|
||||
void ARMTargetInfo::writePltHeader(uint8_t *Buf) const {
|
||||
|
|
@ -1726,7 +1794,13 @@ void ARMTargetInfo::writePltHeader(uint8_t *Buf) const {
|
|||
write32le(Buf + 16, GotPlt - L1 - 8);
|
||||
}
|
||||
|
||||
void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
||||
void ARMTargetInfo::addPltHeaderSymbols(InputSectionBase *ISD) const {
|
||||
auto *IS = cast<InputSection>(ISD);
|
||||
addSyntheticLocal<ELF32LE>("$a", STT_NOTYPE, 0, 0, IS);
|
||||
addSyntheticLocal<ELF32LE>("$d", STT_NOTYPE, 16, 0, IS);
|
||||
}
|
||||
|
||||
void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
// FIXME: Using simple code sequence with simple relocations.
|
||||
|
|
@ -1740,18 +1814,24 @@ void ARMTargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
|||
};
|
||||
memcpy(Buf, PltData, sizeof(PltData));
|
||||
uint64_t L1 = PltEntryAddr + 4;
|
||||
write32le(Buf + 12, GotEntryAddr - L1 - 8);
|
||||
write32le(Buf + 12, GotPltEntryAddr - L1 - 8);
|
||||
}
|
||||
|
||||
RelExpr ARMTargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile &File,
|
||||
const SymbolBody &S) const {
|
||||
void ARMTargetInfo::addPltSymbols(InputSectionBase *ISD, uint64_t Off) const {
|
||||
auto *IS = cast<InputSection>(ISD);
|
||||
addSyntheticLocal<ELF32LE>("$a", STT_NOTYPE, Off, 0, IS);
|
||||
addSyntheticLocal<ELF32LE>("$d", STT_NOTYPE, Off + 12, 0, IS);
|
||||
}
|
||||
|
||||
bool ARMTargetInfo::needsThunk(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile *File,
|
||||
const SymbolBody &S) const {
|
||||
// If S is an undefined weak symbol in an executable we don't need a Thunk.
|
||||
// In a DSO calls to undefined symbols, including weak ones get PLT entries
|
||||
// which may need a thunk.
|
||||
if (S.isUndefined() && !S.isLocal() && S.symbol()->isWeak()
|
||||
&& !Config->Shared)
|
||||
return Expr;
|
||||
if (S.isUndefined() && !S.isLocal() && S.symbol()->isWeak() &&
|
||||
!Config->Shared)
|
||||
return false;
|
||||
// A state change from ARM to Thumb and vice versa must go through an
|
||||
// interworking thunk if the relocation type is not R_ARM_CALL or
|
||||
// R_ARM_THM_CALL.
|
||||
|
|
@ -1761,20 +1841,18 @@ RelExpr ARMTargetInfo::getThunkExpr(RelExpr Expr, uint32_t RelocType,
|
|||
case R_ARM_JUMP24:
|
||||
// Source is ARM, all PLT entries are ARM so no interworking required.
|
||||
// Otherwise we need to interwork if Symbol has bit 0 set (Thumb).
|
||||
if (Expr == R_PC && ((S.getVA<ELF32LE>() & 1) == 1))
|
||||
return R_THUNK_PC;
|
||||
if (Expr == R_PC && ((S.getVA() & 1) == 1))
|
||||
return true;
|
||||
break;
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_THM_JUMP24:
|
||||
// Source is Thumb, all PLT entries are ARM so interworking is required.
|
||||
// Otherwise we need to interwork if Symbol has bit 0 clear (ARM).
|
||||
if (Expr == R_PLT_PC)
|
||||
return R_THUNK_PLT_PC;
|
||||
if ((S.getVA<ELF32LE>() & 1) == 0)
|
||||
return R_THUNK_PC;
|
||||
if (Expr == R_PLT_PC || ((S.getVA() & 1) == 0))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
return Expr;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
|
||||
|
|
@ -1796,6 +1874,7 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
|
|||
case R_ARM_TLS_LDO32:
|
||||
case R_ARM_TLS_LE32:
|
||||
case R_ARM_TLS_TPOFF32:
|
||||
case R_ARM_TLS_DTPOFF32:
|
||||
write32le(Loc, Val);
|
||||
break;
|
||||
case R_ARM_TLS_DTPMOD32:
|
||||
|
|
@ -1911,8 +1990,8 @@ void ARMTargetInfo::relocateOne(uint8_t *Loc, uint32_t Type,
|
|||
}
|
||||
}
|
||||
|
||||
uint64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf,
|
||||
uint32_t Type) const {
|
||||
int64_t ARMTargetInfo::getImplicitAddend(const uint8_t *Buf,
|
||||
uint32_t Type) const {
|
||||
switch (Type) {
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -1990,10 +2069,6 @@ bool ARMTargetInfo::isTlsLocalDynamicRel(uint32_t Type) const {
|
|||
return Type == R_ARM_TLS_LDO32 || Type == R_ARM_TLS_LDM32;
|
||||
}
|
||||
|
||||
bool ARMTargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const {
|
||||
return Type == R_ARM_TLS_GD32;
|
||||
}
|
||||
|
||||
bool ARMTargetInfo::isTlsInitialExecRel(uint32_t Type) const {
|
||||
return Type == R_ARM_TLS_IE32;
|
||||
}
|
||||
|
|
@ -2022,8 +2097,8 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
|
|||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
|
||||
const SymbolBody &S) const {
|
||||
RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const {
|
||||
// See comment in the calculateMipsRelChain.
|
||||
if (ELFT::Is64Bits || Config->MipsN32Abi)
|
||||
Type &= 0xff;
|
||||
|
|
@ -2039,13 +2114,16 @@ RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
|
|||
return R_PLT;
|
||||
case R_MIPS_HI16:
|
||||
case R_MIPS_LO16:
|
||||
case R_MIPS_GOT_OFST:
|
||||
// R_MIPS_HI16/R_MIPS_LO16 relocations against _gp_disp calculate
|
||||
// offset between start of function and 'gp' value which by default
|
||||
// equal to the start of .got section. In that case we consider these
|
||||
// relocations as relative.
|
||||
if (&S == ElfSym<ELFT>::MipsGpDisp)
|
||||
return R_PC;
|
||||
if (&S == ElfSym::MipsGpDisp)
|
||||
return R_MIPS_GOT_GP_PC;
|
||||
if (&S == ElfSym::MipsLocalGp)
|
||||
return R_MIPS_GOT_GP;
|
||||
// fallthrough
|
||||
case R_MIPS_GOT_OFST:
|
||||
return R_ABS;
|
||||
case R_MIPS_PC32:
|
||||
case R_MIPS_PC16:
|
||||
|
|
@ -2091,11 +2169,6 @@ bool MipsTargetInfo<ELFT>::isTlsLocalDynamicRel(uint32_t Type) const {
|
|||
return Type == R_MIPS_TLS_LDM;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
bool MipsTargetInfo<ELFT>::isTlsGlobalDynamicRel(uint32_t Type) const {
|
||||
return Type == R_MIPS_TLS_GD;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MipsTargetInfo<ELFT>::writeGotPlt(uint8_t *Buf, const SymbolBody &) const {
|
||||
write32<ELFT::TargetEndianness>(Buf, In<ELFT>::Plt->getVA());
|
||||
|
|
@ -2161,18 +2234,20 @@ void MipsTargetInfo<ELFT>::writePltHeader(uint8_t *Buf) const {
|
|||
write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
||||
write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
|
||||
}
|
||||
|
||||
write32<E>(Buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
write32<E>(Buf + 24, 0x0320f809); // jalr $25
|
||||
write32<E>(Buf + 28, 0x2718fffe); // subu $24, $24, 2
|
||||
uint64_t Got = In<ELFT>::GotPlt->getVA();
|
||||
writeMipsHi16<E>(Buf, Got);
|
||||
writeMipsLo16<E>(Buf + 4, Got);
|
||||
writeMipsLo16<E>(Buf + 8, Got);
|
||||
|
||||
uint64_t GotPlt = In<ELFT>::GotPlt->getVA();
|
||||
writeMipsHi16<E>(Buf, GotPlt);
|
||||
writeMipsLo16<E>(Buf + 4, GotPlt);
|
||||
writeMipsLo16<E>(Buf + 8, GotPlt);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
||||
void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
|
|
@ -2181,37 +2256,37 @@ void MipsTargetInfo<ELFT>::writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
|||
// jr $25
|
||||
write32<E>(Buf + 8, isMipsR6<ELFT>() ? 0x03200009 : 0x03200008);
|
||||
write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
|
||||
writeMipsHi16<E>(Buf, GotEntryAddr);
|
||||
writeMipsLo16<E>(Buf + 4, GotEntryAddr);
|
||||
writeMipsLo16<E>(Buf + 12, GotEntryAddr);
|
||||
writeMipsHi16<E>(Buf, GotPltEntryAddr);
|
||||
writeMipsLo16<E>(Buf + 4, GotPltEntryAddr);
|
||||
writeMipsLo16<E>(Buf + 12, GotPltEntryAddr);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
RelExpr MipsTargetInfo<ELFT>::getThunkExpr(RelExpr Expr, uint32_t Type,
|
||||
const InputFile &File,
|
||||
const SymbolBody &S) const {
|
||||
bool MipsTargetInfo<ELFT>::needsThunk(RelExpr Expr, uint32_t Type,
|
||||
const InputFile *File,
|
||||
const SymbolBody &S) const {
|
||||
// Any MIPS PIC code function is invoked with its address in register $t9.
|
||||
// So if we have a branch instruction from non-PIC code to the PIC one
|
||||
// we cannot make the jump directly and need to create a small stubs
|
||||
// to save the target function address.
|
||||
// See page 3-38 ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf
|
||||
if (Type != R_MIPS_26)
|
||||
return Expr;
|
||||
auto *F = dyn_cast<ELFFileBase<ELFT>>(&File);
|
||||
return false;
|
||||
auto *F = dyn_cast_or_null<ELFFileBase<ELFT>>(File);
|
||||
if (!F)
|
||||
return Expr;
|
||||
return false;
|
||||
// If current file has PIC code, LA25 stub is not required.
|
||||
if (F->getObj().getHeader()->e_flags & EF_MIPS_PIC)
|
||||
return Expr;
|
||||
auto *D = dyn_cast<DefinedRegular<ELFT>>(&S);
|
||||
return false;
|
||||
auto *D = dyn_cast<DefinedRegular>(&S);
|
||||
// LA25 is required if target file has PIC code
|
||||
// or target symbol is a PIC symbol.
|
||||
return D && D->isMipsPIC() ? R_THUNK_ABS : Expr;
|
||||
return D && D->isMipsPIC<ELFT>();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
|
||||
uint32_t Type) const {
|
||||
int64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
|
||||
uint32_t Type) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
switch (Type) {
|
||||
default:
|
||||
|
|
@ -2220,7 +2295,7 @@ uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
|
|||
case R_MIPS_GPREL32:
|
||||
case R_MIPS_TLS_DTPREL32:
|
||||
case R_MIPS_TLS_TPREL32:
|
||||
return read32<E>(Buf);
|
||||
return SignExtend64<32>(read32<E>(Buf));
|
||||
case R_MIPS_26:
|
||||
// FIXME (simon): If the relocation target symbol is not a PLT entry
|
||||
// we should use another expression for calculation:
|
||||
|
|
@ -2303,9 +2378,19 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
|
|||
case R_MIPS_26:
|
||||
write32<E>(Loc, (read32<E>(Loc) & ~0x3ffffff) | ((Val >> 2) & 0x3ffffff));
|
||||
break;
|
||||
case R_MIPS_GOT16:
|
||||
// The R_MIPS_GOT16 relocation's value in "relocatable" linking mode
|
||||
// is updated addend (not a GOT index). In that case write high 16 bits
|
||||
// to store a correct addend value.
|
||||
if (Config->Relocatable)
|
||||
writeMipsHi16<E>(Loc, Val);
|
||||
else {
|
||||
checkInt<16>(Loc, Val, Type);
|
||||
writeMipsLo16<E>(Loc, Val);
|
||||
}
|
||||
break;
|
||||
case R_MIPS_GOT_DISP:
|
||||
case R_MIPS_GOT_PAGE:
|
||||
case R_MIPS_GOT16:
|
||||
case R_MIPS_GPREL16:
|
||||
case R_MIPS_TLS_GD:
|
||||
case R_MIPS_TLS_LDM:
|
||||
|
|
|
|||
24
ELF/Target.h
24
ELF/Target.h
|
|
@ -25,13 +25,12 @@ class TargetInfo {
|
|||
public:
|
||||
virtual bool isTlsInitialExecRel(uint32_t Type) const;
|
||||
virtual bool isTlsLocalDynamicRel(uint32_t Type) const;
|
||||
virtual bool isTlsGlobalDynamicRel(uint32_t Type) const;
|
||||
virtual bool isPicRel(uint32_t Type) const { return true; }
|
||||
virtual uint32_t getDynRel(uint32_t Type) const { return Type; }
|
||||
virtual void writeGotPltHeader(uint8_t *Buf) const {}
|
||||
virtual void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const {};
|
||||
virtual void writeIgotPlt(uint8_t *Buf, const SymbolBody &S) const;
|
||||
virtual uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const;
|
||||
virtual int64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const;
|
||||
|
||||
// If lazy binding is supported, the first entry of the PLT has code
|
||||
// to call the dynamic linker to resolve PLT entries the first time
|
||||
|
|
@ -41,7 +40,8 @@ public:
|
|||
virtual void writePlt(uint8_t *Buf, uint64_t GotEntryAddr,
|
||||
uint64_t PltEntryAddr, int32_t Index,
|
||||
unsigned RelOff) const {}
|
||||
|
||||
virtual void addPltHeaderSymbols(InputSectionBase *IS) const {}
|
||||
virtual void addPltSymbols(InputSectionBase *IS, uint64_t Off) const {}
|
||||
// Returns true if a relocation only uses the low bits of a value such that
|
||||
// all those bits are in in the same page. For example, if the relocation
|
||||
// only uses the low 12 bits in a system with 4k pages. If this is true, the
|
||||
|
|
@ -50,15 +50,11 @@ public:
|
|||
virtual bool usesOnlyLowPageBits(uint32_t Type) const;
|
||||
|
||||
// Decide whether a Thunk is needed for the relocation from File
|
||||
// targeting S. Returns one of:
|
||||
// Expr if there is no Thunk required
|
||||
// R_THUNK_ABS if thunk is required and expression is absolute
|
||||
// R_THUNK_PC if thunk is required and expression is pc rel
|
||||
// R_THUNK_PLT_PC if thunk is required to PLT entry and expression is pc rel
|
||||
virtual RelExpr getThunkExpr(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile &File,
|
||||
const SymbolBody &S) const;
|
||||
virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const = 0;
|
||||
// targeting S.
|
||||
virtual bool needsThunk(RelExpr Expr, uint32_t RelocType,
|
||||
const InputFile *File, const SymbolBody &S) const;
|
||||
virtual RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
|
||||
const uint8_t *Loc) const = 0;
|
||||
virtual void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const = 0;
|
||||
virtual ~TargetInfo();
|
||||
|
||||
|
|
@ -95,6 +91,10 @@ public:
|
|||
|
||||
bool NeedsThunks = false;
|
||||
|
||||
// A 4-byte field corresponding to one or more trap instructions, used to pad
|
||||
// executable OutputSections.
|
||||
uint32_t TrapInstr = 0;
|
||||
|
||||
virtual RelExpr adjustRelaxExpr(uint32_t Type, const uint8_t *Data,
|
||||
RelExpr Expr) const;
|
||||
virtual void relaxGot(uint8_t *Loc, uint64_t Val) const;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
//
|
||||
// That said, we don't want to do "too clever" things using threads.
|
||||
// Complex multi-threaded algorithms are sometimes extremely hard to
|
||||
// justify the correctness and can easily mess up the entire design.
|
||||
// reason about and can easily mess up the entire design.
|
||||
//
|
||||
// Fortunately, when a linker links large programs (when the link time is
|
||||
// most critical), it spends most of the time to work on massive number of
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
// instead of std::for_each (or a plain for loop). Because tasks are
|
||||
// completely independent from each other, we can run them in parallel
|
||||
// without any coordination between them. That's very easy to understand
|
||||
// and justify.
|
||||
// and reason about.
|
||||
//
|
||||
// For the cases such as the latter, we can use parallel algorithms to
|
||||
// deal with massive data. We have to write code for a tailored algorithm
|
||||
|
|
@ -69,14 +69,15 @@ namespace lld {
|
|||
namespace elf {
|
||||
|
||||
template <class IterTy, class FuncTy>
|
||||
void forEach(IterTy Begin, IterTy End, FuncTy Fn) {
|
||||
void parallelForEach(IterTy Begin, IterTy End, FuncTy Fn) {
|
||||
if (Config->Threads)
|
||||
parallel_for_each(Begin, End, Fn);
|
||||
else
|
||||
std::for_each(Begin, End, Fn);
|
||||
}
|
||||
|
||||
inline void forLoop(size_t Begin, size_t End, std::function<void(size_t)> Fn) {
|
||||
inline void parallelFor(size_t Begin, size_t End,
|
||||
std::function<void(size_t)> Fn) {
|
||||
if (Config->Threads) {
|
||||
parallel_for(Begin, End, Fn);
|
||||
} else {
|
||||
|
|
|
|||
210
ELF/Thunks.cpp
210
ELF/Thunks.cpp
|
|
@ -28,6 +28,7 @@
|
|||
#include "Memory.h"
|
||||
#include "OutputSections.h"
|
||||
#include "Symbols.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/ELF.h"
|
||||
|
|
@ -49,127 +50,159 @@ namespace {
|
|||
|
||||
// Specific ARM Thunk implementations. The naming convention is:
|
||||
// Source State, TargetState, Target Requirement, ABS or PI, Range
|
||||
template <class ELFT>
|
||||
class ARMToThumbV7ABSLongThunk final : public Thunk<ELFT> {
|
||||
template <class ELFT> class ARMV7ABSLongThunk final : public Thunk {
|
||||
public:
|
||||
ARMToThumbV7ABSLongThunk(const SymbolBody &Dest,
|
||||
const InputSection<ELFT> &Owner)
|
||||
: Thunk<ELFT>(Dest, Owner) {}
|
||||
ARMV7ABSLongThunk(const SymbolBody &Dest) : Thunk(Dest) {}
|
||||
|
||||
uint32_t size() const override { return 12; }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
};
|
||||
|
||||
template <class ELFT> class ARMToThumbV7PILongThunk final : public Thunk<ELFT> {
|
||||
template <class ELFT> class ARMV7PILongThunk final : public Thunk {
|
||||
public:
|
||||
ARMToThumbV7PILongThunk(const SymbolBody &Dest,
|
||||
const InputSection<ELFT> &Owner)
|
||||
: Thunk<ELFT>(Dest, Owner) {}
|
||||
ARMV7PILongThunk(const SymbolBody &Dest) : Thunk(Dest) {}
|
||||
|
||||
uint32_t size() const override { return 16; }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
};
|
||||
|
||||
template <class ELFT>
|
||||
class ThumbToARMV7ABSLongThunk final : public Thunk<ELFT> {
|
||||
template <class ELFT> class ThumbV7ABSLongThunk final : public Thunk {
|
||||
public:
|
||||
ThumbToARMV7ABSLongThunk(const SymbolBody &Dest,
|
||||
const InputSection<ELFT> &Owner)
|
||||
: Thunk<ELFT>(Dest, Owner) {}
|
||||
ThumbV7ABSLongThunk(const SymbolBody &Dest) : Thunk(Dest) {
|
||||
this->alignment = 2;
|
||||
}
|
||||
|
||||
uint32_t size() const override { return 10; }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
};
|
||||
|
||||
template <class ELFT> class ThumbToARMV7PILongThunk final : public Thunk<ELFT> {
|
||||
template <class ELFT> class ThumbV7PILongThunk final : public Thunk {
|
||||
public:
|
||||
ThumbToARMV7PILongThunk(const SymbolBody &Dest,
|
||||
const InputSection<ELFT> &Owner)
|
||||
: Thunk<ELFT>(Dest, Owner) {}
|
||||
ThumbV7PILongThunk(const SymbolBody &Dest) : Thunk(Dest) {
|
||||
this->alignment = 2;
|
||||
}
|
||||
|
||||
uint32_t size() const override { return 12; }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
};
|
||||
|
||||
// MIPS LA25 thunk
|
||||
template <class ELFT> class MipsThunk final : public Thunk<ELFT> {
|
||||
template <class ELFT> class MipsThunk final : public Thunk {
|
||||
public:
|
||||
MipsThunk(const SymbolBody &Dest, const InputSection<ELFT> &Owner)
|
||||
: Thunk<ELFT>(Dest, Owner) {}
|
||||
MipsThunk(const SymbolBody &Dest) : Thunk(Dest) {}
|
||||
|
||||
uint32_t size() const override { return 16; }
|
||||
void writeTo(uint8_t *Buf) const override;
|
||||
void writeTo(uint8_t *Buf, ThunkSection &IS) const override;
|
||||
void addSymbols(ThunkSection &IS) override;
|
||||
InputSection *getTargetInputSection() const override;
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
// ARM Target Thunks
|
||||
template <class ELFT> static uint64_t getARMThunkDestVA(const SymbolBody &S) {
|
||||
uint64_t V = S.isInPlt() ? S.getPltVA<ELFT>() : S.getVA<ELFT>();
|
||||
static uint64_t getARMThunkDestVA(const SymbolBody &S) {
|
||||
uint64_t V = S.isInPlt() ? S.getPltVA() : S.getVA();
|
||||
return SignExtend64<32>(V);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ARMToThumbV7ABSLongThunk<ELFT>::writeTo(uint8_t *Buf) const {
|
||||
void ARMV7ABSLongThunk<ELFT>::writeTo(uint8_t *Buf, ThunkSection &IS) const {
|
||||
const uint8_t Data[] = {
|
||||
0x00, 0xc0, 0x00, 0xe3, // movw ip,:lower16:S
|
||||
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S
|
||||
0x1c, 0xff, 0x2f, 0xe1, // bx ip
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA<ELFT>(this->Destination);
|
||||
uint64_t S = getARMThunkDestVA(this->Destination);
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_MOVW_ABS_NC, S);
|
||||
Target->relocateOne(Buf + 4, R_ARM_MOVT_ABS, S);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ThumbToARMV7ABSLongThunk<ELFT>::writeTo(uint8_t *Buf) const {
|
||||
void ARMV7ABSLongThunk<ELFT>::addSymbols(ThunkSection &IS) {
|
||||
this->ThunkSym = addSyntheticLocal<ELFT>(
|
||||
Saver.save("__ARMv7ABSLongThunk_" + this->Destination.getName()),
|
||||
STT_FUNC, this->Offset, size(), &IS);
|
||||
addSyntheticLocal<ELFT>("$a", STT_NOTYPE, this->Offset, 0, &IS);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ThumbV7ABSLongThunk<ELFT>::writeTo(uint8_t *Buf, ThunkSection &IS) const {
|
||||
const uint8_t Data[] = {
|
||||
0x40, 0xf2, 0x00, 0x0c, // movw ip, :lower16:S
|
||||
0xc0, 0xf2, 0x00, 0x0c, // movt ip, :upper16:S
|
||||
0x60, 0x47, // bx ip
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA<ELFT>(this->Destination);
|
||||
uint64_t S = getARMThunkDestVA(this->Destination);
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_THM_MOVW_ABS_NC, S);
|
||||
Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_ABS, S);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ARMToThumbV7PILongThunk<ELFT>::writeTo(uint8_t *Buf) const {
|
||||
void ThumbV7ABSLongThunk<ELFT>::addSymbols(ThunkSection &IS) {
|
||||
this->ThunkSym = addSyntheticLocal<ELFT>(
|
||||
Saver.save("__Thumbv7ABSLongThunk_" + this->Destination.getName()),
|
||||
STT_FUNC, this->Offset, size(), &IS);
|
||||
addSyntheticLocal<ELFT>("$t", STT_NOTYPE, this->Offset, 0, &IS);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ARMV7PILongThunk<ELFT>::writeTo(uint8_t *Buf, ThunkSection &IS) const {
|
||||
const uint8_t Data[] = {
|
||||
0xf0, 0xcf, 0x0f, 0xe3, // P: movw ip,:lower16:S - (P + (L1-P) +8)
|
||||
0x00, 0xc0, 0x40, 0xe3, // movt ip,:upper16:S - (P + (L1-P+4) +8)
|
||||
0x0f, 0xc0, 0x8c, 0xe0, // L1: add ip, ip, pc
|
||||
0x1c, 0xff, 0x2f, 0xe1, // bx r12
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA<ELFT>(this->Destination);
|
||||
uint64_t P = this->getVA();
|
||||
uint64_t S = getARMThunkDestVA(this->Destination);
|
||||
uint64_t P = this->ThunkSym->getVA();
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_MOVW_PREL_NC, S - P - 16);
|
||||
Target->relocateOne(Buf + 4, R_ARM_MOVT_PREL, S - P - 12);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ThumbToARMV7PILongThunk<ELFT>::writeTo(uint8_t *Buf) const {
|
||||
void ARMV7PILongThunk<ELFT>::addSymbols(ThunkSection &IS) {
|
||||
this->ThunkSym = addSyntheticLocal<ELFT>(
|
||||
Saver.save("__ARMV7PILongThunk_" + this->Destination.getName()), STT_FUNC,
|
||||
this->Offset, size(), &IS);
|
||||
addSyntheticLocal<ELFT>("$a", STT_NOTYPE, this->Offset, 0, &IS);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ThumbV7PILongThunk<ELFT>::writeTo(uint8_t *Buf, ThunkSection &IS) const {
|
||||
const uint8_t Data[] = {
|
||||
0x4f, 0xf6, 0xf4, 0x7c, // P: movw ip,:lower16:S - (P + (L1-P) + 4)
|
||||
0xc0, 0xf2, 0x00, 0x0c, // movt ip,:upper16:S - (P + (L1-P+4) + 4)
|
||||
0xfc, 0x44, // L1: add r12, pc
|
||||
0x60, 0x47, // bx r12
|
||||
};
|
||||
uint64_t S = getARMThunkDestVA<ELFT>(this->Destination);
|
||||
uint64_t P = this->getVA();
|
||||
uint64_t S = getARMThunkDestVA(this->Destination);
|
||||
uint64_t P = this->ThunkSym->getVA();
|
||||
memcpy(Buf, Data, sizeof(Data));
|
||||
Target->relocateOne(Buf, R_ARM_THM_MOVW_PREL_NC, S - P - 12);
|
||||
Target->relocateOne(Buf + 4, R_ARM_THM_MOVT_PREL, S - P - 8);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void ThumbV7PILongThunk<ELFT>::addSymbols(ThunkSection &IS) {
|
||||
this->ThunkSym = addSyntheticLocal<ELFT>(
|
||||
Saver.save("__ThumbV7PILongThunk_" + this->Destination.getName()),
|
||||
STT_FUNC, this->Offset, size(), &IS);
|
||||
addSyntheticLocal<ELFT>("$t", STT_NOTYPE, this->Offset, 0, &IS);
|
||||
}
|
||||
|
||||
// Write MIPS LA25 thunk code to call PIC function from the non-PIC one.
|
||||
template <class ELFT> void MipsThunk<ELFT>::writeTo(uint8_t *Buf) const {
|
||||
template <class ELFT>
|
||||
void MipsThunk<ELFT>::writeTo(uint8_t *Buf, ThunkSection &) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
|
||||
uint64_t S = this->Destination.template getVA<ELFT>();
|
||||
uint64_t S = this->Destination.getVA();
|
||||
write32<E>(Buf, 0x3c190000); // lui $25, %hi(func)
|
||||
write32<E>(Buf + 4, 0x08000000 | (S >> 2)); // j func
|
||||
write32<E>(Buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
|
||||
|
|
@ -178,20 +211,24 @@ template <class ELFT> void MipsThunk<ELFT>::writeTo(uint8_t *Buf) const {
|
|||
Target->relocateOne(Buf + 8, R_MIPS_LO16, S);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
Thunk<ELFT>::Thunk(const SymbolBody &D, const InputSection<ELFT> &O)
|
||||
: Destination(D), Owner(O), Offset(O.getThunkOff() + O.getThunksSize()) {}
|
||||
|
||||
template <class ELFT> typename ELFT::uint Thunk<ELFT>::getVA() const {
|
||||
return Owner.OutSec->Addr + Owner.OutSecOff + Offset;
|
||||
template <class ELFT> void MipsThunk<ELFT>::addSymbols(ThunkSection &IS) {
|
||||
this->ThunkSym = addSyntheticLocal<ELFT>(
|
||||
Saver.save("__LA25Thunk_" + this->Destination.getName()), STT_FUNC,
|
||||
this->Offset, size(), &IS);
|
||||
}
|
||||
|
||||
template <class ELFT> Thunk<ELFT>::~Thunk() = default;
|
||||
template <class ELFT>
|
||||
InputSection *MipsThunk<ELFT>::getTargetInputSection() const {
|
||||
auto *DR = dyn_cast<DefinedRegular>(&this->Destination);
|
||||
return dyn_cast<InputSection>(DR->Section);
|
||||
}
|
||||
|
||||
Thunk::Thunk(const SymbolBody &D) : Destination(D), Offset(0) {}
|
||||
|
||||
Thunk::~Thunk() = default;
|
||||
|
||||
// Creates a thunk for Thumb-ARM interworking.
|
||||
template <class ELFT>
|
||||
static Thunk<ELFT> *createThunkArm(uint32_t Reloc, SymbolBody &S,
|
||||
InputSection<ELFT> &IS) {
|
||||
template <class ELFT> static Thunk *addThunkArm(uint32_t Reloc, SymbolBody &S) {
|
||||
// ARM relocations need ARM to Thumb interworking Thunks.
|
||||
// Thumb relocations need Thumb to ARM relocations.
|
||||
// Use position independent Thunks if we require position independent code.
|
||||
|
|
@ -200,76 +237,33 @@ static Thunk<ELFT> *createThunkArm(uint32_t Reloc, SymbolBody &S,
|
|||
case R_ARM_PLT32:
|
||||
case R_ARM_JUMP24:
|
||||
if (Config->Pic)
|
||||
return new (BAlloc) ARMToThumbV7PILongThunk<ELFT>(S, IS);
|
||||
return new (BAlloc) ARMToThumbV7ABSLongThunk<ELFT>(S, IS);
|
||||
return make<ARMV7PILongThunk<ELFT>>(S);
|
||||
return make<ARMV7ABSLongThunk<ELFT>>(S);
|
||||
case R_ARM_THM_JUMP19:
|
||||
case R_ARM_THM_JUMP24:
|
||||
if (Config->Pic)
|
||||
return new (BAlloc) ThumbToARMV7PILongThunk<ELFT>(S, IS);
|
||||
return new (BAlloc) ThumbToARMV7ABSLongThunk<ELFT>(S, IS);
|
||||
return make<ThumbV7PILongThunk<ELFT>>(S);
|
||||
return make<ThumbV7ABSLongThunk<ELFT>>(S);
|
||||
}
|
||||
fatal("unrecognized relocation type");
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void addThunkARM(uint32_t Reloc, SymbolBody &S, InputSection<ELFT> &IS) {
|
||||
// Only one Thunk supported per symbol.
|
||||
if (S.hasThunk<ELFT>())
|
||||
return;
|
||||
|
||||
// ARM Thunks are added to the same InputSection as the relocation. This
|
||||
// isn't strictly necessary but it makes it more likely that a limited range
|
||||
// branch can reach the Thunk, and it makes Thunks to the PLT section easier
|
||||
Thunk<ELFT> *T = createThunkArm(Reloc, S, IS);
|
||||
IS.addThunk(T);
|
||||
if (auto *Sym = dyn_cast<DefinedRegular<ELFT>>(&S))
|
||||
Sym->ThunkData = T;
|
||||
else if (auto *Sym = dyn_cast<SharedSymbol<ELFT>>(&S))
|
||||
Sym->ThunkData = T;
|
||||
else if (auto *Sym = dyn_cast<Undefined<ELFT>>(&S))
|
||||
Sym->ThunkData = T;
|
||||
else
|
||||
fatal("symbol not DefinedRegular or Shared");
|
||||
template <class ELFT> static Thunk *addThunkMips(SymbolBody &S) {
|
||||
return make<MipsThunk<ELFT>>(S);
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static void addThunkMips(uint32_t RelocType, SymbolBody &S,
|
||||
InputSection<ELFT> &IS) {
|
||||
// Only one Thunk supported per symbol.
|
||||
if (S.hasThunk<ELFT>())
|
||||
return;
|
||||
|
||||
// Mips Thunks are added to the InputSection defining S.
|
||||
auto *R = cast<DefinedRegular<ELFT>>(&S);
|
||||
auto *Sec = cast<InputSection<ELFT>>(R->Section);
|
||||
auto *T = new (BAlloc) MipsThunk<ELFT>(S, *Sec);
|
||||
Sec->addThunk(T);
|
||||
R->ThunkData = T;
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
void addThunk(uint32_t RelocType, SymbolBody &S, InputSection<ELFT> &IS) {
|
||||
template <class ELFT> Thunk *addThunk(uint32_t RelocType, SymbolBody &S) {
|
||||
if (Config->EMachine == EM_ARM)
|
||||
addThunkARM<ELFT>(RelocType, S, IS);
|
||||
return addThunkArm<ELFT>(RelocType, S);
|
||||
else if (Config->EMachine == EM_MIPS)
|
||||
addThunkMips<ELFT>(RelocType, S, IS);
|
||||
else
|
||||
llvm_unreachable("add Thunk only supported for ARM and Mips");
|
||||
return addThunkMips<ELFT>(S);
|
||||
llvm_unreachable("add Thunk only supported for ARM and Mips");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template void addThunk<ELF32LE>(uint32_t, SymbolBody &,
|
||||
InputSection<ELF32LE> &);
|
||||
template void addThunk<ELF32BE>(uint32_t, SymbolBody &,
|
||||
InputSection<ELF32BE> &);
|
||||
template void addThunk<ELF64LE>(uint32_t, SymbolBody &,
|
||||
InputSection<ELF64LE> &);
|
||||
template void addThunk<ELF64BE>(uint32_t, SymbolBody &,
|
||||
InputSection<ELF64BE> &);
|
||||
|
||||
template class Thunk<ELF32LE>;
|
||||
template class Thunk<ELF32BE>;
|
||||
template class Thunk<ELF64LE>;
|
||||
template class Thunk<ELF64BE>;
|
||||
|
||||
template Thunk *addThunk<ELF32LE>(uint32_t, SymbolBody &);
|
||||
template Thunk *addThunk<ELF32BE>(uint32_t, SymbolBody &);
|
||||
template Thunk *addThunk<ELF64LE>(uint32_t, SymbolBody &);
|
||||
template Thunk *addThunk<ELF64BE>(uint32_t, SymbolBody &);
|
||||
} // end namespace elf
|
||||
} // end namespace lld
|
||||
|
|
|
|||
39
ELF/Thunks.h
39
ELF/Thunks.h
|
|
@ -15,8 +15,7 @@
|
|||
namespace lld {
|
||||
namespace elf {
|
||||
class SymbolBody;
|
||||
template <class ELFT> class InputSection;
|
||||
|
||||
class ThunkSection;
|
||||
// Class to describe an instance of a Thunk.
|
||||
// A Thunk is a code-sequence inserted by the linker in between a caller and
|
||||
// the callee. The relocation to the callee is redirected to the Thunk, which
|
||||
|
|
@ -24,31 +23,35 @@ template <class ELFT> class InputSection;
|
|||
// include transferring control from non-pi to pi and changing state on
|
||||
// targets like ARM.
|
||||
//
|
||||
// Thunks can be created for DefinedRegular and Shared Symbols. The Thunk
|
||||
// is stored in a field of the Symbol Destination.
|
||||
// Thunks to be written to an InputSection are recorded by the InputSection.
|
||||
template <class ELFT> class Thunk {
|
||||
typedef typename ELFT::uint uintX_t;
|
||||
|
||||
// Thunks can be created for DefinedRegular, Shared and Undefined Symbols.
|
||||
// Thunks are assigned to synthetic ThunkSections
|
||||
class Thunk {
|
||||
public:
|
||||
Thunk(const SymbolBody &Destination, const InputSection<ELFT> &Owner);
|
||||
Thunk(const SymbolBody &Destination);
|
||||
virtual ~Thunk();
|
||||
|
||||
virtual uint32_t size() const { return 0; }
|
||||
virtual void writeTo(uint8_t *Buf) const {}
|
||||
uintX_t getVA() const;
|
||||
virtual void writeTo(uint8_t *Buf, ThunkSection &IS) const {}
|
||||
|
||||
protected:
|
||||
// All Thunks must define at least one symbol ThunkSym so that we can
|
||||
// redirect relocations to it.
|
||||
virtual void addSymbols(ThunkSection &IS) {}
|
||||
|
||||
// Some Thunks must be placed immediately before their Target as they elide
|
||||
// a branch and fall through to the first Symbol in the Target.
|
||||
virtual InputSection *getTargetInputSection() const { return nullptr; }
|
||||
|
||||
// The alignment requirement for this Thunk, defaults to the size of the
|
||||
// typical code section alignment.
|
||||
const SymbolBody &Destination;
|
||||
const InputSection<ELFT> &Owner;
|
||||
SymbolBody *ThunkSym;
|
||||
uint64_t Offset;
|
||||
uint32_t alignment = 4;
|
||||
};
|
||||
|
||||
// For a Relocation to symbol S from InputSection Src, create a Thunk and
|
||||
// update the fields of S and the InputSection that the Thunk body will be
|
||||
// written to. At present there are implementations for ARM and Mips Thunks.
|
||||
template <class ELFT>
|
||||
void addThunk(uint32_t RelocType, SymbolBody &S, InputSection<ELFT> &Src);
|
||||
// For a Relocation to symbol S create a Thunk to be added to a synthetic
|
||||
// ThunkSection. At present there are implementations for ARM and Mips Thunks.
|
||||
template <class ELFT> Thunk *addThunk(uint32_t RelocType, SymbolBody &S);
|
||||
|
||||
} // namespace elf
|
||||
} // namespace lld
|
||||
|
|
|
|||
1202
ELF/Writer.cpp
1202
ELF/Writer.cpp
File diff suppressed because it is too large
Load diff
20
ELF/Writer.h
20
ELF/Writer.h
|
|
@ -18,41 +18,39 @@
|
|||
namespace lld {
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
class OutputSectionBase;
|
||||
template <class ELFT> class InputSectionBase;
|
||||
class OutputSection;
|
||||
class InputSectionBase;
|
||||
template <class ELFT> class ObjectFile;
|
||||
template <class ELFT> class SymbolTable;
|
||||
template <class ELFT> void writeResult();
|
||||
template <class ELFT> void markLive();
|
||||
template <class ELFT> bool isRelroSection(const OutputSectionBase *Sec);
|
||||
template <class ELFT> bool isRelroSection(const OutputSection *Sec);
|
||||
|
||||
// This describes a program header entry.
|
||||
// Each contains type, access flags and range of output sections that will be
|
||||
// placed in it.
|
||||
struct PhdrEntry {
|
||||
PhdrEntry(unsigned Type, unsigned Flags);
|
||||
void add(OutputSectionBase *Sec);
|
||||
void add(OutputSection *Sec);
|
||||
|
||||
uint64_t p_paddr = 0;
|
||||
uint64_t p_vaddr = 0;
|
||||
uint64_t p_align = 0;
|
||||
uint64_t p_memsz = 0;
|
||||
uint64_t p_filesz = 0;
|
||||
uint64_t p_offset = 0;
|
||||
uint32_t p_align = 0;
|
||||
uint32_t p_type = 0;
|
||||
uint32_t p_flags = 0;
|
||||
|
||||
OutputSectionBase *First = nullptr;
|
||||
OutputSectionBase *Last = nullptr;
|
||||
OutputSection *First = nullptr;
|
||||
OutputSection *Last = nullptr;
|
||||
bool HasLMA = false;
|
||||
};
|
||||
|
||||
llvm::StringRef getOutputSectionName(llvm::StringRef Name);
|
||||
|
||||
template <class ELFT>
|
||||
void allocateHeaders(llvm::MutableArrayRef<PhdrEntry>,
|
||||
llvm::ArrayRef<OutputSectionBase *>);
|
||||
template <class ELFT> void reportDiscarded(InputSectionBase<ELFT> *IS);
|
||||
bool allocateHeaders(std::vector<PhdrEntry> &, llvm::ArrayRef<OutputSection *>,
|
||||
uint64_t Min);
|
||||
|
||||
template <class ELFT> uint32_t getMipsEFlags();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
ATOM-based lld
|
||||
==============
|
||||
|
||||
Note: this document discuss Mach-O port of LLD. For ELF and COFF,
|
||||
see :doc:`index`.
|
||||
|
||||
ATOM-based lld is a new set of modular code for creating linker tools.
|
||||
Currently it supports Mach-O.
|
||||
|
||||
|
|
@ -46,9 +49,7 @@ Contents
|
|||
|
||||
design
|
||||
getting_started
|
||||
ReleaseNotes
|
||||
development
|
||||
windows_support
|
||||
open_projects
|
||||
sphinx_intro
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
C++11
|
||||
=====
|
||||
|
||||
Originally, LLD was developed in C++11 unlike the rest of LLVM. Now, all of
|
||||
LLVM, LLD, and Clang are developed using C++11. See the `LLVM Coding
|
||||
Standards`_ for details on the precise subset of C++11 supported by the various
|
||||
host compilers.
|
||||
|
||||
.. _LLVM Coding Standards: http://llvm.org/docs/CodingStandards.html
|
||||
|
|
@ -2,6 +2,9 @@
|
|||
Driver
|
||||
======
|
||||
|
||||
Note: this document discuss Mach-O port of LLD. For ELF and COFF,
|
||||
see :doc:`index`.
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,6 @@
|
|||
The ELF and COFF Linkers
|
||||
========================
|
||||
|
||||
We started rewriting the ELF (Unix) and COFF (Windows) linkers in May 2015.
|
||||
Since then, we have been making a steady progress towards providing
|
||||
drop-in replacements for the system linkers.
|
||||
|
||||
Currently, the Windows support is mostly complete and is about 2x faster
|
||||
than the linker that comes as a part of Micrsoft Visual Studio toolchain.
|
||||
|
||||
The ELF support is in progress and is able to link large programs
|
||||
such as Clang or LLD itself. Unless your program depends on linker scripts,
|
||||
you can expect it to be linkable with LLD.
|
||||
It is currently about 1.2x to 2x faster than GNU gold linker.
|
||||
We aim to make it a drop-in replacement for the GNU linker.
|
||||
|
||||
We expect that FreeBSD is going to be the first large system
|
||||
to adopt LLD as the system linker.
|
||||
We are working on it in collaboration with the FreeBSD project.
|
||||
|
||||
The linkers are notably small; as of October 2016,
|
||||
the COFF linker is about 7k lines and the ELF linker is about 18k lines,
|
||||
while gold is 165K lines.
|
||||
|
||||
The linkers are designed to be as fast and simple as possible.
|
||||
Because it is simple, it is easy to extend to support new features.
|
||||
It already supports several advanced features such section garbage
|
||||
collection and identical code folding.
|
||||
|
||||
The COFF linker supports i386, x86-64 and ARM. The ELF linker supports
|
||||
i386, x86-64, x32, MIPS32, MIPS64, PowerPC, AMDGPU, ARM and Aarch64,
|
||||
although the quality varies depending on platform. By default, LLD
|
||||
provides support for all targets because the amount of code we have for
|
||||
each target is so small. We do not even provide a way to disable
|
||||
targets at compile time.
|
||||
|
||||
There are a few key design choices that we made to achieve these goals.
|
||||
We will describe them in this document.
|
||||
|
||||
The ELF Linker as a Library
|
||||
---------------------------
|
||||
|
||||
|
|
@ -126,7 +90,7 @@ between speed, simplicity and extensibility.
|
|||
|
||||
Visiting the same archive files multiple makes the linker slower.
|
||||
|
||||
Here is how LLD approached the problem. Instead of memorizing only undefined symbols,
|
||||
Here is how LLD approaches the problem. Instead of memorizing only undefined symbols,
|
||||
we program LLD so that it memorizes all symbols.
|
||||
When it sees an undefined symbol that can be resolved by extracting an object file
|
||||
from an archive file it previously visited, it immediately extracts the file and link it.
|
||||
|
|
@ -169,7 +133,7 @@ it would slow down the linker by 10%. So, don't do that.
|
|||
On the other hand, you don't have to pursue efficiency
|
||||
when handling files.
|
||||
|
||||
Important Data Strcutures
|
||||
Important Data Structures
|
||||
-------------------------
|
||||
|
||||
We will describe the key data structures in LLD in this section.
|
||||
|
|
@ -216,7 +180,7 @@ Once you understand their functions, the code of the linker should look obvious
|
|||
* SymbolTable
|
||||
|
||||
SymbolTable is basically a hash table from strings to Symbols
|
||||
with a logic to resolve symbol conflicts. It resolves conflicts by symbol type.
|
||||
with logic to resolve symbol conflicts. It resolves conflicts by symbol type.
|
||||
|
||||
- If we add Defined and Undefined symbols, the symbol table will keep the former.
|
||||
- If we add Defined and Lazy symbols, it will keep the former.
|
||||
|
|
@ -269,11 +233,11 @@ There are mainly three actors in this linker.
|
|||
|
||||
* Driver
|
||||
|
||||
The linking process is driven by the driver. The driver
|
||||
The linking process is driven by the driver. The driver:
|
||||
|
||||
- processes command line options,
|
||||
- creates a symbol table,
|
||||
- creates an InputFile for each input file and put all symbols in it into the symbol table,
|
||||
- creates an InputFile for each input file and puts all symbols within into the symbol table,
|
||||
- checks if there's no remaining undefined symbols,
|
||||
- creates a writer,
|
||||
- and passes the symbol table to the writer to write the result to a file.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
Developing lld Readers
|
||||
======================
|
||||
|
||||
Note: this document discuss Mach-O port of LLD. For ELF and COFF,
|
||||
see :doc:`index`.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
|
|
|
|||
|
|
@ -1,101 +1,37 @@
|
|||
=======================
|
||||
LLD 4.0.0 Release Notes
|
||||
LLD 5.0.0 Release Notes
|
||||
=======================
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
.. warning::
|
||||
These are in-progress notes for the upcoming LLVM 5.0.0 release.
|
||||
Release notes for previous releases can be found on
|
||||
`the Download Page <http://releases.llvm.org/download.html>`_.
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
LLD is a linker which supports ELF (Unix), COFF (Windows) and Mach-O
|
||||
(macOS). It is generally faster than the GNU BFD/gold linkers or the
|
||||
MSVC linker.
|
||||
|
||||
LLD is designed to be a drop-in replacement for the system linkers, so
|
||||
that users don't need to change their build systems other than swapping
|
||||
the linker command.
|
||||
|
||||
This document contains the release notes for LLD 4.0.0.
|
||||
This document contains the release notes for the LLD linker, release 5.0.0.
|
||||
Here we describe the status of LLD, including major improvements
|
||||
from the previous release. All LLD releases may be downloaded
|
||||
from the `LLVM releases web site <http://llvm.org/releases/>`_.
|
||||
|
||||
|
||||
What's New in LLD 4.0?
|
||||
======================
|
||||
Non-comprehensive list of changes in this release
|
||||
=================================================
|
||||
|
||||
ELF Improvements
|
||||
----------------
|
||||
|
||||
LLD provides much better compatibility with the GNU linker than before.
|
||||
Now it is able to link the entire FreeBSD base system including the kernel
|
||||
out of the box. We are working closely with the FreeBSD project to
|
||||
make it usable as the system linker in a future release of the operating
|
||||
system.
|
||||
|
||||
Multi-threading performance has been improved, and multi-threading
|
||||
is now enabled by default. Combined with other optimizations, LLD 4.0
|
||||
is about 1.5 times faster than LLD 3.9 when linking large programs
|
||||
in our test environment.
|
||||
|
||||
Other notable changes are listed below:
|
||||
|
||||
* Error messages contain more information than before. If debug info
|
||||
is available, the linker prints out not only the object file name
|
||||
but the source location of unresolved symbols.
|
||||
|
||||
* Error messages are printed in red just like Clang by default. You
|
||||
can disable it by passing ``-no-color-diagnostics``.
|
||||
|
||||
* LLD's version string is now embedded in a .comment section in the
|
||||
result output file. You can dump it with this command: ``objdump -j -s
|
||||
.comment <file>``.
|
||||
|
||||
* The ``-Map`` option is supported. With that, you can print out section
|
||||
and symbol information to a specified file. This feature is useful
|
||||
for analyzing link results.
|
||||
|
||||
* The file format for the ``-reproduce`` option has changed from cpio to
|
||||
tar.
|
||||
|
||||
* When creating a copy relocation for a symbol, LLD now scans the
|
||||
DSO's header to see if the symbol is in a read-only segment. If so,
|
||||
space for the copy relocation is reserved in .bss.rel.ro instead of
|
||||
.bss. This fixes a security issue that read-only data in a DSO
|
||||
becomes writable if it is copied by a copy relocation. This issue
|
||||
was disclosed originally on the
|
||||
`binutils mailing list <https://sourceware.org/ml/libc-alpha/2016-12/msg00914.html>`_.
|
||||
|
||||
* Compressed input sections are supported.
|
||||
|
||||
* ``--oformat binary``, ``--section-start``, ``-Tbss``, ``-Tdata``,
|
||||
``-Ttext``, ``-b binary``, ``-build-id=uuid``, ``-no-rosegment``,
|
||||
``-nopie``, ``-nostdlib``, ``-omagic``, ``-retain-symbols-file``,
|
||||
``-sort-section``, ``-z max-page-size`` and ``-z wxneeded`` are
|
||||
supported.
|
||||
|
||||
* A lot of linker script directives have been added.
|
||||
|
||||
* Default image base address for x86-64 has changed from 0x10000 to
|
||||
0x200000 to make it huge-page friendly.
|
||||
|
||||
* ARM port now supports GNU ifunc, the ARM C++ exceptions ABI, TLS
|
||||
relocations and static linking. Problems with ``dlopen()`` on systems
|
||||
using eglibc fixed.
|
||||
|
||||
* MIPS port now supports input files in new R6 revision of MIPS ABIs
|
||||
or N32 ABI. Generated file now contains .MIPS.abiflags section and
|
||||
complete set of ELF headers flags.
|
||||
|
||||
* Relocations produced by the ``-mxgot`` compiler flag is supported
|
||||
for MIPS. Now it is possible to generate "large" GOT that exceeds the 64K
|
||||
limit.
|
||||
* Item 1.
|
||||
|
||||
COFF Improvements
|
||||
-----------------
|
||||
|
||||
* Performance on Windows has been improved by parallelizing parts of the
|
||||
linker and optimizing file system operations. As a result of these
|
||||
improvements, LLD 4.0 has been measured to be about 2.5 times faster
|
||||
than LLD 3.9 when linking a large Chromium DLL.
|
||||
* Item 1.
|
||||
|
||||
MachO Improvements
|
||||
------------------
|
||||
|
||||
* Item 1.
|
||||
|
|
|
|||
|
|
@ -47,10 +47,10 @@ copyright = u'2011-%d, LLVM Project' % date.today().year
|
|||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '4'
|
||||
# The short version.
|
||||
version = '5'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '4'
|
||||
release = '5'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
Linker Design
|
||||
=============
|
||||
|
||||
Note: this document discuss Mach-O port of LLD. For ELF and COFF,
|
||||
see :doc:`index`.
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@
|
|||
Development
|
||||
===========
|
||||
|
||||
Note: this document discuss Mach-O port of LLD. For ELF and COFF,
|
||||
see :doc:`index`.
|
||||
|
||||
lld is developed as part of the `LLVM <http://llvm.org>`_ project.
|
||||
|
||||
Using C++11 in lld
|
||||
------------------
|
||||
|
||||
:doc:`C++11`.
|
||||
|
||||
Creating a Reader
|
||||
-----------------
|
||||
|
||||
|
|
@ -43,6 +41,5 @@ information on writing documentation for the project, see the
|
|||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
C++11
|
||||
Readers
|
||||
Driver
|
||||
|
|
|
|||
188
docs/index.rst
188
docs/index.rst
|
|
@ -1,25 +1,179 @@
|
|||
.. _index:
|
||||
|
||||
lld - The LLVM Linker
|
||||
LLD - The LLVM Linker
|
||||
=====================
|
||||
|
||||
lld contains two linkers whose architectures are different from each other.
|
||||
LLD is a linker from the LLVM project. That is a drop-in replacement
|
||||
for system linkers and runs much faster than them. It also provides
|
||||
features that are useful for toolchain developers.
|
||||
|
||||
The linker supports ELF (Unix), PE/COFF (Windows) and Mach-O (macOS)
|
||||
in descending order of completeness. Internally, LLD consists of three
|
||||
different linkers. The ELF port is the one that will be described in
|
||||
this document. The PE/COFF port is almost complete except the lack of
|
||||
the Windows debug info (PDB) support. The Mach-O port is built based
|
||||
on a different architecture than the ELF or COFF ports. For the
|
||||
details about Mach-O, please read :doc:`AtomLLD`.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- LLD is a drop-in replacement for the GNU linkers. That accepts the
|
||||
same command line arguments and linker scripts as GNU.
|
||||
|
||||
We are currently working closely with the FreeBSD project to make
|
||||
LLD default system linker in future versions of the operating
|
||||
system, so we are serious about addressing compatibility issues. As
|
||||
of February 2017, LLD is able to link the entire FreeBSD/amd64 base
|
||||
system including the kernel. With a few work-in-progress patches it
|
||||
can link approximately 95% of the ports collection on AMD64. For the
|
||||
details, see `FreeBSD quarterly status report
|
||||
<https://www.freebsd.org/news/status/report-2016-10-2016-12.html#Using-LLVM%27s-LLD-Linker-as-FreeBSD%27s-System-Linker>`_.
|
||||
|
||||
- LLD is very fast. When you link a large program on a multicore
|
||||
machine, you can expect that LLD runs more than twice as fast as GNU
|
||||
gold linker. Your milage may vary, though.
|
||||
|
||||
- It supports various CPUs/ABIs including x86-64, x86, x32, AArch64,
|
||||
ARM, MIPS 32/64 big/little-endian, PowerPC, PowerPC 64 and AMDGPU.
|
||||
Among these, x86-64 is the most well-supported target and have
|
||||
reached production quality. AArch64 and MIPS seem decent too. x86
|
||||
should be OK but not well tested yet. ARM support is being developed
|
||||
actively.
|
||||
|
||||
- It is always a cross-linker, meaning that it always supports all the
|
||||
above targets however it was built. In fact, we don't provide a
|
||||
build-time option to enable/disable each target. This should make it
|
||||
easy to use our linker as part of a cross-compile toolchain.
|
||||
|
||||
- You can embed LLD to your program to eliminate dependency to
|
||||
external linkers. All you have to do is to construct object files
|
||||
and command line arguments just like you would do to invoke an
|
||||
external linker and then call the linker's main function,
|
||||
``lld::elf::link``, from your code.
|
||||
|
||||
- It is small. We are using LLVM libObject library to read from object
|
||||
files, so it is not completely a fair comparison, but as of February
|
||||
2017, LLD/ELF consists only of 21k lines of C++ code while GNU gold
|
||||
consists of 198k lines of C++ code.
|
||||
|
||||
- Link-time optimization (LTO) is supported by default. Essentially,
|
||||
all you have to do to do LTO is to pass the ``-flto`` option to clang.
|
||||
Then clang creates object files not in the native object file format
|
||||
but in LLVM bitcode format. LLD reads bitcode object files, compile
|
||||
them using LLVM and emit an output file. Because in this way LLD can
|
||||
see the entire program, it can do the whole program optimization.
|
||||
|
||||
- Some very old features for ancient Unix systems (pre-90s or even
|
||||
before that) have been removed. Some default settings have been
|
||||
tuned for the 21st century. For example, the stack is marked as
|
||||
non-executable by default to tighten security.
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
This is a link time comparison on a 2-socket 20-core 40-thread Xeon
|
||||
E5-2680 2.80 GHz machine with an SSD drive.
|
||||
|
||||
LLD is much faster than the GNU linkers for large programs. That's
|
||||
fast for small programs too, but because the link time is short
|
||||
anyway, the difference is not very noticeable in that case.
|
||||
|
||||
Note that this is just a benchmark result of our environment.
|
||||
Depending on number of available cores, available amount of memory or
|
||||
disk latency/throughput, your results may vary.
|
||||
|
||||
============ =========== ============ ============= ======
|
||||
Program Output size GNU ld GNU gold [1]_ LLD
|
||||
ffmpeg dbg 91 MiB 1.59s 1.15s 0.78s
|
||||
mysqld dbg 157 MiB 7.09s 2.49s 1.31s
|
||||
clang dbg 1.45 GiB 86.76s 21.93s 8.38s
|
||||
chromium dbg 1.52 GiB 142.30s [2]_ 40.86s 12.69s
|
||||
============ =========== ============ ============= ======
|
||||
|
||||
.. [1] With the ``--threads`` option to enable multi-threading support.
|
||||
|
||||
.. [2] Since GNU ld doesn't support the ``-icf=all`` option, we
|
||||
removed that from the command line for GNU ld. GNU ld would be
|
||||
slower than this if it had that option support. For gold and
|
||||
LLD, we use ``-icf=all``.
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
If you have already checked out LLVM using SVN, you can check out LLD
|
||||
under ``tools`` directory just like you probably did for clang. For the
|
||||
details, see `Getting Started with the LLVM System
|
||||
<http://llvm.org/docs/GettingStarted.html>`_.
|
||||
|
||||
If you haven't checkout out LLVM, the easiest way to build LLD is to
|
||||
checkout the entire LLVM projects/sub-projects from a git mirror and
|
||||
build that tree. You need `cmake` and of course a C++ compiler.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ git clone https://github.com/llvm-project/llvm-project/
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS=lld -DCMAKE_INSTALL_PREFIX=/usr/local ../llvm-project/llvm
|
||||
$ make install
|
||||
|
||||
Using LLD
|
||||
---------
|
||||
|
||||
LLD is installed as ``ld.lld``. On Unix, linkers are invoked by
|
||||
compiler drivers, so you are not expected to use that command
|
||||
directly. There are a few ways to tell compiler drivers to use ld.lld
|
||||
instead of the default linker.
|
||||
|
||||
The easiest way to do that is to overwrite the default linker. After
|
||||
installing LLD to somewhere on your disk, you can create a symbolic
|
||||
link by doing ``ln -s /path/to/ld.lld /usr/bin/ld`` so that
|
||||
``/usr/bin/ld`` is resolved to LLD.
|
||||
|
||||
If you don't want to change the system setting, you can use clang's
|
||||
``-fuse-ld`` option. In this way, you want to set ``-fuse-ld=lld`` to
|
||||
LDFLAGS when building your programs.
|
||||
|
||||
LLD leaves its name and version number to a ``.comment`` section in an
|
||||
output. If you are in doubt whether you are successfully using LLD or
|
||||
not, run ``objdump -s -j .comment <output-file>`` and examine the
|
||||
output. If the string "Linker: LLD" is included in the output, you are
|
||||
using LLD.
|
||||
|
||||
History
|
||||
-------
|
||||
|
||||
Here is a brief project history of the ELF and COFF ports.
|
||||
|
||||
- May 2015: We decided to rewrite the COFF linker and did that.
|
||||
Noticed that the new linker is much faster than the MSVC linker.
|
||||
|
||||
- July 2015: The new ELF port was developed based on the COFF linker
|
||||
architecture.
|
||||
|
||||
- September 2015: The first patches to support MIPS and AArch64 landed.
|
||||
|
||||
- October 2015: Succeeded to self-host the ELF port. We have noticed
|
||||
that the linker was faster than the GNU linkers, but we weren't sure
|
||||
at the time if we would be able to keep the gap as we would add more
|
||||
features to the linker.
|
||||
|
||||
- July 2016: Started working on improving the linker script support.
|
||||
|
||||
- December 2016: Succeeded to build the entire FreeBSD base system
|
||||
including the kernel. We had widen the performance gap against the
|
||||
GNU linkers.
|
||||
|
||||
Internals
|
||||
---------
|
||||
|
||||
For the internals of the linker, please read :doc:`NewLLD`. It is a bit
|
||||
outdated but the fundamental concepts remain valid. We'll update the
|
||||
document soon.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
NewLLD
|
||||
AtomLLD
|
||||
|
||||
Source
|
||||
------
|
||||
|
||||
lld is available in the LLVM SVN repository::
|
||||
|
||||
svn co http://llvm.org/svn/llvm-project/lld/trunk lld
|
||||
|
||||
lld is also available via the read-only git mirror::
|
||||
|
||||
git clone http://llvm.org/git/lld.git
|
||||
|
||||
Put it in llvm's tools/ directory, rerun cmake, then build target lld.
|
||||
windows_support
|
||||
ReleaseNotes
|
||||
|
|
|
|||
|
|
@ -318,12 +318,11 @@ void parallel_for(IndexTy Begin, IndexTy End, FuncTy Fn) {
|
|||
|
||||
TaskGroup Tg;
|
||||
IndexTy I = Begin;
|
||||
for (; I < End; I += TaskSize) {
|
||||
for (; I + TaskSize < End; I += TaskSize) {
|
||||
Tg.spawn([=, &Fn] {
|
||||
for (IndexTy J = I, E = I + TaskSize; J != E; ++J)
|
||||
Fn(J);
|
||||
});
|
||||
Begin += TaskSize;
|
||||
}
|
||||
Tg.spawn([=, &Fn] {
|
||||
for (IndexTy J = I; J < End; ++J)
|
||||
|
|
|
|||
20
include/lld/Core/TargetOptionsCommandFlags.h
Normal file
20
include/lld/Core/TargetOptionsCommandFlags.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//===-- TargetOptionsCommandFlags.h ----------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Helper to create TargetOptions from command line flags.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Support/CodeGen.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
|
||||
namespace lld {
|
||||
llvm::TargetOptions InitTargetOptionsFromCodeGenFlags();
|
||||
llvm::CodeModel::Model GetCodeModelFromCMModel();
|
||||
}
|
||||
|
|
@ -15,7 +15,8 @@
|
|||
|
||||
namespace lld {
|
||||
namespace coff {
|
||||
bool link(llvm::ArrayRef<const char *> Args);
|
||||
bool link(llvm::ArrayRef<const char *> Args,
|
||||
llvm::raw_ostream &Diag = llvm::errs());
|
||||
}
|
||||
|
||||
namespace elf {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
if(NOT LLD_BUILT_STANDALONE)
|
||||
set(tablegen_deps intrinsics_gen)
|
||||
endif()
|
||||
|
||||
add_lld_library(lldCore
|
||||
DefinedAtom.cpp
|
||||
Error.cpp
|
||||
|
|
@ -7,11 +11,16 @@ add_lld_library(lldCore
|
|||
Reproduce.cpp
|
||||
Resolver.cpp
|
||||
SymbolTable.cpp
|
||||
TargetOptionsCommandFlags.cpp
|
||||
Writer.cpp
|
||||
|
||||
ADDITIONAL_HEADER_DIRS
|
||||
${LLD_INCLUDE_DIR}/lld/Core
|
||||
|
||||
LINK_COMPONENTS
|
||||
MC
|
||||
Support
|
||||
|
||||
DEPENDS
|
||||
${tablegen_deps}
|
||||
)
|
||||
|
|
|
|||
32
lib/Core/TargetOptionsCommandFlags.cpp
Normal file
32
lib/Core/TargetOptionsCommandFlags.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
//===-- TargetOptionsCommandFlags.cpp ---------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Linker
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file exists as a place for global variables defined in LLVM's
|
||||
// CodeGen/CommandFlags.h. By putting the resulting object file in
|
||||
// an archive and linking with it, the definitions will automatically be
|
||||
// included when needed and skipped when already present.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "lld/Core/TargetOptionsCommandFlags.h"
|
||||
|
||||
#include "llvm/CodeGen/CommandFlags.h"
|
||||
#include "llvm/Target/TargetOptions.h"
|
||||
|
||||
// Define an externally visible version of
|
||||
// InitTargetOptionsFromCodeGenFlags, so that its functionality can be
|
||||
// used without having to include llvm/CodeGen/CommandFlags.h, which
|
||||
// would lead to multiple definitions of the command line flags.
|
||||
llvm::TargetOptions lld::InitTargetOptionsFromCodeGenFlags() {
|
||||
return ::InitTargetOptionsFromCodeGenFlags();
|
||||
}
|
||||
|
||||
llvm::CodeModel::Model lld::GetCodeModelFromCMModel() {
|
||||
return CMModel;
|
||||
}
|
||||
|
|
@ -51,11 +51,7 @@ public:
|
|||
canBypassGOT = true;
|
||||
return true;
|
||||
case delta32ToGOT:
|
||||
canBypassGOT = false;
|
||||
return true;
|
||||
case unwindCIEToPersonalityFunction:
|
||||
canBypassGOT = false;
|
||||
return true;
|
||||
case imageOffsetGot:
|
||||
canBypassGOT = false;
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -118,14 +118,7 @@ public:
|
|||
normalized::Relocations &relocs) override;
|
||||
|
||||
bool isDataInCodeTransition(Reference::KindValue refKind) override {
|
||||
switch (refKind) {
|
||||
case modeCode:
|
||||
case modeData:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return refKind == modeCode || refKind == modeData;
|
||||
}
|
||||
|
||||
Reference::KindValue dataInCodeTransitionStart(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ add_lld_library(lldMachO
|
|||
LINK_LIBS
|
||||
lldCore
|
||||
lldYAML
|
||||
${PTHREAD_LIB}
|
||||
${LLVM_PTHREAD_LIB}
|
||||
)
|
||||
|
||||
include_directories(.)
|
||||
|
|
|
|||
|
|
@ -3,13 +3,18 @@ set(LLVM_BINARY_DIR "${LLVM_BINARY_DIR}")
|
|||
set(LLVM_BUILD_MODE "%(build_mode)s")
|
||||
set(LLVM_TOOLS_DIR "${LLVM_TOOLS_BINARY_DIR}/%(build_config)s")
|
||||
set(LLVM_LIBS_DIR "${LLVM_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/%(build_config)s")
|
||||
set(CLANG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
set(CLANG_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/..")
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(ENABLE_SHARED 1)
|
||||
else()
|
||||
set(ENABLE_SHARED 0)
|
||||
endif(BUILD_SHARED_LIBS)
|
||||
|
||||
if(LLD_BUILT_STANDALONE)
|
||||
# Set HAVE_LIBZ according to recorded LLVM_ENABLE_ZLIB value. This
|
||||
# value is forced to 0 if zlib was not found, so it is fine to use it
|
||||
# instead of HAVE_LIBZ (not recorded).
|
||||
if(LLVM_ENABLE_ZLIB)
|
||||
set(HAVE_LIBZ 1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
llvm_canonicalize_cmake_booleans(
|
||||
HAVE_LIBZ)
|
||||
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
|
||||
|
|
@ -19,13 +24,17 @@ configure_lit_site_cfg(
|
|||
${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
|
||||
)
|
||||
|
||||
set(LLD_TEST_DEPS
|
||||
FileCheck not llvm-ar llvm-as llvm-dis llvm-dwarfdump llvm-nm
|
||||
llc lld llvm-config llvm-objdump llvm-readobj yaml2obj obj2yaml
|
||||
llvm-mc llvm-lib llvm-pdbdump opt
|
||||
)
|
||||
set(LLD_TEST_DEPS lld)
|
||||
if (NOT LLD_BUILT_STANDALONE)
|
||||
list(APPEND LLD_TEST_DEPS
|
||||
FileCheck count not llvm-ar llvm-as llvm-dis llvm-dwarfdump llvm-nm
|
||||
llc llvm-config llvm-objdump llvm-readobj yaml2obj obj2yaml
|
||||
llvm-mc llvm-lib llvm-pdbdump opt
|
||||
)
|
||||
endif()
|
||||
|
||||
if (LLVM_INCLUDE_TESTS)
|
||||
set(LLD_TEST_DEPS ${LLD_TEST_DEPS} LLDUnitTests)
|
||||
list(APPEND LLD_TEST_DEPS LLDUnitTests)
|
||||
endif()
|
||||
|
||||
set(LLD_TEST_PARAMS
|
||||
|
|
|
|||
6
test/COFF/Inputs/bar.ll
Normal file
6
test/COFF/Inputs/bar.ll
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc"
|
||||
|
||||
define void @bar() {
|
||||
ret void
|
||||
}
|
||||
3
test/COFF/Inputs/msvclto.s
Normal file
3
test/COFF/Inputs/msvclto.s
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.globl foo
|
||||
foo:
|
||||
ret
|
||||
28
test/COFF/Inputs/thinlto-mangled-qux.ll
Normal file
28
test/COFF/Inputs/thinlto-mangled-qux.ll
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-pc-windows-msvc19.0.24215"
|
||||
|
||||
%class.baz = type { %class.bar }
|
||||
%class.bar = type { i32 (...)** }
|
||||
|
||||
$"\01?x@bar@@UEBA_NXZ" = comdat any
|
||||
|
||||
$"\01??_7baz@@6B@" = comdat any
|
||||
|
||||
$"\01??_Gbaz@@UEAAPEAXI@Z" = comdat any
|
||||
|
||||
@"\01??_7baz@@6B@" = linkonce_odr unnamed_addr constant { [2 x i8*] } { [2 x i8*] [i8* bitcast (i8* (%class.baz*, i32)* @"\01??_Gbaz@@UEAAPEAXI@Z" to i8*), i8* bitcast (i1 (%class.bar*)* @"\01?x@bar@@UEBA_NXZ" to i8*)] }, comdat, !type !0, !type !1
|
||||
|
||||
define void @"\01?qux@@YAXXZ"() local_unnamed_addr {
|
||||
ret void
|
||||
}
|
||||
|
||||
define linkonce_odr i8* @"\01??_Gbaz@@UEAAPEAXI@Z"(%class.baz* %this, i32 %should_call_delete) unnamed_addr comdat {
|
||||
ret i8* null
|
||||
}
|
||||
|
||||
define linkonce_odr zeroext i1 @"\01?x@bar@@UEBA_NXZ"(%class.bar* %this) unnamed_addr comdat {
|
||||
ret i1 false
|
||||
}
|
||||
|
||||
!0 = !{i64 0, !"?AVbar@@"}
|
||||
!1 = !{i64 0, !"?AVbaz@@"}
|
||||
28
test/COFF/def-name.test
Normal file
28
test/COFF/def-name.test
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# REQUIRES: winres
|
||||
|
||||
# RUN: rm -rf %t
|
||||
# RUN: mkdir -p %t
|
||||
# RUN: cd %t
|
||||
# RUN: yaml2obj < %p/Inputs/ret42.yaml > in.obj
|
||||
|
||||
# RUN: lld-link /entry:main in.obj
|
||||
# RUN: lld-link /entry:main /dll in.obj
|
||||
|
||||
# RUN: echo -e "NAME foo\n" > fooexe.def
|
||||
# RUN: echo -e "LIBRARY foo\n" > foodll.def
|
||||
# RUN: lld-link /entry:main /def:fooexe.def in.obj
|
||||
# RUN: lld-link /entry:main /def:foodll.def /dll in.obj
|
||||
|
||||
# RUN: lld-link /entry:main /out:bar.exe /def:fooexe.def in.obj
|
||||
# RUN: lld-link /entry:main /out:bar.dll /def:foodll.def /dll in.obj
|
||||
|
||||
# RUN: llvm-readobj in.exe | FileCheck %s
|
||||
# RUN: llvm-readobj in.dll | FileCheck %s
|
||||
|
||||
# RUN: llvm-readobj foo.exe | FileCheck %s
|
||||
# RUN: llvm-readobj foo.dll | FileCheck %s
|
||||
|
||||
# RUN: llvm-readobj bar.exe | FileCheck %s
|
||||
# RUN: llvm-readobj bar.dll | FileCheck %s
|
||||
|
||||
CHECK: File:
|
||||
3
test/COFF/driver-windows.test
Normal file
3
test/COFF/driver-windows.test
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# REQUIRES: system-windows
|
||||
# RUN: not LLD-LINK 2>&1 | FileCheck %s
|
||||
CHECK: no input files
|
||||
29
test/COFF/error-limit.test
Normal file
29
test/COFF/error-limit.test
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
RUN: not lld-link 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 \
|
||||
RUN: 21 22 2>&1 | FileCheck -check-prefix=DEFAULT %s
|
||||
|
||||
DEFAULT: could not open 01
|
||||
DEFAULT: could not open 20
|
||||
DEFAULT-NEXT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
|
||||
DEFAULT-NOT: could not open 21
|
||||
|
||||
RUN: not lld-link /ERRORLIMIT:5 01 02 03 04 05 06 07 08 09 10 2>&1 \
|
||||
RUN: | FileCheck -check-prefix=LIMIT5 %s
|
||||
|
||||
LIMIT5: could not open 01
|
||||
LIMIT5: could not open 05
|
||||
LIMIT5-NEXT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
|
||||
LIMIT5-NOT: could not open 06
|
||||
|
||||
RUN: not lld-link /ERRORLIMIT:0 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 \
|
||||
RUN: 16 17 18 19 20 21 22 2>&1 | FileCheck -check-prefix=UNLIMITED %s
|
||||
|
||||
UNLIMITED: could not open 01
|
||||
UNLIMITED: could not open 20
|
||||
UNLIMITED: could not open 21
|
||||
UNLIMITED: could not open 22
|
||||
UNLIMITED-NOT: too many errors emitted, stopping now (use /ERRORLIMIT:0 to see all errors)
|
||||
|
||||
RUN: not lld-link /ERRORLIMIT:XYZ 01 02 03 04 05 06 07 08 09 10 11 12 13 14 \
|
||||
RUN: 15 16 17 18 19 20 21 22 2>&1 | FileCheck -check-prefix=WRONG %s
|
||||
|
||||
WRONG: /ERRORLIMIT: number expected, but got XYZ
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue