--- /dev/null
+# Almost identical to the top-level .clang-tidy, except that {Member,Parameter,Variable}Case use camelBack.
+Checks: '-*,clang-diagnostic-*,llvm-*,misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming'
+CheckOptions:
+ - key: readability-identifier-naming.ClassCase
+ value: CamelCase
+ - key: readability-identifier-naming.EnumCase
+ value: CamelCase
+ - key: readability-identifier-naming.FunctionCase
+ value: camelBack
+ - key: readability-identifier-naming.MemberCase
+ value: camelBack
+ - key: readability-identifier-naming.ParameterCase
+ value: camelBack
+ - key: readability-identifier-naming.UnionCase
+ value: CamelCase
+ - key: readability-identifier-naming.VariableCase
+ value: camelBack
+ - key: readability-identifier-naming.IgnoreMainLikeFunctions
+ value: 1
include(AddLLVM)
include(TableGen)
include(HandleLLVMOptions)
+ include(CheckAtomic)
if(LLVM_INCLUDE_TESTS)
- include(FindPythonInterp)
- if(NOT PYTHONINTERP_FOUND)
- message(FATAL_ERROR
-"Unable to find Python interpreter, required for testing.
+ if(CMAKE_VERSION VERSION_LESS 3.12)
+ 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()
+ 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()
- if(${PYTHON_VERSION_STRING} VERSION_LESS 2.7)
- message(FATAL_ERROR "Python 2.7 or newer is required")
+ add_executable(Python3::Interpeter IMPORTED)
+ set_target_properties(Python3::Interpreter PROPERTIES
+ IMPORTED_LOCATION ${PYTHON_EXECUTABLE})
+ set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
+ else()
+ find_package(Python3 COMPONENTS Interpreter)
+ if(NOT Python3_Interpreter_FOUND)
+ message(WARNING "Python3 not found, using python2 as a fallback")
+ find_package(Python2 COMPONENTS Interpreter REQUIRED)
+ if(Python2_VERSION VERSION_LESS 2.7)
+ message(SEND_ERROR "Python 2.7 or newer is required")
+ endif()
+
+ # Treat python2 as python3
+ add_executable(Python3::Interpreter IMPORTED)
+ set_target_properties(Python3::Interpreter PROPERTIES
+ IMPORTED_LOCATION ${Python2_EXECUTABLE})
+ set(Python3_EXECUTABLE ${Python2_EXECUTABLE})
+ endif()
endif()
# Check prebuilt llvm/utils.
set(LLD_INCLUDE_DIR ${LLD_SOURCE_DIR}/include )
set(LLD_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})
+set(LLD_VENDOR ${PACKAGE_VENDOR} CACHE STRING
+ "Vendor-specific text for showing with version information.")
+
+if(LLD_VENDOR)
+ add_definitions(-DLLD_VENDOR="${LLD_VENDOR}")
+endif()
+
# Compute the LLD version from the LLVM version.
string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLD_VERSION
${PACKAGE_VERSION})
string(REGEX REPLACE "[0-9]+\\.([0-9]+)(\\.[0-9]+)?" "\\1" LLD_VERSION_MINOR
${LLD_VERSION})
-# Determine LLD revision and repository.
-# TODO: Figure out a way to get the revision and the repository on windows.
-if ( NOT CMAKE_SYSTEM_NAME MATCHES "Windows" )
- execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetSourceVersion ${LLD_SOURCE_DIR}
- OUTPUT_VARIABLE LLD_REVISION)
-
- execute_process(COMMAND ${CMAKE_SOURCE_DIR}/utils/GetRepositoryPath ${LLD_SOURCE_DIR}
- OUTPUT_VARIABLE LLD_REPOSITORY)
- if ( LLD_REPOSITORY )
- # Replace newline characters with spaces
- string(REGEX REPLACE "(\r?\n)+" " " LLD_REPOSITORY ${LLD_REPOSITORY})
- # Remove leading spaces
- STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REPOSITORY "${LLD_REPOSITORY}" )
- # Remove trailing spaces
- string(REGEX REPLACE "(\ )+$" "" LLD_REPOSITORY ${LLD_REPOSITORY})
- endif()
-
- if ( LLD_REVISION )
- # Replace newline characters with spaces
- string(REGEX REPLACE "(\r?\n)+" " " LLD_REVISION ${LLD_REVISION})
- # Remove leading spaces
- STRING(REGEX REPLACE "^[ \t\r\n]+" "" LLD_REVISION "${LLD_REVISION}" )
- # Remove trailing spaces
- string(REGEX REPLACE "(\ )+$" "" LLD_REVISION ${LLD_REVISION})
- endif()
-endif ()
-
# Configure the Version.inc file.
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/include/lld/Common/Version.inc.in
add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)
+add_subdirectory(MachO)
add_subdirectory(MinGW)
add_subdirectory(wasm)
+
+add_subdirectory(cmake/modules)
DriverUtils.cpp
ICF.cpp
InputFiles.cpp
+ LLDMapFile.cpp
LTO.cpp
MapFile.cpp
MarkLive.cpp
} else {
COFFSymbolRef coffSym =
check(file->getCOFFObj()->getSymbol(rel.SymbolTableIndex));
- file->getCOFFObj()->getSymbolName(coffSym, name);
+ name = check(file->getCOFFObj()->getSymbolName(coffSym));
}
std::vector<std::string> symbolLocations =
AssociatedIterator() = default;
AssociatedIterator(SectionChunk *head) : cur(head) {}
bool operator==(const AssociatedIterator &r) const { return cur == r.cur; }
- const SectionChunk &operator*() const { return *cur; }
+ // FIXME: Wrong const-ness, but it makes filter ranges work.
+ SectionChunk &operator*() const { return *cur; }
SectionChunk &operator*() { return *cur; }
AssociatedIterator &operator++() {
cur = cur->assocChildren;
bool showSummary = false;
unsigned debugTypes = static_cast<unsigned>(DebugType::None);
std::vector<std::string> natvisFiles;
+ llvm::StringMap<std::string> namedStreams;
llvm::SmallString<128> pdbAltPath;
llvm::SmallString<128> pdbPath;
llvm::SmallString<128> pdbSourcePath;
bool safeSEH = false;
Symbol *sehTable = nullptr;
Symbol *sehCount = nullptr;
+ bool noSEH = false;
// Used for /opt:lldlto=N
unsigned ltoo = 2;
// Used for /opt:lldltojobs=N
- unsigned thinLTOJobs = 0;
+ std::string thinLTOJobs;
// Used for /opt:lldltopartitions=N
unsigned ltoPartitions = 1;
llvm::StringMap<int> order;
// Used for /lldmap.
+ std::string lldmapFile;
+
+ // Used for /map.
std::string mapFile;
// Used for /thinlto-index-only:
uint32_t functionPadMin = 0;
bool dynamicBase = true;
bool allowBind = true;
+ bool cetCompat = false;
bool nxCompat = true;
bool allowIsolation = true;
bool terminalServerAware = true;
bool swaprunNet = false;
bool thinLTOEmitImportsFiles;
bool thinLTOIndexOnly;
+ bool autoImport = false;
+ bool pseudoRelocs = false;
};
extern Configuration *config;
//===----------------------------------------------------------------------===//
#include "DebugTypes.h"
+#include "Chunks.h"
#include "Driver.h"
#include "InputFiles.h"
+#include "TypeMerger.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
+#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/TpiStream.h"
#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::codeview;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
namespace {
// The TypeServerSource class represents a PDB type server, a file referenced by
// before any dependent OBJ.
class TypeServerSource : public TpiSource {
public:
- explicit TypeServerSource(MemoryBufferRef m, llvm::pdb::NativeSession *s)
- : TpiSource(PDB, nullptr), session(s), mb(m) {}
-
- // Queue a PDB type server for loading in the COFF Driver
- static void enqueue(const ObjFile *dependentFile,
- const TypeServer2Record &ts);
-
- // Create an instance
- static Expected<TypeServerSource *> getInstance(MemoryBufferRef m);
-
- // Fetch the PDB instance loaded for a corresponding dependent OBJ.
- static Expected<TypeServerSource *>
- findFromFile(const ObjFile *dependentFile);
-
- static std::map<std::string, std::pair<std::string, TypeServerSource *>>
- instances;
-
- // The interface to the PDB (if it was opened successfully)
- std::unique_ptr<llvm::pdb::NativeSession> session;
-
-private:
- MemoryBufferRef mb;
+ explicit TypeServerSource(PDBInputFile *f)
+ : TpiSource(PDB, nullptr), pdbInputFile(f) {
+ if (f->loadErr && *f->loadErr)
+ return;
+ pdb::PDBFile &file = f->session->getPDBFile();
+ auto expectedInfo = file.getPDBInfoStream();
+ if (!expectedInfo)
+ return;
+ auto it = mappings.emplace(expectedInfo->getGuid(), this);
+ assert(it.second);
+ (void)it;
+ tsIndexMap.isTypeServerMap = true;
+ }
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
+ bool isDependency() const override { return true; }
+
+ PDBInputFile *pdbInputFile = nullptr;
+
+ CVIndexMap tsIndexMap;
+
+ static std::map<codeview::GUID, TypeServerSource *> mappings;
};
// This class represents the debug type stream of an OBJ file that depends on a
// PDB type server (see TypeServerSource).
class UseTypeServerSource : public TpiSource {
public:
- UseTypeServerSource(const ObjFile *f, const TypeServer2Record *ts)
- : TpiSource(UsingPDB, f), typeServerDependency(*ts) {}
+ UseTypeServerSource(ObjFile *f, TypeServer2Record ts)
+ : TpiSource(UsingPDB, f), typeServerDependency(ts) {}
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
// Information about the PDB type server dependency, that needs to be loaded
// in before merging this OBJ.
// such files, clang does not.
class PrecompSource : public TpiSource {
public:
- PrecompSource(const ObjFile *f) : TpiSource(PCH, f) {}
+ PrecompSource(ObjFile *f) : TpiSource(PCH, f) {
+ if (!f->pchSignature || !*f->pchSignature)
+ fatal(toString(f) +
+ " claims to be a PCH object, but does not have a valid signature");
+ auto it = mappings.emplace(*f->pchSignature, this);
+ if (!it.second)
+ fatal("a PCH object with the same signature has already been provided (" +
+ toString(it.first->second->file) + " and " + toString(file) + ")");
+ precompIndexMap.isPrecompiledTypeMap = true;
+ }
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
+ bool isDependency() const override { return true; }
+
+ CVIndexMap precompIndexMap;
+
+ static std::map<uint32_t, PrecompSource *> mappings;
};
// This class represents the debug type stream of an OBJ file that depends on a
// Microsoft precompiled headers OBJ (see PrecompSource).
class UsePrecompSource : public TpiSource {
public:
- UsePrecompSource(const ObjFile *f, const PrecompRecord *precomp)
- : TpiSource(UsingPCH, f), precompDependency(*precomp) {}
+ UsePrecompSource(ObjFile *f, PrecompRecord precomp)
+ : TpiSource(UsingPCH, f), precompDependency(precomp) {}
+
+ Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) override;
// Information about the Precomp OBJ dependency, that needs to be loaded in
// before merging this OBJ.
};
} // namespace
-TpiSource::TpiSource(TpiKind k, const ObjFile *f) : kind(k), file(f) {}
+static std::vector<TpiSource *> gc;
-TpiSource *makeTpiSource(const ObjFile *f) {
- return make<TpiSource>(TpiSource::Regular, f);
+TpiSource::TpiSource(TpiKind k, ObjFile *f) : kind(k), file(f) {
+ gc.push_back(this);
}
-TpiSource *makeUseTypeServerSource(const ObjFile *f,
- const TypeServer2Record *ts) {
- TypeServerSource::enqueue(f, *ts);
- return make<UseTypeServerSource>(f, ts);
+// Vtable key method.
+TpiSource::~TpiSource() = default;
+
+TpiSource *lld::coff::makeTpiSource(ObjFile *file) {
+ return make<TpiSource>(TpiSource::Regular, file);
}
-TpiSource *makePrecompSource(const ObjFile *f) {
- return make<PrecompSource>(f);
+TpiSource *lld::coff::makeTypeServerSource(PDBInputFile *pdbInputFile) {
+ return make<TypeServerSource>(pdbInputFile);
}
-TpiSource *makeUsePrecompSource(const ObjFile *f,
- const PrecompRecord *precomp) {
- return make<UsePrecompSource>(f, precomp);
+TpiSource *lld::coff::makeUseTypeServerSource(ObjFile *file,
+ TypeServer2Record ts) {
+ return make<UseTypeServerSource>(file, ts);
}
-template <>
-const PrecompRecord &retrieveDependencyInfo(const TpiSource *source) {
- assert(source->kind == TpiSource::UsingPCH);
- return ((const UsePrecompSource *)source)->precompDependency;
+TpiSource *lld::coff::makePrecompSource(ObjFile *file) {
+ return make<PrecompSource>(file);
}
-template <>
-const TypeServer2Record &retrieveDependencyInfo(const TpiSource *source) {
- assert(source->kind == TpiSource::UsingPDB);
- return ((const UseTypeServerSource *)source)->typeServerDependency;
+TpiSource *lld::coff::makeUsePrecompSource(ObjFile *file,
+ PrecompRecord precomp) {
+ return make<UsePrecompSource>(file, precomp);
}
-std::map<std::string, std::pair<std::string, TypeServerSource *>>
- TypeServerSource::instances;
+void TpiSource::forEachSource(llvm::function_ref<void(TpiSource *)> fn) {
+ for_each(gc, fn);
+}
-// Make a PDB path assuming the PDB is in the same folder as the OBJ
-static std::string getPdbBaseName(const ObjFile *file, StringRef tSPath) {
- StringRef localPath =
- !file->parentName.empty() ? file->parentName : file->getName();
- SmallString<128> path = sys::path::parent_path(localPath);
+std::map<codeview::GUID, TypeServerSource *> TypeServerSource::mappings;
+
+std::map<uint32_t, PrecompSource *> PrecompSource::mappings;
+
+// A COFF .debug$H section is currently a clang extension. This function checks
+// if a .debug$H section is in a format that we expect / understand, so that we
+// can ignore any sections which are coincidentally also named .debug$H but do
+// not contain a format we recognize.
+static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
+ if (debugH.size() < sizeof(object::debug_h_header))
+ return false;
+ auto *header =
+ reinterpret_cast<const object::debug_h_header *>(debugH.data());
+ debugH = debugH.drop_front(sizeof(object::debug_h_header));
+ return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
+ header->Version == 0 &&
+ header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
+ (debugH.size() % 8 == 0);
+}
- // Currently, type server PDBs are only created by MSVC cl, which only runs
- // on Windows, so we can assume type server paths are Windows style.
- sys::path::append(path, sys::path::filename(tSPath, sys::path::Style::windows));
- return path.str();
+static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
+ SectionChunk *sec =
+ SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
+ if (!sec)
+ return llvm::None;
+ ArrayRef<uint8_t> contents = sec->getContents();
+ if (!canUseDebugH(contents))
+ return None;
+ return contents;
}
-// The casing of the PDB path stamped in the OBJ can differ from the actual path
-// on disk. With this, we ensure to always use lowercase as a key for the
-// PDBInputFile::Instances map, at least on Windows.
-static std::string normalizePdbPath(StringRef path) {
-#if defined(_WIN32)
- return path.lower();
-#else // LINUX
- return path;
-#endif
+static ArrayRef<GloballyHashedType>
+getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
+ assert(canUseDebugH(debugH));
+
+ debugH = debugH.drop_front(sizeof(object::debug_h_header));
+ uint32_t count = debugH.size() / sizeof(GloballyHashedType);
+ return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count};
}
-// If existing, return the actual PDB path on disk.
-static Optional<std::string> findPdbPath(StringRef pdbPath,
- const ObjFile *dependentFile) {
- // Ensure the file exists before anything else. In some cases, if the path
- // points to a removable device, Driver::enqueuePath() would fail with an
- // error (EAGAIN, "resource unavailable try again") which we want to skip
- // silently.
- if (llvm::sys::fs::exists(pdbPath))
- return normalizePdbPath(pdbPath);
- std::string ret = getPdbBaseName(dependentFile, pdbPath);
- if (llvm::sys::fs::exists(ret))
- return normalizePdbPath(ret);
- return None;
+// Merge .debug$T for a generic object file.
+Expected<const CVIndexMap *> TpiSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap) {
+ CVTypeArray types;
+ BinaryStreamReader reader(file->debugTypes, support::little);
+ cantFail(reader.readArray(types, reader.getLength()));
+
+ // When dealing with PCH.OBJ, some indices were already merged.
+ unsigned nbHeadIndices = indexMap->tpiMap.size();
+
+ if (config->debugGHashes) {
+ ArrayRef<GloballyHashedType> hashes;
+ std::vector<GloballyHashedType> ownedHashes;
+ if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file))
+ hashes = getHashesFromDebugH(*debugH);
+ else {
+ ownedHashes = GloballyHashedType::hashTypes(types);
+ hashes = ownedHashes;
+ }
+
+ if (auto err = mergeTypeAndIdRecords(m->globalIDTable, m->globalTypeTable,
+ indexMap->tpiMap, types, hashes,
+ file->pchSignature))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(err)));
+ } else {
+ if (auto err =
+ mergeTypeAndIdRecords(m->idTable, m->typeTable, indexMap->tpiMap,
+ types, file->pchSignature))
+ fatal("codeview::mergeTypeAndIdRecords failed: " +
+ toString(std::move(err)));
+ }
+
+ if (config->showSummary) {
+ // Count how many times we saw each type record in our input. This
+ // calculation requires a second pass over the type records to classify each
+ // record as a type or index. This is slow, but this code executes when
+ // collecting statistics.
+ m->tpiCounts.resize(m->getTypeTable().size());
+ m->ipiCounts.resize(m->getIDTable().size());
+ uint32_t srcIdx = nbHeadIndices;
+ for (CVType &ty : types) {
+ TypeIndex dstIdx = indexMap->tpiMap[srcIdx++];
+ // Type merging may fail, so a complex source type may become the simple
+ // NotTranslated type, which cannot be used as an array index.
+ if (dstIdx.isSimple())
+ continue;
+ SmallVectorImpl<uint32_t> &counts =
+ isIdRecord(ty.kind()) ? m->ipiCounts : m->tpiCounts;
+ ++counts[dstIdx.toArrayIndex()];
+ }
+ }
+
+ return indexMap;
}
-// Fetch the PDB instance that was already loaded by the COFF Driver.
-Expected<TypeServerSource *>
-TypeServerSource::findFromFile(const ObjFile *dependentFile) {
- const TypeServer2Record &ts =
- retrieveDependencyInfo<TypeServer2Record>(dependentFile->debugTypesObj);
+// Merge types from a type server PDB.
+Expected<const CVIndexMap *> TypeServerSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *) {
+ pdb::PDBFile &pdbFile = pdbInputFile->session->getPDBFile();
+ Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
+ if (auto e = expectedTpi.takeError())
+ fatal("Type server does not have TPI stream: " + toString(std::move(e)));
+ pdb::TpiStream *maybeIpi = nullptr;
+ if (pdbFile.hasPDBIpiStream()) {
+ Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
+ if (auto e = expectedIpi.takeError())
+ fatal("Error getting type server IPI stream: " + toString(std::move(e)));
+ maybeIpi = &*expectedIpi;
+ }
+
+ if (config->debugGHashes) {
+ // PDBs do not actually store global hashes, so when merging a type server
+ // PDB we have to synthesize global hashes. To do this, we first synthesize
+ // global hashes for the TPI stream, since it is independent, then we
+ // synthesize hashes for the IPI stream, using the hashes for the TPI stream
+ // as inputs.
+ auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray());
+ Optional<uint32_t> endPrecomp;
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto err =
+ mergeTypeRecords(m->globalTypeTable, tsIndexMap.tpiMap,
+ expectedTpi->typeArray(), tpiHashes, endPrecomp))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
+
+ // Merge IPI.
+ if (maybeIpi) {
+ auto ipiHashes =
+ GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes);
+ if (auto err = mergeIdRecords(m->globalIDTable, tsIndexMap.tpiMap,
+ tsIndexMap.ipiMap, maybeIpi->typeArray(),
+ ipiHashes))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
+ } else {
+ // Merge TPI first, because the IPI stream will reference type indices.
+ if (auto err = mergeTypeRecords(m->typeTable, tsIndexMap.tpiMap,
+ expectedTpi->typeArray()))
+ fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
+
+ // Merge IPI.
+ if (maybeIpi) {
+ if (auto err = mergeIdRecords(m->idTable, tsIndexMap.tpiMap,
+ tsIndexMap.ipiMap, maybeIpi->typeArray()))
+ fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
+ }
+ }
+
+ if (config->showSummary) {
+ // Count how many times we saw each type record in our input. If a
+ // destination type index is present in the source to destination type index
+ // map, that means we saw it once in the input. Add it to our histogram.
+ m->tpiCounts.resize(m->getTypeTable().size());
+ m->ipiCounts.resize(m->getIDTable().size());
+ for (TypeIndex ti : tsIndexMap.tpiMap)
+ if (!ti.isSimple())
+ ++m->tpiCounts[ti.toArrayIndex()];
+ for (TypeIndex ti : tsIndexMap.ipiMap)
+ if (!ti.isSimple())
+ ++m->ipiCounts[ti.toArrayIndex()];
+ }
+
+ return &tsIndexMap;
+}
- Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
- if (!p)
- return createFileError(ts.Name, errorCodeToError(std::error_code(
- ENOENT, std::generic_category())));
+Expected<const CVIndexMap *>
+UseTypeServerSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) {
+ const codeview::GUID &tsId = typeServerDependency.getGuid();
+ StringRef tsPath = typeServerDependency.getName();
+
+ TypeServerSource *tsSrc;
+ auto it = TypeServerSource::mappings.find(tsId);
+ if (it != TypeServerSource::mappings.end()) {
+ tsSrc = it->second;
+ } else {
+ // The file failed to load, lookup by name
+ PDBInputFile *pdb = PDBInputFile::findFromRecordPath(tsPath, file);
+ if (!pdb)
+ return createFileError(tsPath, errorCodeToError(std::error_code(
+ ENOENT, std::generic_category())));
+ // If an error occurred during loading, throw it now
+ if (pdb->loadErr && *pdb->loadErr)
+ return createFileError(tsPath, std::move(*pdb->loadErr));
+
+ tsSrc = (TypeServerSource *)pdb->debugTypesObj;
+ }
+
+ pdb::PDBFile &pdbSession = tsSrc->pdbInputFile->session->getPDBFile();
+ auto expectedInfo = pdbSession.getPDBInfoStream();
+ if (!expectedInfo)
+ return &tsSrc->tsIndexMap;
+
+ // Just because a file with a matching name was found and it was an actual
+ // PDB file doesn't mean it matches. For it to match the InfoStream's GUID
+ // must match the GUID specified in the TypeServer2 record.
+ if (expectedInfo->getGuid() != typeServerDependency.getGuid())
+ return createFileError(
+ tsPath,
+ make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
- auto it = TypeServerSource::instances.find(*p);
- // The PDB file exists on disk, at this point we expect it to have been
- // inserted in the map by TypeServerSource::loadPDB()
- assert(it != TypeServerSource::instances.end());
+ return &tsSrc->tsIndexMap;
+}
+
+static bool equalsPath(StringRef path1, StringRef path2) {
+#if defined(_WIN32)
+ return path1.equals_lower(path2);
+#else
+ return path1.equals(path2);
+#endif
+}
- std::pair<std::string, TypeServerSource *> &pdb = it->second;
+// Find by name an OBJ provided on the command line
+static PrecompSource *findObjByName(StringRef fileNameOnly) {
+ SmallString<128> currentPath;
+ for (auto kv : PrecompSource::mappings) {
+ StringRef currentFileName = sys::path::filename(kv.second->file->getName(),
+ sys::path::Style::windows);
+
+ // Compare based solely on the file name (link.exe behavior)
+ if (equalsPath(currentFileName, fileNameOnly))
+ return kv.second;
+ }
+ return nullptr;
+}
- if (!pdb.second)
+Expected<const CVIndexMap *> findPrecompMap(ObjFile *file, PrecompRecord &pr) {
+ // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
+ // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
+ // the paths embedded in the OBJs are in the Windows format.
+ SmallString<128> prFileName =
+ sys::path::filename(pr.getPrecompFilePath(), sys::path::Style::windows);
+
+ PrecompSource *precomp;
+ auto it = PrecompSource::mappings.find(pr.getSignature());
+ if (it != PrecompSource::mappings.end()) {
+ precomp = it->second;
+ } else {
+ // Lookup by name
+ precomp = findObjByName(prFileName);
+ }
+
+ if (!precomp)
return createFileError(
- *p, createStringError(inconvertibleErrorCode(), pdb.first.c_str()));
+ prFileName,
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- pdb::PDBFile &pdbFile = (pdb.second)->session->getPDBFile();
- pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
+ if (pr.getSignature() != file->pchSignature)
+ return createFileError(
+ toString(file),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- // Just because a file with a matching name was found doesn't mean it can be
- // used. The GUID must match between the PDB header and the OBJ
- // TypeServer2 record. The 'Age' is used by MSVC incremental compilation.
- if (info.getGuid() != ts.getGuid())
+ if (pr.getSignature() != *precomp->file->pchSignature)
return createFileError(
- ts.Name,
- make_error<pdb::PDBError>(pdb::pdb_error_code::signature_out_of_date));
+ toString(precomp->file),
+ make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
- return pdb.second;
+ return &precomp->precompIndexMap;
}
-// FIXME: Temporary interface until PDBLinker::maybeMergeTypeServerPDB() is
-// moved here.
-Expected<llvm::pdb::NativeSession *> findTypeServerSource(const ObjFile *f) {
- Expected<TypeServerSource *> ts = TypeServerSource::findFromFile(f);
- if (!ts)
- return ts.takeError();
- return ts.get()->session.get();
+/// Merges a precompiled headers TPI map into the current TPI map. The
+/// precompiled headers object will also be loaded and remapped in the
+/// process.
+static Expected<const CVIndexMap *>
+mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *indexMap,
+ PrecompRecord &precomp) {
+ auto e = findPrecompMap(file, precomp);
+ if (!e)
+ return e.takeError();
+
+ const CVIndexMap *precompIndexMap = *e;
+ assert(precompIndexMap->isPrecompiledTypeMap);
+
+ if (precompIndexMap->tpiMap.empty())
+ return precompIndexMap;
+
+ assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
+ assert(precomp.getTypesCount() <= precompIndexMap->tpiMap.size());
+ // Use the previously remapped index map from the precompiled headers.
+ indexMap->tpiMap.append(precompIndexMap->tpiMap.begin(),
+ precompIndexMap->tpiMap.begin() +
+ precomp.getTypesCount());
+ return indexMap;
}
-// Queue a PDB type server for loading in the COFF Driver
-void TypeServerSource::enqueue(const ObjFile *dependentFile,
- const TypeServer2Record &ts) {
- // Start by finding where the PDB is located (either the record path or next
- // to the OBJ file)
- Optional<std::string> p = findPdbPath(ts.Name, dependentFile);
- if (!p)
- return;
- auto it = TypeServerSource::instances.emplace(
- *p, std::pair<std::string, TypeServerSource *>{});
- if (!it.second)
- return; // another OBJ already scheduled this PDB for load
-
- driver->enqueuePath(*p, false, false);
+Expected<const CVIndexMap *>
+UsePrecompSource::mergeDebugT(TypeMerger *m, CVIndexMap *indexMap) {
+ // This object was compiled with /Yu, so process the corresponding
+ // precompiled headers object (/Yc) first. Some type indices in the current
+ // object are referencing data in the precompiled headers object, so we need
+ // both to be loaded.
+ auto e = mergeInPrecompHeaderObj(file, indexMap, precompDependency);
+ if (!e)
+ return e.takeError();
+
+ // Drop LF_PRECOMP record from the input stream, as it has been replaced
+ // with the precompiled headers Type stream in the mergeInPrecompHeaderObj()
+ // call above. Note that we can't just call Types.drop_front(), as we
+ // explicitly want to rebase the stream.
+ CVTypeArray types;
+ BinaryStreamReader reader(file->debugTypes, support::little);
+ cantFail(reader.readArray(types, reader.getLength()));
+ auto firstType = types.begin();
+ file->debugTypes = file->debugTypes.drop_front(firstType->RecordData.size());
+
+ return TpiSource::mergeDebugT(m, indexMap);
}
-// Create an instance of TypeServerSource or an error string if the PDB couldn't
-// be loaded. The error message will be displayed later, when the referring OBJ
-// will be merged in. NOTE - a PDB load failure is not a link error: some
-// debug info will simply be missing from the final PDB - that is the default
-// accepted behavior.
-void loadTypeServerSource(llvm::MemoryBufferRef m) {
- std::string path = normalizePdbPath(m.getBufferIdentifier());
-
- Expected<TypeServerSource *> ts = TypeServerSource::getInstance(m);
- if (!ts)
- TypeServerSource::instances[path] = {toString(ts.takeError()), nullptr};
- else
- TypeServerSource::instances[path] = {{}, *ts};
+Expected<const CVIndexMap *> PrecompSource::mergeDebugT(TypeMerger *m,
+ CVIndexMap *) {
+ // Note that we're not using the provided CVIndexMap. Instead, we use our
+ // local one. Precompiled headers objects need to save the index map for
+ // further reference by other objects which use the precompiled headers.
+ return TpiSource::mergeDebugT(m, &precompIndexMap);
}
-Expected<TypeServerSource *> TypeServerSource::getInstance(MemoryBufferRef m) {
- std::unique_ptr<llvm::pdb::IPDBSession> iSession;
- Error err = pdb::NativeSession::createFromPdb(
- MemoryBuffer::getMemBuffer(m, false), iSession);
- if (err)
- return std::move(err);
-
- std::unique_ptr<llvm::pdb::NativeSession> session(
- static_cast<pdb::NativeSession *>(iSession.release()));
-
- pdb::PDBFile &pdbFile = session->getPDBFile();
- Expected<pdb::InfoStream &> info = pdbFile.getPDBInfoStream();
- // All PDB Files should have an Info stream.
- if (!info)
- return info.takeError();
- return make<TypeServerSource>(m, session.release());
+uint32_t TpiSource::countTypeServerPDBs() {
+ return TypeServerSource::mappings.size();
}
-} // namespace coff
-} // namespace lld
+uint32_t TpiSource::countPrecompObjs() {
+ return PrecompSource::mappings.size();
+}
+
+void TpiSource::clear() {
+ gc.clear();
+ TypeServerSource::mappings.clear();
+ PrecompSource::mappings.clear();
+}
namespace coff {
class ObjFile;
+class PDBInputFile;
+struct CVIndexMap;
+class TypeMerger;
class TpiSource {
public:
enum TpiKind { Regular, PCH, UsingPCH, PDB, UsingPDB };
- TpiSource(TpiKind k, const ObjFile *f);
- virtual ~TpiSource() {}
+ TpiSource(TpiKind k, ObjFile *f);
+ virtual ~TpiSource();
- const TpiKind kind;
- const ObjFile *file;
-};
+ /// Produce a mapping from the type and item indices used in the object
+ /// file to those in the destination PDB.
+ ///
+ /// If the object file uses a type server PDB (compiled with /Zi), merge TPI
+ /// and IPI from the type server PDB and return a map for it. Each unique type
+ /// server PDB is merged at most once, so this may return an existing index
+ /// mapping.
+ ///
+ /// If the object does not use a type server PDB (compiled with /Z7), we merge
+ /// all the type and item records from the .debug$S stream and fill in the
+ /// caller-provided ObjectIndexMap.
+ virtual llvm::Expected<const CVIndexMap *> mergeDebugT(TypeMerger *m,
+ CVIndexMap *indexMap);
+ /// Is this a dependent file that needs to be processed first, before other
+ /// OBJs?
+ virtual bool isDependency() const { return false; }
+
+ static void forEachSource(llvm::function_ref<void(TpiSource *)> fn);
-TpiSource *makeTpiSource(const ObjFile *f);
-TpiSource *makeUseTypeServerSource(const ObjFile *f,
- const llvm::codeview::TypeServer2Record *ts);
-TpiSource *makePrecompSource(const ObjFile *f);
-TpiSource *makeUsePrecompSource(const ObjFile *f,
- const llvm::codeview::PrecompRecord *precomp);
+ static uint32_t countTypeServerPDBs();
+ static uint32_t countPrecompObjs();
-void loadTypeServerSource(llvm::MemoryBufferRef m);
+ /// Clear global data structures for TpiSources.
+ static void clear();
-// Temporary interface to get the dependency
-template <typename T> const T &retrieveDependencyInfo(const TpiSource *source);
+ const TpiKind kind;
+ ObjFile *file;
+};
-// Temporary interface until we move PDBLinker::maybeMergeTypeServerPDB here
-llvm::Expected<llvm::pdb::NativeSession *>
-findTypeServerSource(const ObjFile *f);
+TpiSource *makeTpiSource(ObjFile *file);
+TpiSource *makeTypeServerSource(PDBInputFile *pdbInputFile);
+TpiSource *makeUseTypeServerSource(ObjFile *file,
+ llvm::codeview::TypeServer2Record ts);
+TpiSource *makePrecompSource(ObjFile *file);
+TpiSource *makeUsePrecompSource(ObjFile *file,
+ llvm::codeview::PrecompRecord ts);
} // namespace coff
} // namespace lld
-#endif
\ No newline at end of file
+#endif
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TarWriter.h"
ImportFile::instances.clear();
BitcodeFile::instances.clear();
memset(MergeChunk::instances, 0, sizeof(MergeChunk::instances));
+ TpiSource::clear();
+
return !errorCount();
}
symtab->addFile(make<ObjFile>(mbref));
break;
case file_magic::pdb:
- loadTypeServerSource(mbref);
+ symtab->addFile(make<PDBInputFile>(mbref));
break;
case file_magic::coff_cl_gl_object:
error(filename + ": is not a native COFF file. Recompile without /GL");
}
void LinkerDriver::enqueuePath(StringRef path, bool wholeArchive, bool lazy) {
- auto future =
- std::make_shared<std::future<MBErrPair>>(createFutureForFile(path));
- std::string pathStr = path;
+ auto future = std::make_shared<std::future<MBErrPair>>(
+ createFutureForFile(std::string(path)));
+ std::string pathStr = std::string(path);
enqueueTask([=]() {
auto mbOrErr = future->get();
if (mbOrErr.second) {
// the option `/nodefaultlib` than a reference to a file in the root
// directory.
std::string nearest;
- if (COFFOptTable().findNearest(pathStr, nearest) > 1)
+ if (optTable.findNearest(pathStr, nearest) > 1)
error(msg);
else
error(msg + "; did you mean '" + nearest + "'");
ArgParser parser;
// .drectve is always tokenized using Windows shell rules.
// /EXPORT: option can appear too many times, processing in fastpath.
- opt::InputArgList args;
- std::vector<StringRef> exports;
- std::tie(args, exports) = parser.parseDirectives(s);
+ ParsedDirectives directives = parser.parseDirectives(s);
- for (StringRef e : exports) {
+ for (StringRef e : directives.exports) {
// If a common header file contains dllexported function
// declarations, many object files may end up with having the
// same /EXPORT options. In order to save cost of parsing them,
config->exports.push_back(exp);
}
- for (auto *arg : args) {
+ // Handle /include: in bulk.
+ for (StringRef inc : directives.includes)
+ addUndefined(inc);
+
+ for (auto *arg : directives.args) {
switch (arg->getOption().getID()) {
case OPT_aligncomm:
parseAligncomm(arg->getValue());
}
if (path.endswith_lower(".lib"))
- visitedLibs.insert(sys::path::filename(path));
+ visitedLibs.insert(std::string(sys::path::filename(path)));
return path;
}
break;
case OPT_implib:
case OPT_pdb:
+ case OPT_pdbstripped:
case OPT_out:
os << arg->getSpelling() << sys::path::filename(arg->getValue()) << "\n";
break;
for (StringRef path : filePaths)
os << quote(relativeToRoot(path)) << "\n";
- return data.str();
+ return std::string(data.str());
}
enum class DebugKind { Unknown, None, Full, FastLink, GHash, Dwarf, Symtab };
return debugTypes;
}
-static std::string getMapFile(const opt::InputArgList &args) {
- auto *arg = args.getLastArg(OPT_lldmap, OPT_lldmap_file);
+static std::string getMapFile(const opt::InputArgList &args,
+ opt::OptSpecifier os, opt::OptSpecifier osFile) {
+ auto *arg = args.getLastArg(os, osFile);
if (!arg)
return "";
- if (arg->getOption().getID() == OPT_lldmap_file)
+ if (arg->getOption().getID() == osFile.getID())
return arg->getValue();
- assert(arg->getOption().getID() == OPT_lldmap);
+ assert(arg->getOption().getID() == os.getID());
StringRef outFile = config->outputFile;
return (outFile.substr(0, outFile.rfind('.')) + ".map").str();
}
static std::string getImplibPath() {
if (!config->implib.empty())
- return config->implib;
+ return std::string(config->implib);
SmallString<128> out = StringRef(config->outputFile);
sys::path::replace_extension(out, ".lib");
- return out.str();
+ return std::string(out.str());
}
// The import name is calculated as follows:
(config->dll || asLib) ? ".dll" : ".exe");
}
- return out.str();
+ return std::string(out.str());
}
static void createImportLibrary(bool asLib) {
std::vector<COFFShortExport> exports;
for (Export &e1 : config->exports) {
COFFShortExport e2;
- e2.Name = e1.name;
- e2.SymbolName = e1.symbolName;
- e2.ExtName = e1.extName;
+ e2.Name = std::string(e1.name);
+ e2.SymbolName = std::string(e1.symbolName);
+ e2.ExtName = std::string(e1.extName);
e2.Ordinal = e1.ordinal;
e2.Noname = e1.noname;
e2.Data = e1.data;
mb->getMemBufferRef(), config->machine, config->mingw));
if (config->outputFile.empty())
- config->outputFile = saver.save(m.OutputFile);
- config->importName = saver.save(m.ImportName);
+ config->outputFile = std::string(saver.save(m.OutputFile));
+ config->importName = std::string(saver.save(m.ImportName));
if (m.ImageBase)
config->imageBase = m.ImageBase;
if (m.StackReserve)
// and set as "ExtName = Name". If Name has the form "OtherDll.Func",
// it shouldn't be a normal exported function but a forward to another
// DLL instead. This is supported by both MS and GNU linkers.
- if (e1.ExtName != e1.Name && StringRef(e1.Name).contains('.')) {
+ if (!e1.ExtName.empty() && e1.ExtName != e1.Name &&
+ StringRef(e1.Name).contains('.')) {
e2.name = saver.save(e1.ExtName);
e2.forwardTo = saver.save(e1.Name);
config->exports.push_back(e2);
// All symbols that were not present in a given order file are
// considered to have the lowest priority 0 and are placed at
// end of an output section.
- for (std::string s : args::getLines(mb->getMemBufferRef())) {
+ for (StringRef arg : args::getLines(mb->getMemBufferRef())) {
+ std::string s(arg);
if (config->machine == I386 && !isDecorated(s))
s = "_" + s;
if (auto *arg = args.getLastArg(OPT_linkrepro)) {
SmallString<64> path = StringRef(arg->getValue());
sys::path::append(path, "repro.tar");
- return path.str().str();
+ return std::string(path);
}
return None;
}
void LinkerDriver::link(ArrayRef<const char *> argsArr) {
+ ScopedTimer rootTimer(Timer::root());
+
// Needed for LTO.
InitializeAllTargetInfos();
InitializeAllTargets();
return;
}
- lld::threadsEnabled = args.hasFlag(OPT_threads, OPT_threads_no, true);
+ // /threads: takes a positive integer and provides the default value for
+ // /opt:lldltojobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v.str();
+ }
if (args.hasArg(OPT_show_timing))
config->showTiming = true;
config->showSummary = args.hasArg(OPT_summary);
- ScopedTimer t(Timer::root());
// Handle --version, which is an lld extension. This option is a bit odd
// because it doesn't start with "/", but we deliberately chose "--" to
// avoid conflict with /version and for compatibility with clang-cl.
config->pdbAltPath = arg->getValue();
if (args.hasArg(OPT_natvis))
config->natvisFiles = args.getAllArgValues(OPT_natvis);
+ if (args.hasArg(OPT_pdbstream)) {
+ for (const StringRef value : args.getAllArgValues(OPT_pdbstream)) {
+ const std::pair<StringRef, StringRef> nameFile = value.split("=");
+ const StringRef name = nameFile.first;
+ const std::string file = nameFile.second.str();
+ config->namedStreams[name] = file;
+ }
+ }
if (auto *arg = args.getLastArg(OPT_pdb_source_path))
config->pdbSourcePath = arg->getValue();
}
+ // Handle /pdbstripped
+ if (args.hasArg(OPT_pdbstripped))
+ warn("ignoring /pdbstripped flag, it is not yet supported");
+
// Handle /noentry
if (args.hasArg(OPT_noentry)) {
if (args.hasArg(OPT_dll))
error("/opt:lldlto: invalid optimization level: " + optLevel);
} else if (s.startswith("lldltojobs=")) {
StringRef jobs = s.substr(11);
- if (jobs.getAsInteger(10, config->thinLTOJobs) ||
- config->thinLTOJobs == 0)
+ if (!get_threadpool_strategy(jobs))
error("/opt:lldltojobs: invalid job count: " + jobs);
+ config->thinLTOJobs = jobs.str();
} else if (s.startswith("lldltopartitions=")) {
StringRef n = s.substr(17);
if (n.getAsInteger(10, config->ltoPartitions) ||
!args.hasArg(OPT_profile));
config->integrityCheck =
args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
+ config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
for (auto *arg : args.filtered(OPT_swaprun))
parseSwaprun(arg->getValue());
config->debugDwarf = debug == DebugKind::Dwarf;
config->debugGHashes = debug == DebugKind::GHash;
config->debugSymtab = debug == DebugKind::Symtab;
+ config->autoImport =
+ args.hasFlag(OPT_auto_import, OPT_auto_import_no, config->mingw);
+ config->pseudoRelocs = args.hasFlag(
+ OPT_runtime_pseudo_reloc, OPT_runtime_pseudo_reloc_no, config->mingw);
// Don't warn about long section names, such as .debug_info, for mingw or when
// -debug:dwarf is requested.
if (config->mingw || config->debugDwarf)
config->warnLongSectionNames = false;
- config->mapFile = getMapFile(args);
+ config->lldmapFile = getMapFile(args, OPT_lldmap, OPT_lldmap_file);
+ config->mapFile = getMapFile(args, OPT_map, OPT_map_file);
+
+ if (config->lldmapFile != "" && config->lldmapFile == config->mapFile) {
+ warn("/lldmap and /map have the same output file '" + config->mapFile +
+ "'.\n>>> ignoring /lldmap");
+ config->lldmapFile.clear();
+ }
if (config->incremental && args.hasArg(OPT_profile)) {
warn("ignoring '/incremental' due to '/profile' specification");
config->wordsize = config->is64() ? 8 : 4;
// Handle /safeseh, x86 only, on by default, except for mingw.
- if (config->machine == I386 &&
- args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw))
- config->safeSEH = true;
+ if (config->machine == I386) {
+ config->safeSEH = args.hasFlag(OPT_safeseh, OPT_safeseh_no, !config->mingw);
+ config->noSEH = args.hasArg(OPT_noseh);
+ }
// Handle /functionpadmin
for (auto *arg : args.filtered(OPT_functionpadmin, OPT_functionpadmin_opt))
// Needed for MSVC 2017 15.5 CRT.
symtab->addAbsolute(mangle("__enclave_config"), 0);
- if (config->mingw) {
+ if (config->pseudoRelocs) {
symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
symtab->addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
+ }
+ if (config->mingw) {
symtab->addAbsolute(mangle("__CTOR_LIST__"), 0);
symtab->addAbsolute(mangle("__DTOR_LIST__"), 0);
}
while (run());
}
- if (config->mingw) {
+ if (config->autoImport) {
+ // MinGW specific.
// Load any further object files that might be needed for doing automatic
// imports.
//
writeResult();
// Stop early so we can print the results.
- Timer::root().stop();
+ rootTimer.stop();
if (config->showTiming)
Timer::root().print();
}
COFFOptTable();
};
+// Constructing the option table is expensive. Use a global table to avoid doing
+// it more than once.
+extern COFFOptTable optTable;
+
+// The result of parsing the .drective section. The /export: and /include:
+// options are handled separately because they reference symbols, and the number
+// of symbols can be quite large. The LLVM Option library will perform at least
+// one memory allocation per argument, and that is prohibitively slow for
+// parsing directives.
+struct ParsedDirectives {
+ std::vector<StringRef> exports;
+ std::vector<StringRef> includes;
+ llvm::opt::InputArgList args;
+};
+
class ArgParser {
public:
// Parses command line options.
// Tokenizes a given string and then parses as command line options in
// .drectve section. /EXPORT options are returned in second element
// to be processed in fastpath.
- std::pair<llvm::opt::InputArgList, std::vector<StringRef>>
- parseDirectives(StringRef s);
+ ParsedDirectives parseDirectives(StringRef s);
private:
// Concatenate LINK environment variable.
void addLINK(SmallVector<const char *, 256> &argv);
std::vector<const char *> tokenize(StringRef s);
-
- COFFOptTable table;
};
class LinkerDriver {
void enqueueArchiveMember(const Archive::Child &c, const Archive::Symbol &sym,
StringRef parentName);
+ void enqueuePDB(StringRef Path) { enqueuePath(Path, false, false); }
+
MemoryBufferRef takeBuffer(std::unique_ptr<MemoryBuffer> mb);
void enqueuePath(StringRef path, bool wholeArchive, bool lazy);
error("/aligncomm: invalid argument: " + s);
return;
}
- config->alignComm[name] = std::max(config->alignComm[name], 1 << v);
+ config->alignComm[std::string(name)] =
+ std::max(config->alignComm[std::string(name)], 1 << v);
}
// Parses /functionpadmin option argument.
SmallString<128> s;
if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s))
fatal("cannot create a temporary file: " + ec.message());
- path = s.str();
+ path = std::string(s.str());
if (!contents.empty()) {
std::error_code ec;
toString(std::move(e)));
}
- return merger.getMergedManifest().get()->getBuffer();
+ return std::string(merger.getMergedManifest().get()->getBuffer());
}
static std::string createManifestXmlWithExternalMt(StringRef defaultXml) {
e.add("/out:" + StringRef(user.path));
e.run();
- return CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
- .get()
- ->getBuffer();
+ return std::string(
+ CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)
+ .get()
+ ->getBuffer());
}
static std::string createManifestXml() {
}
void createSideBySideManifest() {
- std::string path = config->manifestFile;
+ std::string path = std::string(config->manifestFile);
if (path == "")
path = config->outputFile + ".manifest";
std::error_code ec;
COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {}
+COFFOptTable optTable;
+
// Set color diagnostics according to --color-diagnostics={auto,always,never}
// or --no-color-diagnostics flags.
static void handleColorDiagnostics(opt::InputArgList &args) {
// options so we parse here before and ignore all the options but
// --rsp-quoting and /lldignoreenv.
// (This means --rsp-quoting can't be added through %LINK%.)
- opt::InputArgList args = table.ParseArgs(argv, missingIndex, missingCount);
-
+ opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount);
// Expand response files (arguments in the form of @<filename>) and insert
// flags from %LINK% and %_LINK_%, and then parse the argument again.
if (!args.hasArg(OPT_lldignoreenv))
addLINK(expandedArgv);
cl::ExpandResponseFiles(saver, getQuotingStyle(args), expandedArgv);
- args = table.ParseArgs(makeArrayRef(expandedArgv).drop_front(), missingIndex,
- missingCount);
+ args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(),
+ missingIndex, missingCount);
// Print the real command line if response files are expanded.
if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) {
for (auto *arg : args.filtered(OPT_UNKNOWN)) {
std::string nearest;
- if (table.findNearest(arg->getAsString(args), nearest) > 1)
+ if (optTable.findNearest(arg->getAsString(args), nearest) > 1)
warn("ignoring unknown argument '" + arg->getAsString(args) + "'");
else
warn("ignoring unknown argument '" + arg->getAsString(args) +
}
// Tokenizes and parses a given string as command line in .drective section.
-// /EXPORT options are processed in fastpath.
-std::pair<opt::InputArgList, std::vector<StringRef>>
-ArgParser::parseDirectives(StringRef s) {
- std::vector<StringRef> exports;
+ParsedDirectives ArgParser::parseDirectives(StringRef s) {
+ ParsedDirectives result;
SmallVector<const char *, 16> rest;
- for (StringRef tok : tokenize(s)) {
+ // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for
+ // potentially every symbol in the object, so they must be handled quickly.
+ SmallVector<StringRef, 16> tokens;
+ cl::TokenizeWindowsCommandLineNoCopy(s, saver, tokens);
+ for (StringRef tok : tokens) {
if (tok.startswith_lower("/export:") || tok.startswith_lower("-export:"))
- exports.push_back(tok.substr(strlen("/export:")));
- else
- rest.push_back(tok.data());
+ result.exports.push_back(tok.substr(strlen("/export:")));
+ else if (tok.startswith_lower("/include:") ||
+ tok.startswith_lower("-include:"))
+ result.includes.push_back(tok.substr(strlen("/include:")));
+ else {
+ // Save non-null-terminated strings to make proper C strings.
+ bool HasNul = tok.data()[tok.size()] == '\0';
+ rest.push_back(HasNul ? tok.data() : saver.save(tok).data());
+ }
}
// Make InputArgList from unparsed string vectors.
unsigned missingIndex;
unsigned missingCount;
- opt::InputArgList args = table.ParseArgs(rest, missingIndex, missingCount);
+ result.args = optTable.ParseArgs(rest, missingIndex, missingCount);
if (missingCount)
- fatal(Twine(args.getArgString(missingIndex)) + ": missing argument");
- for (auto *arg : args.filtered(OPT_UNKNOWN))
- warn("ignoring unknown argument: " + arg->getAsString(args));
- return {std::move(args), std::move(exports)};
+ fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument");
+ for (auto *arg : result.args.filtered(OPT_UNKNOWN))
+ warn("ignoring unknown argument: " + arg->getAsString(result.args));
+ return result;
}
// link.exe has an interesting feature. If LINK or _LINK_ environment
}
void printHelp(const char *argv0) {
- COFFOptTable().PrintHelp(lld::outs(),
- (std::string(argv0) + " [options] file...").c_str(),
- "LLVM Linker", false);
+ optTable.PrintHelp(lld::outs(),
+ (std::string(argv0) + " [options] file...").c_str(),
+ "LLVM Linker", false);
}
} // namespace coff
#include "Chunks.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Support/Debug.h"
// Returns true if two sections' associative children are equal.
bool ICF::assocEquals(const SectionChunk *a, const SectionChunk *b) {
- auto childClasses = [&](const SectionChunk *sc) {
- std::vector<uint32_t> classes;
- for (const SectionChunk &c : sc->children())
- if (!c.getSectionName().startswith(".debug") &&
- c.getSectionName() != ".gfids$y" && c.getSectionName() != ".gljmp$y")
- classes.push_back(c.eqClass[cnt % 2]);
- return classes;
+ // Ignore associated metadata sections that don't participate in ICF, such as
+ // debug info and CFGuard metadata.
+ auto considerForICF = [](const SectionChunk &assoc) {
+ StringRef Name = assoc.getSectionName();
+ return !(Name.startswith(".debug") || Name == ".gfids$y" ||
+ Name == ".gljmp$y");
};
- return childClasses(a) == childClasses(b);
+ auto ra = make_filter_range(a->children(), considerForICF);
+ auto rb = make_filter_range(b->children(), considerForICF);
+ return std::equal(ra.begin(), ra.end(), rb.begin(), rb.end(),
+ [&](const SectionChunk &ia, const SectionChunk &ib) {
+ return ia.eqClass[cnt % 2] == ib.eqClass[cnt % 2];
+ });
}
// Compare "non-moving" part of two sections, namely everything
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
+#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
#include "llvm/LTO/LTO.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/COFF.h"
using namespace llvm::codeview;
using namespace llvm::object;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::coff;
using llvm::Triple;
using llvm::support::ulittle32_t;
-namespace lld {
-
// Returns the last element of a path, which is supposed to be a filename.
static StringRef getBasename(StringRef path) {
return sys::path::filename(path, sys::path::Style::windows);
}
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
-std::string toString(const coff::InputFile *file) {
+std::string lld::toString(const coff::InputFile *file) {
if (!file)
return "<internal>";
if (file->parentName.empty() || file->kind() == coff::InputFile::ImportKind)
- return file->getName();
+ return std::string(file->getName());
return (getBasename(file->parentName) + "(" + getBasename(file->getName()) +
")")
.str();
}
-namespace coff {
-
std::vector<ObjFile *> ObjFile::instances;
+std::map<std::string, PDBInputFile *> PDBInputFile::instances;
std::vector<ImportFile *> ImportFile::instances;
std::vector<BitcodeFile *> BitcodeFile::instances;
driver->enqueueArchiveMember(c, sym, getName());
}
-std::vector<MemoryBufferRef> getArchiveMembers(Archive *file) {
+std::vector<MemoryBufferRef> lld::coff::getArchiveMembers(Archive *file) {
std::vector<MemoryBufferRef> v;
Error err = Error::success();
for (const Archive::Child &c : file->children(err)) {
if (coffSym.isUndefined() || !coffSym.isExternal() ||
coffSym.isWeakExternal())
continue;
- StringRef name;
- coffObj->getSymbolName(coffSym, name);
+ StringRef name = check(coffObj->getSymbolName(coffSym));
if (coffSym.isAbsolute() && ignoredSymbolName(name))
continue;
symtab->addLazyObject(this, name);
initializeDependencies();
}
-const coff_section* ObjFile::getSection(uint32_t i) {
- const coff_section *sec;
- if (auto ec = coffObj->getSection(i, sec))
- fatal("getSection failed: #" + Twine(i) + ": " + ec.message());
- return sec;
+const coff_section *ObjFile::getSection(uint32_t i) {
+ auto sec = coffObj->getSection(i);
+ if (!sec)
+ fatal("getSection failed: #" + Twine(i) + ": " + toString(sec.takeError()));
+ return *sec;
}
// We set SectionChunk pointers in the SparseChunks vector to this value
void ObjFile::initializeChunks() {
uint32_t numSections = coffObj->getNumberOfSections();
- chunks.reserve(numSections);
sparseChunks.resize(numSections + 1);
for (uint32_t i = 1; i < numSections + 1; ++i) {
const coff_section *sec = getSection(i);
else if (name == ".gljmp$y")
guardLJmpChunks.push_back(c);
else if (name == ".sxdata")
- sXDataChunks.push_back(c);
+ sxDataChunks.push_back(c);
else if (config->tailMerge && sec->NumberOfRelocations == 0 &&
name == ".rdata" && leaderName.startswith("??_C@"))
// COFF sections that look like string literal sections (i.e. no
int32_t sectionNumber = sym.getSectionNumber();
auto diag = [&]() {
- StringRef name, parentName;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
+ StringRef parentName;
const coff_section *parentSec = getSection(parentIndex);
if (Expected<StringRef> e = coffObj->getSectionName(parentSec))
parentName = *e;
// of the section chunk we actually include instead of discarding it,
// add the symbol to a map to allow using it for implicitly
// associating .[px]data$<func> sections to it.
+ // Use the suffix from the .text$<func> instead of the leader symbol
+ // name, for cases where the names differ (i386 mangling/decorations,
+ // cases where the leader is a weak symbol named .weak.func.default*).
int32_t sectionNumber = sym.getSectionNumber();
SectionChunk *sc = sparseChunks[sectionNumber];
if (sc && sc->getOutputCharacteristics() & IMAGE_SCN_MEM_EXECUTE) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
- if (getMachineType() == I386)
- name.consume_front("_");
+ StringRef name = sc->getSectionName().split('$').second;
prevailingSectionMap[name] = sectionNumber;
}
}
void ObjFile::maybeAssociateSEHForMingw(
COFFSymbolRef sym, const coff_aux_section_definition *def,
const DenseMap<StringRef, uint32_t> &prevailingSectionMap) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
if (name.consume_front(".pdata$") || name.consume_front(".xdata$") ||
name.consume_front(".eh_frame$")) {
// For MinGW, treat .[px]data$<func> and .eh_frame$<func> as implicitly
Symbol *ObjFile::createRegular(COFFSymbolRef sym) {
SectionChunk *sc = sparseChunks[sym.getSectionNumber()];
if (sym.isExternal()) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
if (sc)
return symtab->addRegular(this, name, sym.getGeneric(), sc,
sym.getValue());
maybeAssociateSEHForMingw(sym, def, prevailingSectionMap);
}
if (sparseChunks[sym.getSectionNumber()] == pendingComdat) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
log("comdat section " + name +
" without leader and unassociated, discarding");
continue;
uint32_t idx = kv.second;
checkAndSetWeakAlias(symtab, this, sym, symbols[idx]);
}
+
+ // Free the memory used by sparseChunks now that symbol loading is finished.
+ decltype(sparseChunks)().swap(sparseChunks);
}
Symbol *ObjFile::createUndefined(COFFSymbolRef sym) {
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
return symtab->addUndefined(name, this, sym.isWeakExternal());
}
case IMAGE_COMDAT_SELECT_LARGEST:
if (leaderChunk->getSize() < getSection(sym)->SizeOfRawData) {
// Replace the existing comdat symbol with the new one.
- StringRef name;
- coffObj->getSymbolName(sym, name);
+ StringRef name = check(coffObj->getSymbolName(sym));
// FIXME: This is incorrect: With /opt:noref, the previous sections
// make it into the final executable as well. Correct handling would
// be to undo reading of the whole old section that's being replaced,
std::vector<const coff_aux_section_definition *> &comdatDefs,
bool &prevailing) {
prevailing = false;
- auto getName = [&]() {
- StringRef s;
- coffObj->getSymbolName(sym, s);
- return s;
- };
+ auto getName = [&]() { return check(coffObj->getSymbolName(sym)); };
if (sym.isCommon()) {
auto *c = make<CommonChunk>(sym);
if (data.empty())
return;
+ // Get the first type record. It will indicate if this object uses a type
+ // server (/Zi) or a PCH file (/Yu).
CVTypeArray types;
BinaryStreamReader reader(data, support::little);
cantFail(reader.readArray(types, reader.getLength()));
-
CVTypeArray::Iterator firstType = types.begin();
if (firstType == types.end())
return;
// Remember the .debug$T or .debug$P section.
debugTypes = data;
+ // This object file is a PCH file that others will depend on.
if (isPCH) {
debugTypesObj = makePrecompSource(this);
return;
}
+ // This object file was compiled with /Zi. Enqueue the PDB dependency.
if (firstType->kind() == LF_TYPESERVER2) {
TypeServer2Record ts = cantFail(
TypeDeserializer::deserializeAs<TypeServer2Record>(firstType->data()));
- debugTypesObj = makeUseTypeServerSource(this, &ts);
+ debugTypesObj = makeUseTypeServerSource(this, ts);
+ PDBInputFile::enqueue(ts.getName(), this);
return;
}
+ // This object was compiled with /Yu. It uses types from another object file
+ // with a matching signature.
if (firstType->kind() == LF_PRECOMP) {
PrecompRecord precomp = cantFail(
TypeDeserializer::deserializeAs<PrecompRecord>(firstType->data()));
- debugTypesObj = makeUsePrecompSource(this, &precomp);
+ debugTypesObj = makeUsePrecompSource(this, precomp);
return;
}
+ // This is a plain old object file.
debugTypesObj = makeTpiSource(this);
}
+// Make a PDB path assuming the PDB is in the same folder as the OBJ
+static std::string getPdbBaseName(ObjFile *file, StringRef tSPath) {
+ StringRef localPath =
+ !file->parentName.empty() ? file->parentName : file->getName();
+ SmallString<128> path = sys::path::parent_path(localPath);
+
+ // Currently, type server PDBs are only created by MSVC cl, which only runs
+ // on Windows, so we can assume type server paths are Windows style.
+ sys::path::append(path,
+ sys::path::filename(tSPath, sys::path::Style::windows));
+ return std::string(path.str());
+}
+
+// The casing of the PDB path stamped in the OBJ can differ from the actual path
+// on disk. With this, we ensure to always use lowercase as a key for the
+// PDBInputFile::instances map, at least on Windows.
+static std::string normalizePdbPath(StringRef path) {
+#if defined(_WIN32)
+ return path.lower();
+#else // LINUX
+ return std::string(path);
+#endif
+}
+
+// If existing, return the actual PDB path on disk.
+static Optional<std::string> findPdbPath(StringRef pdbPath,
+ ObjFile *dependentFile) {
+ // Ensure the file exists before anything else. In some cases, if the path
+ // points to a removable device, Driver::enqueuePath() would fail with an
+ // error (EAGAIN, "resource unavailable try again") which we want to skip
+ // silently.
+ if (llvm::sys::fs::exists(pdbPath))
+ return normalizePdbPath(pdbPath);
+ std::string ret = getPdbBaseName(dependentFile, pdbPath);
+ if (llvm::sys::fs::exists(ret))
+ return normalizePdbPath(ret);
+ return None;
+}
+
+PDBInputFile::PDBInputFile(MemoryBufferRef m) : InputFile(PDBKind, m) {}
+
+PDBInputFile::~PDBInputFile() = default;
+
+PDBInputFile *PDBInputFile::findFromRecordPath(StringRef path,
+ ObjFile *fromFile) {
+ auto p = findPdbPath(path.str(), fromFile);
+ if (!p)
+ return nullptr;
+ auto it = PDBInputFile::instances.find(*p);
+ if (it != PDBInputFile::instances.end())
+ return it->second;
+ return nullptr;
+}
+
+void PDBInputFile::enqueue(StringRef path, ObjFile *fromFile) {
+ auto p = findPdbPath(path.str(), fromFile);
+ if (!p)
+ return;
+ auto it = PDBInputFile::instances.emplace(*p, nullptr);
+ if (!it.second)
+ return; // already scheduled for load
+ driver->enqueuePDB(*p);
+}
+
+void PDBInputFile::parse() {
+ PDBInputFile::instances[mb.getBufferIdentifier().str()] = this;
+
+ std::unique_ptr<pdb::IPDBSession> thisSession;
+ loadErr.emplace(pdb::NativeSession::createFromPdb(
+ MemoryBuffer::getMemBuffer(mb, false), thisSession));
+ if (*loadErr)
+ return; // fail silently at this point - the error will be handled later,
+ // when merging the debug type stream
+
+ session.reset(static_cast<pdb::NativeSession *>(thisSession.release()));
+
+ pdb::PDBFile &pdbFile = session->getPDBFile();
+ auto expectedInfo = pdbFile.getPDBInfoStream();
+ // All PDB Files should have an Info stream.
+ if (!expectedInfo) {
+ loadErr.emplace(expectedInfo.takeError());
+ return;
+ }
+ debugTypesObj = makeTypeServerSource(this);
+}
+
// Used only for DWARF debug info, which is not common (except in MinGW
// environments). This returns an optional pair of file name and line
// number for where the variable was defined.
return dwarf->getDILineInfo(offset, sectionIndex);
}
-StringRef ltrim1(StringRef s, const char *chars) {
+static StringRef ltrim1(StringRef s, const char *chars) {
if (!s.empty() && strchr(chars, s[0]))
return s.substr(1);
return s;
StringRef name = saver.save(StringRef(buf + sizeof(*hdr)));
StringRef impName = saver.save("__imp_" + name);
const char *nameStart = buf + sizeof(coff_import_header) + name.size() + 1;
- dllName = StringRef(nameStart);
+ dllName = std::string(StringRef(nameStart));
StringRef extName;
switch (hdr->getNameType()) {
case IMPORT_ORDINAL:
// filename unique.
MemoryBufferRef mbref(
mb.getBuffer(),
- saver.save(archiveName + path +
- (archiveName.empty() ? "" : utostr(offsetInArchive))));
+ saver.save(archiveName.empty() ? path
+ : archiveName + sys::path::filename(path) +
+ utostr(offsetInArchive)));
obj = check(lto::InputFile::create(mbref));
}
} else if (objSym.isWeak() && objSym.isIndirect()) {
// Weak external.
sym = symtab->addUndefined(symName, this, true);
- std::string fallback = objSym.getCOFFWeakExternalFallback();
+ std::string fallback = std::string(objSym.getCOFFWeakExternalFallback());
Symbol *alias = symtab->addUndefined(saver.save(fallback));
checkAndSetWeakAlias(symtab, this, sym, alias);
} else if (comdatIndex != -1) {
}
}
-std::string replaceThinLTOSuffix(StringRef path) {
+std::string lld::coff::replaceThinLTOSuffix(StringRef path) {
StringRef suffix = config->thinLTOObjectSuffixReplace.first;
StringRef repl = config->thinLTOObjectSuffixReplace.second;
if (path.consume_back(suffix))
return (path + repl).str();
- return path;
+ return std::string(path);
}
-
-} // namespace coff
-} // namespace lld
struct DILineInfo;
namespace pdb {
class DbiModuleDescriptorBuilder;
+class NativeSession;
}
namespace lto {
class InputFile;
ArchiveKind,
ObjectKind,
LazyObjectKind,
+ PDBKind,
ImportKind,
BitcodeKind
};
MachineTypes getMachineType() override;
ArrayRef<Chunk *> getChunks() { return chunks; }
ArrayRef<SectionChunk *> getDebugChunks() { return debugChunks; }
- ArrayRef<SectionChunk *> getSXDataChunks() { return sXDataChunks; }
+ ArrayRef<SectionChunk *> getSXDataChunks() { return sxDataChunks; }
ArrayRef<SectionChunk *> getGuardFidChunks() { return guardFidChunks; }
ArrayRef<SectionChunk *> getGuardLJmpChunks() { return guardLJmpChunks; }
ArrayRef<Symbol *> getSymbols() { return symbols; }
// Chunks containing symbol table indices of exception handlers. Only used for
// 32-bit x86.
- std::vector<SectionChunk *> sXDataChunks;
+ std::vector<SectionChunk *> sxDataChunks;
// Chunks containing symbol table indices of address taken symbols and longjmp
// targets. These are not linked into the final binary when /guard:cf is set.
std::vector<SectionChunk *> guardFidChunks;
std::vector<SectionChunk *> guardLJmpChunks;
- // This vector contains the same chunks as Chunks, but they are
- // indexed such that you can get a SectionChunk by section index.
- // Nonexistent section indices are filled with null pointers.
- // (Because section number is 1-based, the first slot is always a
- // null pointer.)
- std::vector<SectionChunk *> sparseChunks;
-
// This vector contains a list of all symbols defined or referenced by this
// file. They are indexed such that you can get a Symbol by symbol
// index. Nonexistent indices (which are occupied by auxiliary
// symbols in the real symbol table) are filled with null pointers.
std::vector<Symbol *> symbols;
+ // This vector contains the same chunks as Chunks, but they are
+ // indexed such that you can get a SectionChunk by section index.
+ // Nonexistent section indices are filled with null pointers.
+ // (Because section number is 1-based, the first slot is always a
+ // null pointer.) This vector is only valid during initialization.
+ std::vector<SectionChunk *> sparseChunks;
+
DWARFCache *dwarf = nullptr;
};
+// This is a PDB type server dependency, that is not a input file per se, but
+// needs to be treated like one. Such files are discovered from the debug type
+// stream.
+class PDBInputFile : public InputFile {
+public:
+ explicit PDBInputFile(MemoryBufferRef m);
+ ~PDBInputFile();
+ static bool classof(const InputFile *f) { return f->kind() == PDBKind; }
+ void parse() override;
+
+ static void enqueue(StringRef path, ObjFile *fromFile);
+
+ static PDBInputFile *findFromRecordPath(StringRef path, ObjFile *fromFile);
+
+ static std::map<std::string, PDBInputFile *> instances;
+
+ // Record possible errors while opening the PDB file
+ llvm::Optional<Error> loadErr;
+
+ // This is the actual interface to the PDB (if it was opened successfully)
+ std::unique_ptr<llvm::pdb::NativeSession> session;
+
+ // If the PDB has a .debug$T stream, this tells how it will be handled.
+ TpiSource *debugTypesObj = nullptr;
+};
+
// This type represents import library members that contain DLL names
// and symbols exported from the DLLs. See Microsoft PE/COFF spec. 7
// for details about the format.
--- /dev/null
+//===- LLDMapFile.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the /lldmap option. It shows lists in order and
+// hierarchically the output sections, input sections, input files and
+// symbol:
+//
+// Address Size Align Out File Symbol
+// 00201000 00000015 4 .text
+// 00201000 0000000e 4 test.o:(.text)
+// 0020100e 00000000 0 local
+// 00201005 00000000 0 f(int)
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDMapFile.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Writer.h"
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace llvm::object;
+using namespace lld;
+using namespace lld::coff;
+
+using SymbolMapTy =
+ DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
+
+static constexpr char indent8[] = " "; // 8 spaces
+static constexpr char indent16[] = " "; // 16 spaces
+
+// Print out the first three columns of a line.
+static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
+ uint64_t align) {
+ os << format("%08llx %08llx %5lld ", addr, size, align);
+}
+
+// Returns a list of all symbols that we want to print out.
+static std::vector<DefinedRegular *> getSymbols() {
+ std::vector<DefinedRegular *> v;
+ for (ObjFile *file : ObjFile::instances)
+ for (Symbol *b : file->getSymbols())
+ if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
+ if (sym && !sym->getCOFFSymbol().isSectionDefinition())
+ v.push_back(sym);
+ return v;
+}
+
+// Returns a map from sections to their symbols.
+static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
+ SymbolMapTy ret;
+ for (DefinedRegular *s : syms)
+ ret[s->getChunk()].push_back(s);
+
+ // Sort symbols by address.
+ for (auto &it : ret) {
+ SmallVectorImpl<DefinedRegular *> &v = it.second;
+ std::stable_sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
+ return a->getRVA() < b->getRVA();
+ });
+ }
+ return ret;
+}
+
+// Construct a map from symbols to their stringified representations.
+static DenseMap<DefinedRegular *, std::string>
+getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+ std::vector<std::string> str(syms.size());
+ parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
+ raw_string_ostream os(str[i]);
+ writeHeader(os, syms[i]->getRVA(), 0, 0);
+ os << indent16 << toString(*syms[i]);
+ });
+
+ DenseMap<DefinedRegular *, std::string> ret;
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ ret[syms[i]] = std::move(str[i]);
+ return ret;
+}
+
+void lld::coff::writeLLDMapFile(ArrayRef<OutputSection *> outputSections) {
+ if (config->lldmapFile.empty())
+ return;
+
+ std::error_code ec;
+ raw_fd_ostream os(config->lldmapFile, ec, sys::fs::OF_None);
+ if (ec)
+ fatal("cannot open " + config->lldmapFile + ": " + ec.message());
+
+ // Collect symbol info that we want to print out.
+ std::vector<DefinedRegular *> syms = getSymbols();
+ SymbolMapTy sectionSyms = getSectionSyms(syms);
+ DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+
+ // Print out the header line.
+ os << "Address Size Align Out In Symbol\n";
+
+ // Print out file contents.
+ for (OutputSection *sec : outputSections) {
+ writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
+ os << sec->name << '\n';
+
+ for (Chunk *c : sec->chunks) {
+ auto *sc = dyn_cast<SectionChunk>(c);
+ if (!sc)
+ continue;
+
+ writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
+ os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
+ << ")\n";
+ for (DefinedRegular *sym : sectionSyms[sc])
+ os << symStr[sym] << '\n';
+ }
+ }
+}
--- /dev/null
+//===- LLDMapFile.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_COFF_LLDMAPFILE_H
+#define LLD_COFF_LLDMAPFILE_H
+
+#include "llvm/ADT/ArrayRef.h"
+
+namespace lld {
+namespace coff {
+class OutputSection;
+void writeLLDMapFile(llvm::ArrayRef<OutputSection *> outputSections);
+}
+}
+
+#endif
using namespace llvm;
using namespace llvm::object;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
// Creates an empty file to and returns a raw_fd_ostream to write to it.
static std::unique_ptr<raw_fd_ostream> openFile(StringRef file) {
}
static std::string getThinLTOOutputFile(StringRef path) {
- return lto::getThinLTOOutputFile(path,
- config->thinLTOPrefixReplace.first,
- config->thinLTOPrefixReplace.second);
+ return lto::getThinLTOOutputFile(
+ std::string(path), std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second));
}
static lto::Config createConfig() {
c.CPU = getCPUStr();
c.MAttrs = getMAttrs();
c.CGOptLevel = args::getCGOptLevel(config->ltoo);
+ c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
if (config->saveTemps)
checkError(c.addSaveTemps(std::string(config->outputFile) + ".",
if (config->thinLTOIndexOnly) {
auto OnIndexWrite = [&](StringRef S) { thinIndices.erase(S); };
backend = lto::createWriteIndexesThinBackend(
- config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second),
config->thinLTOEmitImportsFiles, indexFile.get(), OnIndexWrite);
- } else if (config->thinLTOJobs != 0) {
- backend = lto::createInProcessThinBackend(config->thinLTOJobs);
+ } else {
+ backend = lto::createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
}
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
// Merge all the bitcode files we have seen, codegen the result
// and return the resulting objects.
-std::vector<StringRef> BitcodeCompiler::compile() {
+std::vector<InputFile *> BitcodeCompiler::compile() {
unsigned maxTasks = ltoObj->getMaxTasks();
buf.resize(maxTasks);
files.resize(maxTasks);
if (!config->ltoCache.empty())
pruneCache(config->ltoCache, config->ltoCachePolicy);
- std::vector<StringRef> ret;
+ std::vector<InputFile *> ret;
for (unsigned i = 0; i != maxTasks; ++i) {
- if (buf[i].empty())
+ // Assign unique names to LTO objects. This ensures they have unique names
+ // in the PDB if one is produced. The names should look like:
+ // - foo.exe.lto.obj
+ // - foo.exe.lto.1.obj
+ // - ...
+ StringRef ltoObjName =
+ saver.save(Twine(config->outputFile) + ".lto" +
+ (i == 0 ? Twine("") : Twine('.') + Twine(i)) + ".obj");
+
+ // Get the native object contents either from the cache or from memory. Do
+ // not use the cached MemoryBuffer directly, or the PDB will not be
+ // deterministic.
+ StringRef objBuf;
+ if (files[i])
+ objBuf = files[i]->getBuffer();
+ else
+ objBuf = buf[i];
+ if (objBuf.empty())
continue;
- if (config->saveTemps) {
- if (i == 0)
- saveBuffer(buf[i], config->outputFile + ".lto.obj");
- else
- saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.obj");
- }
- ret.emplace_back(buf[i].data(), buf[i].size());
- }
- for (std::unique_ptr<MemoryBuffer> &file : files)
- if (file)
- ret.push_back(file->getBuffer());
+ if (config->saveTemps)
+ saveBuffer(buf[i], ltoObjName);
+ ret.push_back(make<ObjFile>(MemoryBufferRef(objBuf, ltoObjName)));
+ }
return ret;
}
-
-} // namespace coff
-} // namespace lld
~BitcodeCompiler();
void add(BitcodeFile &f);
- std::vector<StringRef> compile();
+ std::vector<InputFile *> compile();
private:
std::unique_ptr<llvm::lto::LTO> ltoObj;
//
//===----------------------------------------------------------------------===//
//
-// This file implements the /lldmap option. It shows lists in order and
-// hierarchically the output sections, input sections, input files and
-// symbol:
+// This file implements the /map option in the same format as link.exe
+// (based on observations)
//
-// Address Size Align Out File Symbol
-// 00201000 00000015 4 .text
-// 00201000 0000000e 4 test.o:(.text)
-// 0020100e 00000000 0 local
-// 00201005 00000000 0 f(int)
+// Header (program name, timestamp info, preferred load address)
//
+// Section list (Start = Section index:Base address):
+// Start Length Name Class
+// 0001:00001000 00000015H .text CODE
+//
+// Symbols list:
+// Address Publics by Value Rva + Base Lib:Object
+// 0001:00001000 main 0000000140001000 main.obj
+// 0001:00001300 ?__scrt_common_main@@YAHXZ 0000000140001300 libcmt:exe_main.obj
+//
+// entry point at 0001:00000360
+//
+// Static symbols
+//
+// 0000:00000000 __guard_fids__ 0000000140000000 libcmt : exe_main.obj
//===----------------------------------------------------------------------===//
#include "MapFile.h"
#include "Symbols.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
+#include "lld/Common/Timer.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::coff;
-namespace lld {
-namespace coff {
+static Timer totalMapTimer("MAP emission (Cumulative)", Timer::root());
+static Timer symbolGatherTimer("Gather symbols", totalMapTimer);
+static Timer symbolStringsTimer("Build symbol strings", totalMapTimer);
+static Timer writeTimer("Write to file", totalMapTimer);
-using SymbolMapTy =
- DenseMap<const SectionChunk *, SmallVector<DefinedRegular *, 4>>;
+// Print out the first two columns of a line.
+static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
+ os << format(" %04x:%08llx", sec, addr);
+}
-static constexpr char indent8[] = " "; // 8 spaces
-static constexpr char indent16[] = " "; // 16 spaces
+// Write the time stamp with the format used by link.exe
+// It seems identical to strftime with "%c" on msvc build, but we need a
+// locale-agnostic version.
+static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
+ constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
+ "Thu", "Fri", "Sat"};
+ constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
+ "May", "Jun", "Jul", "Aug",
+ "Sep", "Oct", "Nov", "Dec"};
+ tm *time = localtime(&tds);
+ os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
+ months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
+ time->tm_sec, time->tm_year + 1900);
+}
-// Print out the first three columns of a line.
-static void writeHeader(raw_ostream &os, uint64_t addr, uint64_t size,
- uint64_t align) {
- os << format("%08llx %08llx %5lld ", addr, size, align);
+static void sortUniqueSymbols(std::vector<Defined *> &syms) {
+ // Build helper vector
+ using SortEntry = std::pair<Defined *, size_t>;
+ std::vector<SortEntry> v;
+ v.resize(syms.size());
+ for (size_t i = 0, e = syms.size(); i < e; ++i)
+ v[i] = SortEntry(syms[i], i);
+
+ // Remove duplicate symbol pointers
+ parallelSort(v, std::less<SortEntry>());
+ auto end = std::unique(v.begin(), v.end(),
+ [](const SortEntry &a, const SortEntry &b) {
+ return a.first == b.first;
+ });
+ v.erase(end, v.end());
+
+ // Sort by RVA then original order
+ parallelSort(v, [](const SortEntry &a, const SortEntry &b) {
+ // Add config->imageBase to avoid comparing "negative" RVAs.
+ // This can happen with symbols of Absolute kind
+ uint64_t rvaa = config->imageBase + a.first->getRVA();
+ uint64_t rvab = config->imageBase + b.first->getRVA();
+ return rvaa < rvab || (rvaa == rvab && a.second < b.second);
+ });
+
+ syms.resize(v.size());
+ for (size_t i = 0, e = v.size(); i < e; ++i)
+ syms[i] = v[i].first;
}
-// Returns a list of all symbols that we want to print out.
-static std::vector<DefinedRegular *> getSymbols() {
- std::vector<DefinedRegular *> v;
+// Returns the lists of all symbols that we want to print out.
+static void getSymbols(std::vector<Defined *> &syms,
+ std::vector<Defined *> &staticSyms) {
+
for (ObjFile *file : ObjFile::instances)
- for (Symbol *b : file->getSymbols())
- if (auto *sym = dyn_cast_or_null<DefinedRegular>(b))
- if (sym && !sym->getCOFFSymbol().isSectionDefinition())
- v.push_back(sym);
- return v;
-}
+ for (Symbol *b : file->getSymbols()) {
+ if (!b || !b->isLive())
+ continue;
+ if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
+ COFFSymbolRef symRef = sym->getCOFFSymbol();
+ if (!symRef.isSectionDefinition() &&
+ symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
+ if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
+ staticSyms.push_back(sym);
+ else
+ syms.push_back(sym);
+ }
+ } else if (auto *sym = dyn_cast<Defined>(b)) {
+ syms.push_back(sym);
+ }
+ }
+
+ for (ImportFile *file : ImportFile::instances) {
+ if (!file->live)
+ continue;
+
+ if (!file->thunkSym)
+ continue;
+
+ if (!file->thunkLive)
+ continue;
+
+ if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
+ syms.push_back(thunkSym);
-// Returns a map from sections to their symbols.
-static SymbolMapTy getSectionSyms(ArrayRef<DefinedRegular *> syms) {
- SymbolMapTy ret;
- for (DefinedRegular *s : syms)
- ret[s->getChunk()].push_back(s);
-
- // Sort symbols by address.
- for (auto &it : ret) {
- SmallVectorImpl<DefinedRegular *> &v = it.second;
- std::sort(v.begin(), v.end(), [](DefinedRegular *a, DefinedRegular *b) {
- return a->getRVA() < b->getRVA();
- });
+ if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
+ syms.push_back(impSym);
}
- return ret;
+
+ sortUniqueSymbols(syms);
+ sortUniqueSymbols(staticSyms);
}
// Construct a map from symbols to their stringified representations.
-static DenseMap<DefinedRegular *, std::string>
-getSymbolStrings(ArrayRef<DefinedRegular *> syms) {
+static DenseMap<Defined *, std::string>
+getSymbolStrings(ArrayRef<Defined *> syms) {
std::vector<std::string> str(syms.size());
parallelForEachN((size_t)0, syms.size(), [&](size_t i) {
raw_string_ostream os(str[i]);
- writeHeader(os, syms[i]->getRVA(), 0, 0);
- os << indent16 << toString(*syms[i]);
+ Defined *sym = syms[i];
+
+ uint16_t sectionIdx = 0;
+ uint64_t address = 0;
+ SmallString<128> fileDescr;
+
+ if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
+ address = absSym->getVA();
+ fileDescr = "<absolute>";
+ } else if (isa<DefinedSynthetic>(sym)) {
+ fileDescr = "<linker-defined>";
+ } else if (isa<DefinedCommon>(sym)) {
+ fileDescr = "<common>";
+ } else if (Chunk *chunk = sym->getChunk()) {
+ address = sym->getRVA();
+ if (OutputSection *sec = chunk->getOutputSection())
+ address -= sec->header.VirtualAddress;
+
+ sectionIdx = chunk->getOutputSectionIdx();
+
+ InputFile *file;
+ if (auto *impSym = dyn_cast<DefinedImportData>(sym))
+ file = impSym->file;
+ else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
+ file = thunkSym->wrappedSym->file;
+ else
+ file = sym->getFile();
+
+ if (file) {
+ if (!file->parentName.empty()) {
+ fileDescr = sys::path::filename(file->parentName);
+ sys::path::replace_extension(fileDescr, "");
+ fileDescr += ":";
+ }
+ fileDescr += sys::path::filename(file->getName());
+ }
+ }
+ writeHeader(os, sectionIdx, address);
+ os << " ";
+ os << left_justify(sym->getName(), 26);
+ os << " ";
+ os << format_hex_no_prefix((config->imageBase + sym->getRVA()), 16);
+ if (!fileDescr.empty()) {
+ os << " "; // FIXME : Handle "f" and "i" flags sometimes generated
+ // by link.exe in those spaces
+ os << fileDescr;
+ }
});
- DenseMap<DefinedRegular *, std::string> ret;
+ DenseMap<Defined *, std::string> ret;
for (size_t i = 0, e = syms.size(); i < e; ++i)
ret[syms[i]] = std::move(str[i]);
return ret;
}
-void writeMapFile(ArrayRef<OutputSection *> outputSections) {
+void lld::coff::writeMapFile(ArrayRef<OutputSection *> outputSections) {
if (config->mapFile.empty())
return;
if (ec)
fatal("cannot open " + config->mapFile + ": " + ec.message());
+ ScopedTimer t1(totalMapTimer);
+
// Collect symbol info that we want to print out.
- std::vector<DefinedRegular *> syms = getSymbols();
- SymbolMapTy sectionSyms = getSectionSyms(syms);
- DenseMap<DefinedRegular *, std::string> symStr = getSymbolStrings(syms);
+ ScopedTimer t2(symbolGatherTimer);
+ std::vector<Defined *> syms;
+ std::vector<Defined *> staticSyms;
+ getSymbols(syms, staticSyms);
+ t2.stop();
- // Print out the header line.
- os << "Address Size Align Out In Symbol\n";
+ ScopedTimer t3(symbolStringsTimer);
+ DenseMap<Defined *, std::string> symStr = getSymbolStrings(syms);
+ DenseMap<Defined *, std::string> staticSymStr = getSymbolStrings(staticSyms);
+ t3.stop();
- // Print out file contents.
- for (OutputSection *sec : outputSections) {
- writeHeader(os, sec->getRVA(), sec->getVirtualSize(), /*align=*/pageSize);
- os << sec->name << '\n';
+ ScopedTimer t4(writeTimer);
+ SmallString<128> AppName = sys::path::filename(config->outputFile);
+ sys::path::replace_extension(AppName, "");
+
+ // Print out the file header
+ os << " " << AppName << "\n";
+ os << "\n";
+ os << " Timestamp is " << format_hex_no_prefix(config->timestamp, 8) << " (";
+ if (config->repro) {
+ os << "Repro mode";
+ } else {
+ writeFormattedTimestamp(os, config->timestamp);
+ }
+ os << ")\n";
+
+ os << "\n";
+ os << " Preferred load address is "
+ << format_hex_no_prefix(config->imageBase, 16) << "\n";
+ os << "\n";
+
+ // Print out section table.
+ os << " Start Length Name Class\n";
+
+ for (OutputSection *sec : outputSections) {
+ // Merge display of chunks with same sectionName
+ std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
for (Chunk *c : sec->chunks) {
auto *sc = dyn_cast<SectionChunk>(c);
if (!sc)
continue;
- writeHeader(os, sc->getRVA(), sc->getSize(), sc->getAlignment());
- os << indent8 << sc->file->getName() << ":(" << sc->getSectionName()
- << ")\n";
- for (DefinedRegular *sym : sectionSyms[sc])
- os << symStr[sym] << '\n';
+ if (ChunkRanges.empty() ||
+ c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
+ ChunkRanges.emplace_back(sc, sc);
+ } else {
+ ChunkRanges.back().second = sc;
+ }
+ }
+
+ const bool isCodeSection =
+ (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
+ (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
+ (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
+ StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
+
+ for (auto &cr : ChunkRanges) {
+ size_t size =
+ cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
+
+ auto address = cr.first->getRVA() - sec->header.VirtualAddress;
+ writeHeader(os, sec->sectionIndex, address);
+ os << " " << format_hex_no_prefix(size, 8) << "H";
+ os << " " << left_justify(cr.first->getSectionName(), 23);
+ os << " " << SectionClass;
+ os << '\n';
}
}
-}
-} // namespace coff
-} // namespace lld
+ // Print out the symbols table (without static symbols)
+ os << "\n";
+ os << " Address Publics by Value Rva+Base"
+ " Lib:Object\n";
+ os << "\n";
+ for (Defined *sym : syms)
+ os << symStr[sym] << '\n';
+
+ // Print out the entry point.
+ os << "\n";
+
+ uint16_t entrySecIndex = 0;
+ uint64_t entryAddress = 0;
+
+ if (!config->noEntry) {
+ Defined *entry = dyn_cast_or_null<Defined>(config->entry);
+ if (entry) {
+ Chunk *chunk = entry->getChunk();
+ entrySecIndex = chunk->getOutputSectionIdx();
+ entryAddress =
+ entry->getRVA() - chunk->getOutputSection()->header.VirtualAddress;
+ }
+ }
+ os << " entry point at ";
+ os << format("%04x:%08llx", entrySecIndex, entryAddress);
+ os << "\n";
+
+ // Print out the static symbols
+ os << "\n";
+ os << " Static symbols\n";
+ os << "\n";
+ for (Defined *sym : staticSyms)
+ os << staticSymStr[sym] << '\n';
+
+ t4.stop();
+ t1.stop();
+}
using namespace llvm;
using namespace llvm::COFF;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
AutoExporter::AutoExporter() {
excludeLibs = {
"libclang_rt.builtins-arm",
"libclang_rt.builtins-i386",
"libclang_rt.builtins-x86_64",
+ "libclang_rt.profile",
+ "libclang_rt.profile-aarch64",
+ "libclang_rt.profile-arm",
+ "libclang_rt.profile-i386",
+ "libclang_rt.profile-x86_64",
"libc++",
"libc++abi",
"libunwind",
"__builtin_",
// Artificial symbols such as .refptr
".",
+ // profile generate symbols
+ "__profc_",
+ "__profd_",
+ "__profvp_",
};
excludeSymbolSuffixes = {
return !excludeObjects.count(fileName);
}
-void writeDefFile(StringRef name) {
+void lld::coff::writeDefFile(StringRef name) {
std::error_code ec;
raw_fd_ostream os(name, ec, sys::fs::OF_None);
if (ec)
os << "\n";
}
}
-
-} // namespace coff
-} // namespace lld
def _no : F<name#":no">, HelpText<help_off>;
}
+// Same as B<> above, but without help texts, for private undocumented
+// options.
+multiclass B_priv<string name> {
+ def "" : F<name>;
+ def _no : F<name#":no">;
+}
+
def align : P<"align", "Section alignment">;
def aligncomm : P<"aligncomm", "Set common symbol alignment">;
def alternatename : P<"alternatename", "Define weak alias">;
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
def pdb : P<"pdb", "PDB file path">;
+def pdbstripped : P<"pdbstripped", "Stripped PDB file path">;
def pdbaltpath : P<"pdbaltpath", "PDB file path to embed in the image">;
+def pdbstream : Joined<["/", "-", "/?", "-?"], "pdbstream:">,
+ MetaVarName<"<name>=<file>">,
+ HelpText<"Embed the contents of <file> in the PDB as named stream <name>">;
def section : P<"section", "Specify section attributes">;
def stack : P<"stack", "Size of the stack">;
def stub : P<"stub", "Specify DOS stub file">;
defm appcontainer : B<"appcontainer",
"Image can only be run in an app container",
"Image can run outside an app container (default)">;
+defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack",
+ "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">;
defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
"Disable ASLR (default when /fixed)">;
defm fixed : B<"fixed", "Disable base relocations",
def help_q : Flag<["/??", "-??", "/?", "-?"], "">, Alias<help>;
// LLD extensions
+defm auto_import : B_priv<"auto-import">;
+defm runtime_pseudo_reloc : B_priv<"runtime-pseudo-reloc">;
def end_lib : F<"end-lib">,
HelpText<"Ends group of objects treated as if they were in a library">;
def exclude_all_symbols : F<"exclude-all-symbols">;
HelpText<"Add symbol as undefined, but allow it to remain undefined">;
def kill_at : F<"kill-at">;
def lldmingw : F<"lldmingw">;
+def noseh : F<"noseh">;
def output_def : Joined<["/", "-", "/?", "-?"], "output-def:">;
def pdb_source_path : P<"pdbsourcepath",
"Base path used to make relative source file path absolute in PDB">;
"output native object for merged LTO unit to this path">;
def dash_dash_version : Flag<["--"], "version">,
HelpText<"Print version information">;
-defm threads: B<"threads",
- "Run the linker multi-threaded (default)",
- "Do not run the linker multi-threaded">;
+def threads
+ : P<"threads", "Number of threads. '1' disables multi-threading. By "
+ "default all available hardware threads are used">;
// Flags for debugging
def lldmap : F<"lldmap">;
def lldmap_file : Joined<["/", "-", "/?", "-?"], "lldmap:">;
+def map : F<"map">;
+def map_file : Joined<["/", "-", "/?", "-?"], "map:">;
def show_timing : F<"time">;
def summary : F<"summary">;
#include "TypeMerger.h"
#include "Writer.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h"
#include "llvm/DebugInfo/CodeView/DebugSubsectionRecord.h"
#include "llvm/DebugInfo/CodeView/SymbolDeserializer.h"
#include "llvm/DebugInfo/CodeView/SymbolRecordHelpers.h"
#include "llvm/DebugInfo/CodeView/SymbolSerializer.h"
-#include "llvm/DebugInfo/CodeView/TypeDeserializer.h"
-#include "llvm/DebugInfo/CodeView/TypeDumpVisitor.h"
#include "llvm/DebugInfo/CodeView/TypeIndexDiscovery.h"
-#include "llvm/DebugInfo/CodeView/TypeRecordHelpers.h"
-#include "llvm/DebugInfo/CodeView/TypeStreamMerger.h"
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/MSF/MSFCommon.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
using namespace llvm;
using namespace llvm::codeview;
+using namespace lld;
+using namespace lld::coff;
using llvm::object::coff_section;
-namespace lld {
-namespace coff {
-
static ExitOnError exitOnErr;
static Timer totalPdbLinkTimer("PDB Emission (Cumulative)", Timer::root());
static Timer addObjectsTimer("Add Objects", totalPdbLinkTimer);
static Timer typeMergingTimer("Type Merging", addObjectsTimer);
static Timer symbolMergingTimer("Symbol Merging", addObjectsTimer);
-static Timer globalsLayoutTimer("Globals Stream Layout", totalPdbLinkTimer);
+static Timer publicsLayoutTimer("Publics Stream Layout", totalPdbLinkTimer);
static Timer tpiStreamLayoutTimer("TPI Stream Layout", totalPdbLinkTimer);
static Timer diskCommitTimer("Commit to Disk", totalPdbLinkTimer);
public:
PDBLinker(SymbolTable *symtab)
- : alloc(), symtab(symtab), builder(alloc), tMerger(alloc) {
+ : symtab(symtab), builder(bAlloc), tMerger(bAlloc) {
// This isn't strictly necessary, but link.exe usually puts an empty string
// as the first "valid" string in the string table, so we do the same in
// order to maintain as much byte-for-byte compatibility as possible.
/// Add natvis files specified on the command line.
void addNatvisFiles();
+ /// Add named streams specified on the command line.
+ void addNamedStreams();
+
/// Link CodeView from each object file in the symbol table into the PDB.
void addObjectsToPDB();
+ /// Add every live, defined public symbol to the PDB.
+ void addPublicsToPDB();
+
/// Link info for each import file in the symbol table into the PDB.
void addImportFilesToPDB(ArrayRef<OutputSection *> outputSections);
/// Link CodeView from a single object file into the target (output) PDB.
/// When a precompiled headers object is linked, its TPI map might be provided
/// externally.
- void addObjFile(ObjFile *file, CVIndexMap *externIndexMap = nullptr);
-
- /// Produce a mapping from the type and item indices used in the object
- /// file to those in the destination PDB.
- ///
- /// If the object file uses a type server PDB (compiled with /Zi), merge TPI
- /// and IPI from the type server PDB and return a map for it. Each unique type
- /// server PDB is merged at most once, so this may return an existing index
- /// mapping.
- ///
- /// If the object does not use a type server PDB (compiled with /Z7), we merge
- /// all the type and item records from the .debug$S stream and fill in the
- /// caller-provided objectIndexMap.
- Expected<const CVIndexMap &> mergeDebugT(ObjFile *file,
- CVIndexMap *objectIndexMap);
-
- /// Reads and makes available a PDB.
- Expected<const CVIndexMap &> maybeMergeTypeServerPDB(ObjFile *file);
-
- /// Merges a precompiled headers TPI map into the current TPI map. The
- /// precompiled headers object will also be loaded and remapped in the
- /// process.
- Error mergeInPrecompHeaderObj(ObjFile *file, CVIndexMap *objectIndexMap);
-
- /// Reads and makes available a precompiled headers object.
- ///
- /// This is a requirement for objects compiled with cl.exe /Yu. In that
- /// case, the referenced object (which was compiled with /Yc) has to be loaded
- /// first. This is mainly because the current object's TPI stream has external
- /// references to the precompiled headers object.
- ///
- /// If the precompiled headers object was already loaded, this function will
- /// simply return its (remapped) TPI map.
- Expected<const CVIndexMap &> aquirePrecompObj(ObjFile *file);
-
- /// Adds a precompiled headers object signature -> TPI mapping.
- std::pair<CVIndexMap &, bool /*already there*/>
- registerPrecompiledHeaders(uint32_t signature);
+ void addDebug(TpiSource *source);
+
+ const CVIndexMap *mergeTypeRecords(TpiSource *source, CVIndexMap *localMap);
+
+ void addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap);
void mergeSymbolRecords(ObjFile *file, const CVIndexMap &indexMap,
std::vector<ulittle32_t *> &stringTableRefs,
void printStats();
private:
- BumpPtrAllocator alloc;
-
SymbolTable *symtab;
pdb::PDBFileBuilder builder;
llvm::SmallString<128> nativePath;
- std::vector<pdb::SecMapEntry> sectionMap;
-
- /// Type index mappings of type server PDBs that we've loaded so far.
- std::map<codeview::GUID, CVIndexMap> typeServerIndexMappings;
-
- /// Type index mappings of precompiled objects type map that we've loaded so
- /// far.
- std::map<uint32_t, CVIndexMap> precompTypeIndexMappings;
-
// For statistics
uint64_t globalSymbols = 0;
uint64_t moduleSymbols = 0;
uint64_t publicSymbols = 0;
-
- // When showSummary is enabled, these are histograms of TPI and IPI records
- // keyed by type index.
- SmallVector<uint32_t, 0> tpiCounts;
- SmallVector<uint32_t, 0> ipiCounts;
};
class DebugSHandler {
ObjFile &file;
/// The result of merging type indices.
- const CVIndexMap &indexMap;
+ const CVIndexMap *indexMap;
/// The DEBUG_S_STRINGTABLE subsection. These strings are referred to by
/// index from other records in the .debug$S section. All of these strings
/// need to be added to the global PDB string table, and all references to
/// these strings need to have their indices re-written to refer to the
/// global PDB string table.
- DebugStringTableSubsectionRef cVStrTab;
+ DebugStringTableSubsectionRef cvStrTab;
/// The DEBUG_S_FILECHKSMS subsection. As above, these are referred to
/// by other records in the .debug$S section and need to be merged into the
/// PDB.
DebugChecksumsSubsectionRef checksums;
- /// The DEBUG_S_INLINEELINES subsection. There can be only one of these per
- /// object file.
- DebugInlineeLinesSubsectionRef inlineeLines;
-
/// The DEBUG_S_FRAMEDATA subsection(s). There can be more than one of
/// these and they need not appear in any specific order. However, they
/// contain string table references which need to be re-written, so we
/// references.
std::vector<ulittle32_t *> stringTableReferences;
+ void mergeInlineeLines(const DebugSubsectionRecord &inlineeLines);
+
public:
- DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap &indexMap)
+ DebugSHandler(PDBLinker &linker, ObjFile &file, const CVIndexMap *indexMap)
: linker(linker), file(file), indexMap(indexMap) {}
- void handleDebugS(lld::coff::SectionChunk &debugS);
-
- std::shared_ptr<DebugInlineeLinesSubsection>
- mergeInlineeLines(DebugChecksumsSubsection *newChecksums);
+ void handleDebugS(ArrayRef<uint8_t> relocatedDebugContents);
void finish();
};
fileName = std::move(absoluteFileName);
}
-// A COFF .debug$H section is currently a clang extension. This function checks
-// if a .debug$H section is in a format that we expect / understand, so that we
-// can ignore any sections which are coincidentally also named .debug$H but do
-// not contain a format we recognize.
-static bool canUseDebugH(ArrayRef<uint8_t> debugH) {
- if (debugH.size() < sizeof(object::debug_h_header))
- return false;
- auto *header =
- reinterpret_cast<const object::debug_h_header *>(debugH.data());
- debugH = debugH.drop_front(sizeof(object::debug_h_header));
- return header->Magic == COFF::DEBUG_HASHES_SECTION_MAGIC &&
- header->Version == 0 &&
- header->HashAlgorithm == uint16_t(GlobalTypeHashAlg::SHA1_8) &&
- (debugH.size() % 8 == 0);
-}
-
-static Optional<ArrayRef<uint8_t>> getDebugH(ObjFile *file) {
- SectionChunk *sec =
- SectionChunk::findByName(file->getDebugChunks(), ".debug$H");
- if (!sec)
- return llvm::None;
- ArrayRef<uint8_t> contents = sec->getContents();
- if (!canUseDebugH(contents))
- return None;
- return contents;
-}
-
-static ArrayRef<GloballyHashedType>
-getHashesFromDebugH(ArrayRef<uint8_t> debugH) {
- assert(canUseDebugH(debugH));
-
- debugH = debugH.drop_front(sizeof(object::debug_h_header));
- uint32_t count = debugH.size() / sizeof(GloballyHashedType);
- return {reinterpret_cast<const GloballyHashedType *>(debugH.data()), count};
-}
-
static void addTypeInfo(pdb::TpiStreamBuilder &tpiBuilder,
TypeCollection &typeTable) {
// Start the TPI or IPI stream header.
});
}
-Expected<const CVIndexMap &>
-PDBLinker::mergeDebugT(ObjFile *file, CVIndexMap *objectIndexMap) {
- ScopedTimer t(typeMergingTimer);
-
- if (!file->debugTypesObj)
- return *objectIndexMap; // no Types stream
-
- // Precompiled headers objects need to save the index map for further
- // reference by other objects which use the precompiled headers.
- if (file->debugTypesObj->kind == TpiSource::PCH) {
- uint32_t pchSignature = file->pchSignature.getValueOr(0);
- if (pchSignature == 0)
- fatal("No signature found for the precompiled headers OBJ (" +
- file->getName() + ")");
-
- // When a precompiled headers object comes first on the command-line, we
- // update the mapping here. Otherwise, if an object referencing the
- // precompiled headers object comes first, the mapping is created in
- // aquirePrecompObj(), thus we would skip this block.
- if (!objectIndexMap->isPrecompiledTypeMap) {
- auto r = registerPrecompiledHeaders(pchSignature);
- if (r.second)
- fatal(
- "A precompiled headers OBJ with the same signature was already "
- "provided! (" +
- file->getName() + ")");
-
- objectIndexMap = &r.first;
- }
- }
-
- if (file->debugTypesObj->kind == TpiSource::UsingPDB) {
- // Look through type servers. If we've already seen this type server,
- // don't merge any type information.
- return maybeMergeTypeServerPDB(file);
- }
-
- CVTypeArray types;
- BinaryStreamReader reader(file->debugTypes, support::little);
- cantFail(reader.readArray(types, reader.getLength()));
-
- if (file->debugTypesObj->kind == TpiSource::UsingPCH) {
- // This object was compiled with /Yu, so process the corresponding
- // precompiled headers object (/Yc) first. Some type indices in the current
- // object are referencing data in the precompiled headers object, so we need
- // both to be loaded.
- Error e = mergeInPrecompHeaderObj(file, objectIndexMap);
- if (e)
- return std::move(e);
-
- // Drop LF_PRECOMP record from the input stream, as it has been replaced
- // with the precompiled headers Type stream in the mergeInPrecompHeaderObj()
- // call above. Note that we can't just call Types.drop_front(), as we
- // explicitly want to rebase the stream.
- CVTypeArray::Iterator firstType = types.begin();
- types.setUnderlyingStream(
- types.getUnderlyingStream().drop_front(firstType->RecordData.size()));
- }
-
- // Fill in the temporary, caller-provided ObjectIndexMap.
- if (config->debugGHashes) {
- ArrayRef<GloballyHashedType> hashes;
- std::vector<GloballyHashedType> ownedHashes;
- if (Optional<ArrayRef<uint8_t>> debugH = getDebugH(file))
- hashes = getHashesFromDebugH(*debugH);
- else {
- ownedHashes = GloballyHashedType::hashTypes(types);
- hashes = ownedHashes;
- }
-
- if (auto err = mergeTypeAndIdRecords(
- tMerger.globalIDTable, tMerger.globalTypeTable,
- objectIndexMap->tpiMap, types, hashes, file->pchSignature))
- fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(err)));
- } else {
- if (auto err = mergeTypeAndIdRecords(tMerger.iDTable, tMerger.typeTable,
- objectIndexMap->tpiMap, types,
- file->pchSignature))
- fatal("codeview::mergeTypeAndIdRecords failed: " +
- toString(std::move(err)));
- }
-
- if (config->showSummary) {
- // Count how many times we saw each type record in our input. This
- // calculation requires a second pass over the type records to classify each
- // record as a type or index. This is slow, but this code executes when
- // collecting statistics.
- tpiCounts.resize(tMerger.getTypeTable().size());
- ipiCounts.resize(tMerger.getIDTable().size());
- uint32_t srcIdx = 0;
- for (CVType &ty : types) {
- TypeIndex dstIdx = objectIndexMap->tpiMap[srcIdx++];
- // Type merging may fail, so a complex source type may become the simple
- // NotTranslated type, which cannot be used as an array index.
- if (dstIdx.isSimple())
- continue;
- SmallVectorImpl<uint32_t> &counts =
- isIdRecord(ty.kind()) ? ipiCounts : tpiCounts;
- ++counts[dstIdx.toArrayIndex()];
- }
- }
-
- return *objectIndexMap;
-}
-
-Expected<const CVIndexMap &> PDBLinker::maybeMergeTypeServerPDB(ObjFile *file) {
- Expected<llvm::pdb::NativeSession *> pdbSession = findTypeServerSource(file);
- if (!pdbSession)
- return pdbSession.takeError();
-
- pdb::PDBFile &pdbFile = pdbSession.get()->getPDBFile();
- pdb::InfoStream &info = cantFail(pdbFile.getPDBInfoStream());
-
- auto it = typeServerIndexMappings.emplace(info.getGuid(), CVIndexMap());
- CVIndexMap &indexMap = it.first->second;
- if (!it.second)
- return indexMap; // already merged
-
- // Mark this map as a type server map.
- indexMap.isTypeServerMap = true;
-
- Expected<pdb::TpiStream &> expectedTpi = pdbFile.getPDBTpiStream();
- if (auto e = expectedTpi.takeError())
- fatal("Type server does not have TPI stream: " + toString(std::move(e)));
- pdb::TpiStream *maybeIpi = nullptr;
- if (pdbFile.hasPDBIpiStream()) {
- Expected<pdb::TpiStream &> expectedIpi = pdbFile.getPDBIpiStream();
- if (auto e = expectedIpi.takeError())
- fatal("Error getting type server IPI stream: " + toString(std::move(e)));
- maybeIpi = &*expectedIpi;
- }
-
- if (config->debugGHashes) {
- // PDBs do not actually store global hashes, so when merging a type server
- // PDB we have to synthesize global hashes. To do this, we first synthesize
- // global hashes for the TPI stream, since it is independent, then we
- // synthesize hashes for the IPI stream, using the hashes for the TPI stream
- // as inputs.
- auto tpiHashes = GloballyHashedType::hashTypes(expectedTpi->typeArray());
- Optional<uint32_t> endPrecomp;
- // Merge TPI first, because the IPI stream will reference type indices.
- if (auto err =
- mergeTypeRecords(tMerger.globalTypeTable, indexMap.tpiMap,
- expectedTpi->typeArray(), tpiHashes, endPrecomp))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
-
- // Merge IPI.
- if (maybeIpi) {
- auto ipiHashes =
- GloballyHashedType::hashIds(maybeIpi->typeArray(), tpiHashes);
- if (auto err =
- mergeIdRecords(tMerger.globalIDTable, indexMap.tpiMap,
- indexMap.ipiMap, maybeIpi->typeArray(), ipiHashes))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
- }
- } else {
- // Merge TPI first, because the IPI stream will reference type indices.
- if (auto err = mergeTypeRecords(tMerger.typeTable, indexMap.tpiMap,
- expectedTpi->typeArray()))
- fatal("codeview::mergeTypeRecords failed: " + toString(std::move(err)));
-
- // Merge IPI.
- if (maybeIpi) {
- if (auto err = mergeIdRecords(tMerger.iDTable, indexMap.tpiMap,
- indexMap.ipiMap, maybeIpi->typeArray()))
- fatal("codeview::mergeIdRecords failed: " + toString(std::move(err)));
- }
- }
-
- if (config->showSummary) {
- // Count how many times we saw each type record in our input. If a
- // destination type index is present in the source to destination type index
- // map, that means we saw it once in the input. Add it to our histogram.
- tpiCounts.resize(tMerger.getTypeTable().size());
- ipiCounts.resize(tMerger.getIDTable().size());
- for (TypeIndex ti : indexMap.tpiMap)
- if (!ti.isSimple())
- ++tpiCounts[ti.toArrayIndex()];
- for (TypeIndex ti : indexMap.ipiMap)
- if (!ti.isSimple())
- ++ipiCounts[ti.toArrayIndex()];
- }
-
- return indexMap;
-}
-
-Error PDBLinker::mergeInPrecompHeaderObj(ObjFile *file,
- CVIndexMap *objectIndexMap) {
- const PrecompRecord &precomp =
- retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
-
- Expected<const CVIndexMap &> e = aquirePrecompObj(file);
- if (!e)
- return e.takeError();
-
- const CVIndexMap &precompIndexMap = *e;
- assert(precompIndexMap.isPrecompiledTypeMap);
-
- if (precompIndexMap.tpiMap.empty())
- return Error::success();
-
- assert(precomp.getStartTypeIndex() == TypeIndex::FirstNonSimpleIndex);
- assert(precomp.getTypesCount() <= precompIndexMap.tpiMap.size());
- // Use the previously remapped index map from the precompiled headers.
- objectIndexMap->tpiMap.append(precompIndexMap.tpiMap.begin(),
- precompIndexMap.tpiMap.begin() +
- precomp.getTypesCount());
- return Error::success();
-}
-
-static bool equals_path(StringRef path1, StringRef path2) {
-#if defined(_WIN32)
- return path1.equals_lower(path2);
-#else
- return path1.equals(path2);
-#endif
-}
-// Find by name an OBJ provided on the command line
-static ObjFile *findObjWithPrecompSignature(StringRef fileNameOnly,
- uint32_t precompSignature) {
- for (ObjFile *f : ObjFile::instances) {
- StringRef currentFileName = sys::path::filename(f->getName());
-
- if (f->pchSignature.hasValue() &&
- f->pchSignature.getValue() == precompSignature &&
- equals_path(fileNameOnly, currentFileName))
- return f;
- }
- return nullptr;
-}
-
-std::pair<CVIndexMap &, bool /*already there*/>
-PDBLinker::registerPrecompiledHeaders(uint32_t signature) {
- auto insertion = precompTypeIndexMappings.insert({signature, CVIndexMap()});
- CVIndexMap &indexMap = insertion.first->second;
- if (!insertion.second)
- return {indexMap, true};
- // Mark this map as a precompiled types map.
- indexMap.isPrecompiledTypeMap = true;
- return {indexMap, false};
-}
-
-Expected<const CVIndexMap &> PDBLinker::aquirePrecompObj(ObjFile *file) {
- const PrecompRecord &precomp =
- retrieveDependencyInfo<PrecompRecord>(file->debugTypesObj);
-
- // First, check if we already loaded the precompiled headers object with this
- // signature. Return the type index mapping if we've already seen it.
- auto r = registerPrecompiledHeaders(precomp.getSignature());
- if (r.second)
- return r.first;
-
- CVIndexMap &indexMap = r.first;
-
- // Cross-compile warning: given that Clang doesn't generate LF_PRECOMP
- // records, we assume the OBJ comes from a Windows build of cl.exe. Thusly,
- // the paths embedded in the OBJs are in the Windows format.
- SmallString<128> precompFileName = sys::path::filename(
- precomp.getPrecompFilePath(), sys::path::Style::windows);
-
- // link.exe requires that a precompiled headers object must always be provided
- // on the command-line, even if that's not necessary.
- auto precompFile =
- findObjWithPrecompSignature(precompFileName, precomp.Signature);
- if (!precompFile)
- return createFileError(
- precomp.getPrecompFilePath().str(),
- make_error<pdb::PDBError>(pdb::pdb_error_code::no_matching_pch));
-
- addObjFile(precompFile, &indexMap);
-
- return indexMap;
-}
-
static bool remapTypeIndex(TypeIndex &ti, ArrayRef<TypeIndex> typeIndexMap) {
if (ti.isSimple())
return true;
/// MSVC translates S_PROC_ID_END to S_END, and S_[LG]PROC32_ID to S_[LG]PROC32
static void translateIdSymbols(MutableArrayRef<uint8_t> &recordData,
- TypeCollection &iDTable) {
+ TypeCollection &idTable) {
RecordPrefix *prefix = reinterpret_cast<RecordPrefix *>(recordData.data());
SymbolKind kind = symbolKind(recordData);
// Note that LF_FUNC_ID and LF_MEMFUNC_ID have the same record layout, and
// in both cases we just need the second type index.
if (!ti->isSimple() && !ti->isNoneType()) {
- CVType funcIdData = iDTable.getType(*ti);
- SmallVector<TypeIndex, 2> indices;
- discoverTypeIndices(funcIdData, indices);
- assert(indices.size() == 2);
- *ti = indices[1];
+ CVType funcIdData = idTable.getType(*ti);
+ ArrayRef<uint8_t> tiBuf = funcIdData.data().slice(8, 4);
+ assert(tiBuf.size() == 4 && "corrupt LF_[MEM]FUNC_ID record");
+ *ti = *reinterpret_cast<const TypeIndex *>(tiBuf.data());
}
kind = (kind == SymbolKind::S_GPROC32_ID) ? SymbolKind::S_GPROC32
switch (sym.kind()) {
case SymbolKind::S_GDATA32:
case SymbolKind::S_CONSTANT:
+ case SymbolKind::S_GTHREAD32:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
// since they are synthesized by the linker in response to S_GPROC32 and
// S_LPROC32, but if we do see them, don't put them in the module stream I
return !isGlobalScope;
// S_GDATA32 does not go in the module stream, but S_LDATA32 does.
case SymbolKind::S_LDATA32:
+ case SymbolKind::S_LTHREAD32:
default:
return true;
}
}
-static bool symbolGoesInGlobalsStream(const CVSymbol &sym, bool isGlobalScope) {
+static bool symbolGoesInGlobalsStream(const CVSymbol &sym,
+ bool isFunctionScope) {
switch (sym.kind()) {
case SymbolKind::S_CONSTANT:
case SymbolKind::S_GDATA32:
- // S_LDATA32 goes in both the module stream and the globals stream.
- case SymbolKind::S_LDATA32:
+ case SymbolKind::S_GTHREAD32:
case SymbolKind::S_GPROC32:
case SymbolKind::S_LPROC32:
// We really should not be seeing S_PROCREF and S_LPROCREF in the first place
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
return true;
- // S_UDT records go in the globals stream if it is a global S_UDT.
+ // Records that go in the globals stream, unless they are function-local.
case SymbolKind::S_UDT:
- return isGlobalScope;
+ case SymbolKind::S_LDATA32:
+ case SymbolKind::S_LTHREAD32:
+ return !isFunctionScope;
default:
return false;
}
case SymbolKind::S_CONSTANT:
case SymbolKind::S_UDT:
case SymbolKind::S_GDATA32:
+ case SymbolKind::S_GTHREAD32:
+ case SymbolKind::S_LTHREAD32:
case SymbolKind::S_LDATA32:
case SymbolKind::S_PROCREF:
case SymbolKind::S_LPROCREF:
MutableArrayRef<uint8_t> alignedSymbolMem;
if (needsRealignment) {
void *alignedData =
- alloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb));
+ bAlloc.Allocate(totalRealignedSize, alignOf(CodeViewContainer::Pdb));
alignedSymbolMem = makeMutableArrayRef(
reinterpret_cast<uint8_t *>(alignedData), totalRealignedSize);
}
// adding the symbol to the module since we may need to get the next
// symbol offset, and writing to the module's symbol stream will update
// that offset.
- if (symbolGoesInGlobalsStream(sym, scopes.empty())) {
+ if (symbolGoesInGlobalsStream(sym, !scopes.empty())) {
addGlobalSymbol(builder.getGsiBuilder(),
file->moduleDBI->getModuleIndex(), curSymOffset, sym);
++globalSymbols;
file->moduleDBI->addSymbolsInBulk(bulkSymbols);
}
-// Allocate memory for a .debug$S / .debug$F section and relocate it.
-static ArrayRef<uint8_t> relocateDebugChunk(BumpPtrAllocator &alloc,
- SectionChunk &debugChunk) {
- uint8_t *buffer = alloc.Allocate<uint8_t>(debugChunk.getSize());
- assert(debugChunk.getOutputSectionIdx() == 0 &&
- "debug sections should not be in output sections");
- debugChunk.writeTo(buffer);
- return makeArrayRef(buffer, debugChunk.getSize());
-}
-
static pdb::SectionContrib createSectionContrib(const Chunk *c, uint32_t modi) {
OutputSection *os = c ? c->getOutputSection() : nullptr;
pdb::SectionContrib sc;
return pdbStrTable.insert(*expectedString);
}
-void DebugSHandler::handleDebugS(lld::coff::SectionChunk &debugS) {
- DebugSubsectionArray subsections;
-
- ArrayRef<uint8_t> relocatedDebugContents = SectionChunk::consumeDebugMagic(
- relocateDebugChunk(linker.alloc, debugS), debugS.getSectionName());
+void DebugSHandler::handleDebugS(ArrayRef<uint8_t> relocatedDebugContents) {
+ relocatedDebugContents =
+ SectionChunk::consumeDebugMagic(relocatedDebugContents, ".debug$S");
+ DebugSubsectionArray subsections;
BinaryStreamReader reader(relocatedDebugContents, support::little);
exitOnErr(reader.readArray(subsections, relocatedDebugContents.size()));
+ // If there is no index map, use an empty one.
+ CVIndexMap tempIndexMap;
+ if (!indexMap)
+ indexMap = &tempIndexMap;
+
for (const DebugSubsectionRecord &ss : subsections) {
// Ignore subsections with the 'ignore' bit. Some versions of the Visual C++
// runtime have subsections with this bit set.
switch (ss.kind()) {
case DebugSubsectionKind::StringTable: {
- assert(!cVStrTab.valid() &&
+ assert(!cvStrTab.valid() &&
"Encountered multiple string table subsections!");
- exitOnErr(cVStrTab.initialize(ss.getRecordData()));
+ exitOnErr(cvStrTab.initialize(ss.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
file.moduleDBI->addDebugSubsection(ss);
break;
case DebugSubsectionKind::InlineeLines:
- assert(!inlineeLines.valid() &&
- "Encountered multiple inlinee lines subsections!");
- exitOnErr(inlineeLines.initialize(ss.getRecordData()));
+ // The inlinee lines subsection also has file checksum table references
+ // that can be used directly, but it contains function id references that
+ // must be remapped.
+ mergeInlineeLines(ss);
break;
case DebugSubsectionKind::FrameData: {
// We need to re-write string table indices here, so save off all
break;
}
case DebugSubsectionKind::Symbols: {
- linker.mergeSymbolRecords(&file, indexMap, stringTableReferences,
+ linker.mergeSymbolRecords(&file, *indexMap, stringTableReferences,
ss.getRecordData());
break;
}
return strings.getString(offset);
}
-std::shared_ptr<DebugInlineeLinesSubsection>
-DebugSHandler::mergeInlineeLines(DebugChecksumsSubsection *newChecksums) {
- auto newInlineeLines = std::make_shared<DebugInlineeLinesSubsection>(
- *newChecksums, inlineeLines.hasExtraFiles());
+void DebugSHandler::mergeInlineeLines(
+ const DebugSubsectionRecord &inlineeSubsection) {
+ DebugInlineeLinesSubsectionRef inlineeLines;
+ exitOnErr(inlineeLines.initialize(inlineeSubsection.getRecordData()));
+ // Remap type indices in inlinee line records in place.
for (const InlineeSourceLine &line : inlineeLines) {
- TypeIndex inlinee = line.Header->Inlinee;
- uint32_t fileID = line.Header->FileID;
- uint32_t sourceLine = line.Header->SourceLineNum;
-
+ TypeIndex &inlinee = *const_cast<TypeIndex *>(&line.Header->Inlinee);
ArrayRef<TypeIndex> typeOrItemMap =
- indexMap.isTypeServerMap ? indexMap.ipiMap : indexMap.tpiMap;
+ indexMap->isTypeServerMap ? indexMap->ipiMap : indexMap->tpiMap;
if (!remapTypeIndex(inlinee, typeOrItemMap)) {
- log("ignoring inlinee line record in " + file.getName() +
+ log("bad inlinee line record in " + file.getName() +
" with bad inlinee index 0x" + utohexstr(inlinee.getIndex()));
- continue;
- }
-
- SmallString<128> filename =
- exitOnErr(getFileName(cVStrTab, checksums, fileID));
- pdbMakeAbsolute(filename);
- newInlineeLines->addInlineSite(inlinee, filename, sourceLine);
-
- if (inlineeLines.hasExtraFiles()) {
- for (uint32_t extraFileId : line.ExtraFiles) {
- filename = exitOnErr(getFileName(cVStrTab, checksums, extraFileId));
- pdbMakeAbsolute(filename);
- newInlineeLines->addExtraFile(filename);
- }
}
}
- return newInlineeLines;
+ // Add the modified inlinee line subsection directly.
+ file.moduleDBI->addDebugSubsection(inlineeSubsection);
}
void DebugSHandler::finish() {
// We should have seen all debug subsections across the entire object file now
// which means that if a StringTable subsection and Checksums subsection were
// present, now is the time to handle them.
- if (!cVStrTab.valid()) {
+ if (!cvStrTab.valid()) {
if (checksums.valid())
fatal(".debug$S sections with a checksums subsection must also contain a "
"string table subsection");
for (codeview::FrameData fd : fds) {
fd.RvaStart += *reloc;
fd.FrameFunc =
- translateStringTableIndex(fd.FrameFunc, cVStrTab, linker.pdbStrTab);
+ translateStringTableIndex(fd.FrameFunc, cvStrTab, linker.pdbStrTab);
dbiBuilder.addNewFpoData(fd);
}
}
for (ulittle32_t *ref : stringTableReferences)
- *ref = translateStringTableIndex(*ref, cVStrTab, linker.pdbStrTab);
+ *ref = translateStringTableIndex(*ref, cvStrTab, linker.pdbStrTab);
// Make a new file checksum table that refers to offsets in the PDB-wide
// string table. Generally the string table subsection appears after the
// checksum table, so we have to do this after looping over all the
- // subsections.
+ // subsections. The new checksum table must have the exact same layout and
+ // size as the original. Otherwise, the file references in the line and
+ // inlinee line tables will be incorrect.
auto newChecksums = std::make_unique<DebugChecksumsSubsection>(linker.pdbStrTab);
for (FileChecksumEntry &fc : checksums) {
SmallString<128> filename =
- exitOnErr(cVStrTab.getString(fc.FileNameOffset));
+ exitOnErr(cvStrTab.getString(fc.FileNameOffset));
pdbMakeAbsolute(filename);
exitOnErr(dbiBuilder.addModuleSourceFile(*file.moduleDBI, filename));
newChecksums->addChecksum(filename, fc.Kind, fc.Checksum);
}
-
- // Rewrite inlinee item indices if present.
- if (inlineeLines.valid())
- file.moduleDBI->addDebugSubsection(mergeInlineeLines(newChecksums.get()));
+ assert(checksums.getArray().getUnderlyingStream().getLength() ==
+ newChecksums->calculateSerializedSize() &&
+ "file checksum table must have same layout");
file.moduleDBI->addDebugSubsection(std::move(newChecksums));
}
-void PDBLinker::addObjFile(ObjFile *file, CVIndexMap *externIndexMap) {
- if (file->mergedIntoPDB)
+static void warnUnusable(InputFile *f, Error e) {
+ if (!config->warnDebugInfoUnusable) {
+ consumeError(std::move(e));
return;
- file->mergedIntoPDB = true;
+ }
+ auto msg = "Cannot use debug info for '" + toString(f) + "' [LNK4099]";
+ if (e)
+ warn(msg + "\n>>> failed to load reference " + toString(std::move(e)));
+ else
+ warn(msg);
+}
+const CVIndexMap *PDBLinker::mergeTypeRecords(TpiSource *source,
+ CVIndexMap *localMap) {
+ ScopedTimer t(typeMergingTimer);
// Before we can process symbol substreams from .debug$S, we need to process
// type information, file checksums, and the string table. Add type info to
// the PDB first, so that we can get the map from object file type and item
// indices to PDB type and item indices.
- CVIndexMap objectIndexMap;
- auto indexMapResult =
- mergeDebugT(file, externIndexMap ? externIndexMap : &objectIndexMap);
+ Expected<const CVIndexMap *> r = source->mergeDebugT(&tMerger, localMap);
// If the .debug$T sections fail to merge, assume there is no debug info.
- if (!indexMapResult) {
- if (!config->warnDebugInfoUnusable) {
- consumeError(indexMapResult.takeError());
- return;
- }
- warn("Cannot use debug info for '" + toString(file) + "' [LNK4099]\n" +
- ">>> failed to load reference " +
- StringRef(toString(indexMapResult.takeError())));
- return;
+ if (!r) {
+ warnUnusable(source->file, r.takeError());
+ return nullptr;
}
+ return *r;
+}
- ScopedTimer t(symbolMergingTimer);
+// Allocate memory for a .debug$S / .debug$F section and relocate it.
+static ArrayRef<uint8_t> relocateDebugChunk(SectionChunk &debugChunk) {
+ uint8_t *buffer = bAlloc.Allocate<uint8_t>(debugChunk.getSize());
+ assert(debugChunk.getOutputSectionIdx() == 0 &&
+ "debug sections should not be in output sections");
+ debugChunk.writeTo(buffer);
+ return makeArrayRef(buffer, debugChunk.getSize());
+}
+void PDBLinker::addDebugSymbols(ObjFile *file, const CVIndexMap *indexMap) {
+ ScopedTimer t(symbolMergingTimer);
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
- DebugSHandler dsh(*this, *file, *indexMapResult);
+ DebugSHandler dsh(*this, *file, indexMap);
// Now do all live .debug$S and .debug$F sections.
for (SectionChunk *debugChunk : file->getDebugChunks()) {
if (!debugChunk->live || debugChunk->getSize() == 0)
continue;
- if (debugChunk->getSectionName() == ".debug$S") {
- dsh.handleDebugS(*debugChunk);
+ bool isDebugS = debugChunk->getSectionName() == ".debug$S";
+ bool isDebugF = debugChunk->getSectionName() == ".debug$F";
+ if (!isDebugS && !isDebugF)
continue;
- }
- if (debugChunk->getSectionName() == ".debug$F") {
- ArrayRef<uint8_t> relocatedDebugContents =
- relocateDebugChunk(alloc, *debugChunk);
+ ArrayRef<uint8_t> relocatedDebugContents = relocateDebugChunk(*debugChunk);
+ if (isDebugS) {
+ dsh.handleDebugS(relocatedDebugContents);
+ } else if (isDebugF) {
FixedStreamArray<object::FpoData> fpoRecords;
BinaryStreamReader reader(relocatedDebugContents, support::little);
uint32_t count = relocatedDebugContents.size() / sizeof(object::FpoData);
// can just copy it.
for (const object::FpoData &fd : fpoRecords)
dbiBuilder.addOldFpoData(fd);
- continue;
}
}
// path to the object into the PDB. If this is a plain object, we make its
// path absolute. If it's an object in an archive, we make the archive path
// absolute.
-static void createModuleDBI(pdb::PDBFileBuilder &builder) {
+static void createModuleDBI(pdb::PDBFileBuilder &builder, ObjFile *file) {
pdb::DbiStreamBuilder &dbiBuilder = builder.getDbiBuilder();
SmallString<128> objName;
- for (ObjFile *file : ObjFile::instances) {
+ bool inArchive = !file->parentName.empty();
+ objName = inArchive ? file->parentName : file->getName();
+ pdbMakeAbsolute(objName);
+ StringRef modName = inArchive ? file->getName() : StringRef(objName);
- bool inArchive = !file->parentName.empty();
- objName = inArchive ? file->parentName : file->getName();
- pdbMakeAbsolute(objName);
- StringRef modName = inArchive ? file->getName() : StringRef(objName);
+ file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName));
+ file->moduleDBI->setObjFileName(objName);
- file->moduleDBI = &exitOnErr(dbiBuilder.addModuleInfo(modName));
- file->moduleDBI->setObjFileName(objName);
+ ArrayRef<Chunk *> chunks = file->getChunks();
+ uint32_t modi = file->moduleDBI->getModuleIndex();
- ArrayRef<Chunk *> chunks = file->getChunks();
- uint32_t modi = file->moduleDBI->getModuleIndex();
-
- for (Chunk *c : chunks) {
- auto *secChunk = dyn_cast<SectionChunk>(c);
- if (!secChunk || !secChunk->live)
- continue;
- pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
- file->moduleDBI->setFirstSectionContrib(sc);
- break;
- }
+ for (Chunk *c : chunks) {
+ auto *secChunk = dyn_cast<SectionChunk>(c);
+ if (!secChunk || !secChunk->live)
+ continue;
+ pdb::SectionContrib sc = createSectionContrib(secChunk, modi);
+ file->moduleDBI->setFirstSectionContrib(sc);
+ break;
}
}
-static PublicSym32 createPublic(Defined *def) {
- PublicSym32 pub(SymbolKind::S_PUB32);
- pub.Name = def->getName();
+void PDBLinker::addDebug(TpiSource *source) {
+ CVIndexMap localMap;
+ const CVIndexMap *indexMap = mergeTypeRecords(source, &localMap);
+
+ if (source->kind == TpiSource::PDB)
+ return; // No symbols in TypeServer PDBs
+
+ addDebugSymbols(source->file, indexMap);
+}
+
+static pdb::BulkPublic createPublic(Defined *def) {
+ pdb::BulkPublic pub;
+ pub.Name = def->getName().data();
+ pub.NameLen = def->getName().size();
+
+ PublicSymFlags flags = PublicSymFlags::None;
if (auto *d = dyn_cast<DefinedCOFF>(def)) {
if (d->getCOFFSymbol().isFunctionDefinition())
- pub.Flags = PublicSymFlags::Function;
+ flags = PublicSymFlags::Function;
} else if (isa<DefinedImportThunk>(def)) {
- pub.Flags = PublicSymFlags::Function;
+ flags = PublicSymFlags::Function;
}
+ pub.setFlags(flags);
OutputSection *os = def->getChunk()->getOutputSection();
assert(os && "all publics should be in final image");
void PDBLinker::addObjectsToPDB() {
ScopedTimer t1(addObjectsTimer);
- createModuleDBI(builder);
+ // Create module descriptors
+ for_each(ObjFile::instances,
+ [&](ObjFile *obj) { createModuleDBI(builder, obj); });
- for (ObjFile *file : ObjFile::instances)
- addObjFile(file);
+ // Merge OBJs that do not have debug types
+ for_each(ObjFile::instances, [&](ObjFile *obj) {
+ if (obj->debugTypesObj)
+ return;
+ // Even if there're no types, still merge non-symbol .Debug$S and .Debug$F
+ // sections
+ addDebugSymbols(obj, nullptr);
+ });
+
+ // Merge dependencies
+ TpiSource::forEachSource([&](TpiSource *source) {
+ if (source->isDependency())
+ addDebug(source);
+ });
+
+ // Merge regular and dependent OBJs
+ TpiSource::forEachSource([&](TpiSource *source) {
+ if (!source->isDependency())
+ addDebug(source);
+ });
builder.getStringTableBuilder().setStrings(pdbStrTab);
t1.stop();
addTypeInfo(builder.getTpiBuilder(), tMerger.getTypeTable());
addTypeInfo(builder.getIpiBuilder(), tMerger.getIDTable());
t2.stop();
+}
- ScopedTimer t3(globalsLayoutTimer);
- // Compute the public and global symbols.
+void PDBLinker::addPublicsToPDB() {
+ ScopedTimer t3(publicsLayoutTimer);
+ // Compute the public symbols.
auto &gsiBuilder = builder.getGsiBuilder();
- std::vector<PublicSym32> publics;
+ std::vector<pdb::BulkPublic> publics;
symtab->forEachSymbol([&publics](Symbol *s) {
- // Only emit defined, live symbols that have a chunk.
+ // Only emit external, defined, live symbols that have a chunk. Static,
+ // non-external symbols do not appear in the symbol table.
auto *def = dyn_cast<Defined>(s);
if (def && def->isLive() && def->getChunk())
publics.push_back(createPublic(def));
if (!publics.empty()) {
publicSymbols = publics.size();
- // Sort the public symbols and add them to the stream.
- parallelSort(publics, [](const PublicSym32 &l, const PublicSym32 &r) {
- return l.Name < r.Name;
- });
- for (const PublicSym32 &pub : publics)
- gsiBuilder.addPublicSymbol(pub);
+ gsiBuilder.addPublicSymbols(std::move(publics));
}
}
print(ObjFile::instances.size(),
"Input OBJ files (expanded from all cmd-line inputs)");
- print(typeServerIndexMappings.size(), "PDB type server dependencies");
- print(precompTypeIndexMappings.size(), "Precomp OBJ dependencies");
+ print(TpiSource::countTypeServerPDBs(), "PDB type server dependencies");
+ print(TpiSource::countPrecompObjs(), "Precomp OBJ dependencies");
print(tMerger.getTypeTable().size() + tMerger.getIDTable().size(),
"Merged TPI records");
print(pdbStrTab.size(), "Output PDB strings");
TypeIndex typeIndex;
uint64_t totalInputSize() const { return uint64_t(dupCount) * typeSize; }
bool operator<(const TypeSizeInfo &rhs) const {
+ if (totalInputSize() == rhs.totalInputSize())
+ return typeIndex < rhs.typeIndex;
return totalInputSize() < rhs.totalInputSize();
}
};
}
};
- printLargeInputTypeRecs("TPI", tpiCounts, tMerger.getTypeTable());
- printLargeInputTypeRecs("IPI", ipiCounts, tMerger.getIDTable());
+ printLargeInputTypeRecs("TPI", tMerger.tpiCounts, tMerger.getTypeTable());
+ printLargeInputTypeRecs("IPI", tMerger.ipiCounts, tMerger.getIDTable());
message(buffer);
}
}
}
+void PDBLinker::addNamedStreams() {
+ for (const auto &streamFile : config->namedStreams) {
+ const StringRef stream = streamFile.getKey(), file = streamFile.getValue();
+ ErrorOr<std::unique_ptr<MemoryBuffer>> dataOrErr =
+ MemoryBuffer::getFile(file);
+ if (!dataOrErr) {
+ warn("Cannot open input file: " + file);
+ continue;
+ }
+ exitOnErr(builder.addNamedStream(stream, (*dataOrErr)->getBuffer()));
+ }
+}
+
static codeview::CPUType toCodeViewMachine(COFF::MachineTypes machine) {
switch (machine) {
case COFF::IMAGE_FILE_MACHINE_AMD64:
a.split(s, '"');
r.append(join(s, "\"\""));
} else {
- r.append(a);
+ r.append(std::string(a));
}
if (hasWS || hasQ)
r.push_back('"');
}
static void addCommonLinkerModuleSymbols(StringRef path,
- pdb::DbiModuleDescriptorBuilder &mod,
- BumpPtrAllocator &allocator) {
+ pdb::DbiModuleDescriptorBuilder &mod) {
ObjNameSym ons(SymbolRecordKind::ObjNameSym);
EnvBlockSym ebs(SymbolRecordKind::EnvBlockSym);
Compile3Sym cs(SymbolRecordKind::Compile3Sym);
ebs.Fields.push_back("cmd");
ebs.Fields.push_back(argStr);
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ons, allocator, CodeViewContainer::Pdb));
+ ons, bAlloc, CodeViewContainer::Pdb));
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cs, allocator, CodeViewContainer::Pdb));
+ cs, bAlloc, CodeViewContainer::Pdb));
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ebs, allocator, CodeViewContainer::Pdb));
+ ebs, bAlloc, CodeViewContainer::Pdb));
}
static void addLinkerModuleCoffGroup(PartialSection *sec,
pdb::DbiModuleDescriptorBuilder &mod,
- OutputSection &os,
- BumpPtrAllocator &allocator) {
+ OutputSection &os) {
// If there's a section, there's at least one chunk
assert(!sec->chunks.empty());
const Chunk *firstChunk = *sec->chunks.begin();
cgs.Characteristics |= llvm::COFF::IMAGE_SCN_MEM_WRITE;
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cgs, allocator, CodeViewContainer::Pdb));
+ cgs, bAlloc, CodeViewContainer::Pdb));
}
static void addLinkerModuleSectionSymbol(pdb::DbiModuleDescriptorBuilder &mod,
- OutputSection &os,
- BumpPtrAllocator &allocator) {
+ OutputSection &os) {
SectionSym sym(SymbolRecordKind::SectionSym);
sym.Alignment = 12; // 2^12 = 4KB
sym.Characteristics = os.header.Characteristics;
sym.Rva = os.getRVA();
sym.SectionNumber = os.sectionIndex;
mod.addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- sym, allocator, CodeViewContainer::Pdb));
+ sym, bAlloc, CodeViewContainer::Pdb));
// Skip COFF groups in MinGW because it adds a significant footprint to the
// PDB, due to each function being in its own section
// Output COFF groups for individual chunks of this section.
for (PartialSection *sec : os.contribSections) {
- addLinkerModuleCoffGroup(sec, mod, os, allocator);
+ addLinkerModuleCoffGroup(sec, mod, os);
}
}
ts.Offset = thunkChunk->getRVA() - thunkOS->getRVA();
mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- ons, alloc, CodeViewContainer::Pdb));
+ ons, bAlloc, CodeViewContainer::Pdb));
mod->addSymbol(codeview::SymbolSerializer::writeOneSymbol(
- cs, alloc, CodeViewContainer::Pdb));
+ cs, bAlloc, CodeViewContainer::Pdb));
SmallVector<SymbolScope, 4> scopes;
CVSymbol newSym = codeview::SymbolSerializer::writeOneSymbol(
- ts, alloc, CodeViewContainer::Pdb);
+ ts, bAlloc, CodeViewContainer::Pdb);
scopeStackOpen(scopes, mod->getNextSymbolOffset(), newSym);
mod->addSymbol(newSym);
- newSym = codeview::SymbolSerializer::writeOneSymbol(es, alloc,
+ newSym = codeview::SymbolSerializer::writeOneSymbol(es, bAlloc,
CodeViewContainer::Pdb);
scopeStackClose(scopes, mod->getNextSymbolOffset(), file);
}
// Creates a PDB file.
-void createPDB(SymbolTable *symtab,
- ArrayRef<OutputSection *> outputSections,
- ArrayRef<uint8_t> sectionTable,
- llvm::codeview::DebugInfo *buildId) {
+void lld::coff::createPDB(SymbolTable *symtab,
+ ArrayRef<OutputSection *> outputSections,
+ ArrayRef<uint8_t> sectionTable,
+ llvm::codeview::DebugInfo *buildId) {
ScopedTimer t1(totalPdbLinkTimer);
PDBLinker pdb(symtab);
pdb.addImportFilesToPDB(outputSections);
pdb.addSections(outputSections, sectionTable);
pdb.addNatvisFiles();
+ pdb.addNamedStreams();
+ pdb.addPublicsToPDB();
ScopedTimer t2(diskCommitTimer);
codeview::GUID guid;
uint32_t pdbFilePathNI = dbiBuilder.addECName(nativePath);
auto &linkerModule = exitOnErr(dbiBuilder.addModuleInfo("* Linker *"));
linkerModule.setPdbFilePathNI(pdbFilePathNI);
- addCommonLinkerModuleSymbols(nativePath, linkerModule, alloc);
+ addCommonLinkerModuleSymbols(nativePath, linkerModule);
// Add section contributions. They must be ordered by ascending RVA.
for (OutputSection *os : outputSections) {
- addLinkerModuleSectionSymbol(linkerModule, *os, alloc);
+ addLinkerModuleSectionSymbol(linkerModule, *os);
for (Chunk *c : os->chunks) {
pdb::SectionContrib sc =
createSectionContrib(c, linkerModule.getModuleIndex());
ArrayRef<object::coff_section> sections = {
(const object::coff_section *)sectionTable.data(),
sectionTable.size() / sizeof(object::coff_section)};
- sectionMap = pdb::DbiStreamBuilder::createSectionMap(sections);
- dbiBuilder.setSectionMap(sectionMap);
+ dbiBuilder.createSectionMap(sections);
// Add COFF section header stream.
exitOnErr(
// table are stored in the output arguments. Returns whether a line table was
// found.
static bool findLineTable(const SectionChunk *c, uint32_t addr,
- DebugStringTableSubsectionRef &cVStrTab,
+ DebugStringTableSubsectionRef &cvStrTab,
DebugChecksumsSubsectionRef &checksums,
DebugLinesSubsectionRef &lines,
uint32_t &offsetInLinetable) {
for (const DebugSubsectionRecord &ss : subsections) {
switch (ss.kind()) {
case DebugSubsectionKind::StringTable: {
- assert(!cVStrTab.valid() &&
+ assert(!cvStrTab.valid() &&
"Encountered multiple string table subsections!");
- exitOnErr(cVStrTab.initialize(ss.getRecordData()));
+ exitOnErr(cvStrTab.initialize(ss.getRecordData()));
break;
}
case DebugSubsectionKind::FileChecksums:
break;
}
- if (cVStrTab.valid() && checksums.valid() && lines.header())
+ if (cvStrTab.valid() && checksums.valid() && lines.header())
return true;
}
}
// offset into the given chunk and return them, or None if a line table was
// not found.
Optional<std::pair<StringRef, uint32_t>>
-getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
+lld::coff::getFileLineCodeView(const SectionChunk *c, uint32_t addr) {
ExitOnError exitOnErr;
- DebugStringTableSubsectionRef cVStrTab;
+ DebugStringTableSubsectionRef cvStrTab;
DebugChecksumsSubsectionRef checksums;
DebugLinesSubsectionRef lines;
uint32_t offsetInLinetable;
- if (!findLineTable(c, addr, cVStrTab, checksums, lines, offsetInLinetable))
+ if (!findLineTable(c, addr, cvStrTab, checksums, lines, offsetInLinetable))
return None;
Optional<uint32_t> nameIndex;
lineNumber = li.getStartLine();
}
StringRef filename =
- exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ exitOnErr(getFileName(cvStrTab, checksums, *nameIndex));
return std::make_pair(filename, *lineNumber);
}
nameIndex = entry.NameIndex;
}
if (!nameIndex)
return None;
- StringRef filename = exitOnErr(getFileName(cVStrTab, checksums, *nameIndex));
+ StringRef filename = exitOnErr(getFileName(cvStrTab, checksums, *nameIndex));
return std::make_pair(filename, *lineNumber);
}
-
-} // namespace coff
-} // namespace lld
// of all references to that symbol from that file. If no debug information is
// available, returns just the name of the file, else one string per actual
// reference as described in the debug info.
-std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
+// Returns up to maxStrings string descriptions, along with the total number of
+// locations found.
+static std::pair<std::vector<std::string>, size_t>
+getSymbolLocations(ObjFile *file, uint32_t symIndex, size_t maxStrings) {
struct Location {
Symbol *sym;
std::pair<StringRef, uint32_t> fileLine;
};
std::vector<Location> locations;
+ size_t numLocations = 0;
for (Chunk *c : file->getChunks()) {
auto *sc = dyn_cast<SectionChunk>(c);
for (const coff_relocation &r : sc->getRelocs()) {
if (r.SymbolTableIndex != symIndex)
continue;
+ numLocations++;
+ if (locations.size() >= maxStrings)
+ continue;
+
Optional<std::pair<StringRef, uint32_t>> fileLine =
getFileLine(sc, r.VirtualAddress);
Symbol *sym = getSymbol(sc, r.VirtualAddress);
}
}
- if (locations.empty())
- return std::vector<std::string>({"\n>>> referenced by " + toString(file)});
+ if (maxStrings == 0)
+ return std::make_pair(std::vector<std::string>(), numLocations);
+
+ if (numLocations == 0)
+ return std::make_pair(
+ std::vector<std::string>{"\n>>> referenced by " + toString(file)}, 1);
std::vector<std::string> symbolLocations(locations.size());
size_t i = 0;
if (loc.sym)
os << ":(" << toString(*loc.sym) << ')';
}
- return symbolLocations;
+ return std::make_pair(symbolLocations, numLocations);
}
-std::vector<std::string> getSymbolLocations(InputFile *file,
- uint32_t symIndex) {
+std::vector<std::string> getSymbolLocations(ObjFile *file, uint32_t symIndex) {
+ return getSymbolLocations(file, symIndex, SIZE_MAX).first;
+}
+
+static std::pair<std::vector<std::string>, size_t>
+getSymbolLocations(InputFile *file, uint32_t symIndex, size_t maxStrings) {
if (auto *o = dyn_cast<ObjFile>(file))
- return getSymbolLocations(o, symIndex);
- if (auto *b = dyn_cast<BitcodeFile>(file))
- return getSymbolLocations(b);
+ return getSymbolLocations(o, symIndex, maxStrings);
+ if (auto *b = dyn_cast<BitcodeFile>(file)) {
+ std::vector<std::string> symbolLocations = getSymbolLocations(b);
+ size_t numLocations = symbolLocations.size();
+ if (symbolLocations.size() > maxStrings)
+ symbolLocations.resize(maxStrings);
+ return std::make_pair(symbolLocations, numLocations);
+ }
llvm_unreachable("unsupported file type passed to getSymbolLocations");
- return {};
+ return std::make_pair(std::vector<std::string>(), (size_t)0);
}
// For an undefined symbol, stores all files referencing it and the index of
llvm::raw_string_ostream os(out);
os << "undefined symbol: " << toString(*undefDiag.sym);
- const size_t maxUndefReferences = 10;
- size_t i = 0, numRefs = 0;
+ const size_t maxUndefReferences = 3;
+ size_t numDisplayedRefs = 0, numRefs = 0;
for (const UndefinedDiag::File &ref : undefDiag.files) {
- std::vector<std::string> symbolLocations =
- getSymbolLocations(ref.file, ref.symIndex);
- numRefs += symbolLocations.size();
+ std::vector<std::string> symbolLocations;
+ size_t totalLocations = 0;
+ std::tie(symbolLocations, totalLocations) = getSymbolLocations(
+ ref.file, ref.symIndex, maxUndefReferences - numDisplayedRefs);
+
+ numRefs += totalLocations;
+ numDisplayedRefs += symbolLocations.size();
for (const std::string &s : symbolLocations) {
- if (i >= maxUndefReferences)
- break;
os << s;
- i++;
}
}
- if (i < numRefs)
- os << "\n>>> referenced " << numRefs - i << " more times";
+ if (numDisplayedRefs < numRefs)
+ os << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times";
errorOrWarn(os.str());
}
if (name.contains("_PchSym_"))
continue;
- if (config->mingw && handleMinGWAutomaticImport(sym, name))
+ if (config->autoImport && handleMinGWAutomaticImport(sym, name))
continue;
// Remaining undefined symbols are not fatal if /force is specified.
return addUndefined(name, nullptr, false);
}
-std::vector<StringRef> SymbolTable::compileBitcodeFiles() {
- lto.reset(new BitcodeCompiler);
- for (BitcodeFile *f : BitcodeFile::instances)
- lto->add(*f);
- return lto->compile();
-}
-
void SymbolTable::addCombinedLTOObjects() {
if (BitcodeFile::instances.empty())
return;
ScopedTimer t(ltoTimer);
- for (StringRef object : compileBitcodeFiles()) {
- auto *obj = make<ObjFile>(MemoryBufferRef(object, "lto.tmp"));
+ lto.reset(new BitcodeCompiler);
+ for (BitcodeFile *f : BitcodeFile::instances)
+ lto->add(*f);
+ for (InputFile *newObj : lto->compile()) {
+ ObjFile *obj = cast<ObjFile>(newObj);
obj->parse();
ObjFile::instances.push_back(obj);
}
// 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();
// Creates an Undefined symbol for a given name.
Symbol *addUndefined(StringRef name);
StringRef demangleInput = prefixless;
if (config->machine == I386)
demangleInput.consume_front("_");
- std::string demangled = demangle(demangleInput);
+ std::string demangled = demangle(std::string(demangleInput));
if (demangled != demangleInput)
- return prefix + demangle(demangleInput);
+ return prefix + demangle(std::string(demangleInput));
return (prefix + prefixless).str();
}
- return symName;
+ return std::string(symName);
}
std::string toString(coff::Symbol &b) {
return maybeDemangleSymbol(b.getName());
namespace coff {
-StringRef Symbol::getName() {
- // 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
- // name. Object files contain lots of non-external symbols, and creating
- // StringRefs for them (which involves lots of strlen() on the string table)
- // is a waste of time.
- if (nameData == nullptr) {
- auto *d = cast<DefinedCOFF>(this);
- StringRef nameStr;
- cast<ObjFile>(d->file)->getCOFFObj()->getSymbolName(d->sym, nameStr);
- nameData = nameStr.data();
- nameSize = nameStr.size();
- assert(nameSize == nameStr.size() && "name length truncated");
- }
- return StringRef(nameData, nameSize);
+void Symbol::computeName() {
+ assert(nameData == nullptr &&
+ "should only compute the name once for DefinedCOFF symbols");
+ auto *d = cast<DefinedCOFF>(this);
+ StringRef nameStr =
+ check(cast<ObjFile>(d->file)->getCOFFObj()->getSymbolName(d->sym));
+ nameData = nameStr.data();
+ nameSize = nameStr.size();
+ assert(nameSize == nameStr.size() && "name length truncated");
}
InputFile *Symbol::getFile() {
Kind kind() const { return static_cast<Kind>(symbolKind); }
// Returns the symbol name.
- StringRef getName();
+ StringRef getName() {
+ // 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
+ // name. Object files contain lots of non-external symbols, and creating
+ // StringRefs for them (which involves lots of strlen() on the string table)
+ // is a waste of time.
+ if (nameData == nullptr)
+ computeName();
+ return StringRef(nameData, nameSize);
+ }
void replaceKeepingName(Symbol *other, size_t size);
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
+private:
+ void computeName();
+
protected:
friend SymbolTable;
explicit Symbol(Kind k, StringRef n = "")
class TypeMerger {
public:
TypeMerger(llvm::BumpPtrAllocator &alloc)
- : typeTable(alloc), iDTable(alloc), globalTypeTable(alloc),
+ : typeTable(alloc), idTable(alloc), globalTypeTable(alloc),
globalIDTable(alloc) {}
/// Get the type table or the global type table if /DEBUG:GHASH is enabled.
inline llvm::codeview::TypeCollection &getIDTable() {
if (config->debugGHashes)
return globalIDTable;
- return iDTable;
+ return idTable;
}
/// Type records that will go into the PDB TPI stream.
llvm::codeview::MergingTypeTableBuilder typeTable;
/// Item records that will go into the PDB IPI stream.
- llvm::codeview::MergingTypeTableBuilder iDTable;
+ llvm::codeview::MergingTypeTableBuilder idTable;
/// Type records that will go into the PDB TPI stream (for /DEBUG:GHASH)
llvm::codeview::GlobalTypeTableBuilder globalTypeTable;
/// Item records that will go into the PDB IPI stream (for /DEBUG:GHASH)
llvm::codeview::GlobalTypeTableBuilder globalIDTable;
+
+ // When showSummary is enabled, these are histograms of TPI and IPI records
+ // keyed by type index.
+ SmallVector<uint32_t, 0> tpiCounts;
+ SmallVector<uint32_t, 0> ipiCounts;
};
/// Map from type index and item index in a type server PDB to the
} // namespace coff
} // namespace lld
-#endif
\ No newline at end of file
+#endif
#include "Config.h"
#include "DLL.h"
#include "InputFiles.h"
+#include "LLDMapFile.h"
#include "MapFile.h"
#include "PDB.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Timer.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-
-namespace lld {
-namespace coff {
+using namespace lld;
+using namespace lld::coff;
/* To re-generate DOSProgram:
$ cat > /tmp/DOSProgram.asm
class DebugDirectoryChunk : public NonSectionChunk {
public:
- DebugDirectoryChunk(const std::vector<Chunk *> &r, bool writeRepro)
+ DebugDirectoryChunk(const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
+ bool writeRepro)
: records(r), writeRepro(writeRepro) {}
size_t getSize() const override {
void writeTo(uint8_t *b) const override {
auto *d = reinterpret_cast<debug_directory *>(b);
- for (const Chunk *record : records) {
- OutputSection *os = record->getOutputSection();
- uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA());
- fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(),
- record->getRVA(), offs);
+ for (const std::pair<COFF::DebugType, Chunk *>& record : records) {
+ Chunk *c = record.second;
+ OutputSection *os = c->getOutputSection();
+ uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
+ fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
++d;
}
}
mutable std::vector<support::ulittle32_t *> timeDateStamps;
- const std::vector<Chunk *> &records;
+ const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
bool writeRepro;
};
mutable codeview::DebugInfo *buildId = nullptr;
};
+class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
+public:
+ ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {}
+
+ size_t getSize() const override { return 4; }
+
+ void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); }
+
+ uint32_t characteristics = 0;
+};
+
// PartialSection represents a group of chunks that contribute to an
// OutputSection. Collating a collection of PartialSections of same name and
// characteristics constitutes the OutputSection.
bool setNoSEHCharacteristic = false;
DebugDirectoryChunk *debugDirectory = nullptr;
- std::vector<Chunk *> debugRecords;
+ std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
CVDebugRecordChunk *buildId = nullptr;
ArrayRef<uint8_t> sectionTable;
static Timer codeLayoutTimer("Code Layout", Timer::root());
static Timer diskCommitTimer("Commit Output File", Timer::root());
-void writeResult() { Writer().run(); }
+void lld::coff::writeResult() { Writer().run(); }
void OutputSection::addChunk(Chunk *c) {
chunks.push_back(c);
void Writer::run() {
ScopedTimer t1(codeLayoutTimer);
+ // First, clear the output sections from previous runs
+ outputSections.clear();
+
createImportTables();
createSections();
createMiscChunks();
}
writeBuildId();
+ writeLLDMapFile(outputSections);
writeMapFile(outputSections);
if (errorCount())
// Create Debug Information Chunks
OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
- if (config->debug || config->repro) {
+ if (config->debug || config->repro || config->cetCompat) {
debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
+ debugDirectory->setAlignment(4);
debugInfoSec->addChunk(debugDirectory);
}
// allowing a debugger to match a PDB and an executable. So we need it even
// if we're ultimately not going to write CodeView data to the PDB.
buildId = make<CVDebugRecordChunk>();
- debugRecords.push_back(buildId);
+ debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
+ }
- for (Chunk *c : debugRecords)
- debugInfoSec->addChunk(c);
+ if (config->cetCompat) {
+ ExtendedDllCharacteristicsChunk *extendedDllChars =
+ make<ExtendedDllCharacteristicsChunk>(
+ IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT);
+ debugRecords.push_back(
+ {COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars});
+ }
+
+ if (debugRecords.size() > 0) {
+ for (std::pair<COFF::DebugType, Chunk *> r : debugRecords)
+ debugInfoSec->addChunk(r.second);
}
// Create SEH table. x86-only.
if (config->guardCF != GuardCFLevel::Off)
createGuardCFTables();
- if (config->mingw) {
+ if (config->autoImport)
createRuntimePseudoRelocs();
+ if (config->mingw)
insertCtorDtorSymbols();
- }
}
// Create .idata section for the DLL-imported symbol table.
pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_GUARD_CF;
if (config->integrityCheck)
pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY;
- if (setNoSEHCharacteristic)
+ if (setNoSEHCharacteristic || config->noSEH)
pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_NO_SEH;
if (config->terminalServerAware)
pe->DLLCharacteristics |= IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE;
sc->getRuntimePseudoRelocs(rels);
}
+ if (!config->pseudoRelocs) {
+ // Not writing any pseudo relocs; if some were needed, error out and
+ // indicate what required them.
+ for (const RuntimePseudoReloc &rpr : rels)
+ error("automatic dllimport of " + rpr.sym->getName() + " in " +
+ toString(rpr.target->file) + " requires pseudo relocations");
+ return;
+ }
+
if (!rels.empty())
log("Writing " + Twine(rels.size()) + " runtime pseudo relocations");
PseudoRelocTableChunk *table = make<PseudoRelocTableChunk>(rels);
uint8_t *end = bufAddr(lastPdata) + lastPdata->getSize();
if (config->machine == AMD64) {
struct Entry { ulittle32_t begin, end, unwind; };
+ if ((end - begin) % sizeof(Entry) != 0) {
+ fatal("unexpected .pdata size: " + Twine(end - begin) +
+ " is not a multiple of " + Twine(sizeof(Entry)));
+ }
parallelSort(
MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
[](const Entry &a, const Entry &b) { return a.begin < b.begin; });
}
if (config->machine == ARMNT || config->machine == ARM64) {
struct Entry { ulittle32_t begin, unwind; };
+ if ((end - begin) % sizeof(Entry) != 0) {
+ fatal("unexpected .pdata size: " + Twine(end - begin) +
+ " is not a multiple of " + Twine(sizeof(Entry)));
+ }
parallelSort(
MutableArrayRef<Entry>((Entry *)begin, (Entry *)end),
[](const Entry &a, const Entry &b) { return a.begin < b.begin; });
return it->second;
return nullptr;
}
-
-} // namespace coff
-} // namespace lld
set(tablegen_deps intrinsics_gen)
endif()
+set(LLD_SYSTEM_LIBS ${LLVM_PTHREAD_LIB})
+
+if(NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB)
+ list(APPEND LLD_SYSTEM_LIBS atomic)
+endif()
+
find_first_existing_vc_file("${LLVM_MAIN_SRC_DIR}" llvm_vc)
find_first_existing_vc_file("${LLD_SOURCE_DIR}" lld_vc)
Reproduce.cpp
Strings.cpp
TargetOptionsCommandFlags.cpp
- Threads.cpp
Timer.cpp
VCSVersion.inc
Version.cpp
Target
LINK_LIBS
- ${LLVM_PTHREAD_LIB}
+ ${LLD_SYSTEM_LIBS}
DEPENDS
${tablegen_deps}
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/ADT/Twine.h"
#include "llvm/IR/DiagnosticInfo.h"
// avoid intermittent crashes on Windows when exiting.
llvm_shutdown();
- lld::outs().flush();
- lld::errs().flush();
+ {
+ std::lock_guard<std::mutex> lock(mu);
+ lld::outs().flush();
+ lld::errs().flush();
+ }
_exit(val);
}
// extracted from an error message using regexps.
std::string ErrorHandler::getLocation(const Twine &msg) {
if (!vsDiagnostics)
- return logName;
+ return std::string(logName);
static std::regex regexes[] = {
std::regex(
return m.str(1) + "(" + m.str(2) + ")";
}
- return logName;
+ return std::string(logName);
}
void ErrorHandler::log(const Twine &msg) {
}
}
- std::lock_guard<std::mutex> lock(mu);
+ bool exit = false;
+ {
+ std::lock_guard<std::mutex> lock(mu);
+
+ if (errorLimit == 0 || errorCount < errorLimit) {
+ lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
+ << "error: " << Colors::RESET << msg << "\n";
+ } else if (errorCount == errorLimit) {
+ lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
+ << "error: " << Colors::RESET << errorLimitExceededMsg
+ << "\n";
+ exit = exitEarly;
+ }
- if (errorLimit == 0 || errorCount < errorLimit) {
- lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
- << "error: " << Colors::RESET << msg << "\n";
- } else if (errorCount == errorLimit) {
- lld::errs() << sep << getLocation(msg) << ": " << Colors::RED
- << "error: " << Colors::RESET << errorLimitExceededMsg << "\n";
- if (exitEarly)
- exitLld(1);
+ sep = getSeparator(msg);
+ ++errorCount;
}
- sep = getSeparator(msg);
- ++errorCount;
+ if (exit)
+ exitLld(1);
}
void ErrorHandler::fatal(const Twine &msg) {
//===----------------------------------------------------------------------===//
#include "lld/Common/Filesystem.h"
-#include "lld/Common/Threads.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/Path.h"
#if LLVM_ON_UNIX
#include <unistd.h>
#endif
// This function spawns a background thread to remove the file.
// The calling thread returns almost immediately.
void lld::unlinkAsync(StringRef path) {
+ if (!sys::fs::exists(path) || !sys::fs::is_regular_file(path))
+ return;
+
// Removing a file is async on windows.
#if defined(_WIN32)
+ // On Windows co-operative programs can be expected to open LLD's
+ // output in FILE_SHARE_DELETE mode. This allows us to delete the
+ // file (by moving it to a temporary filename and then deleting
+ // it) so that we can link another output file that overwrites
+ // the existing file, even if the current file is in use.
+ //
+ // This is done on a best effort basis - we do not error if the
+ // operation fails. The consequence is merely that the user
+ // experiences an inconvenient work-flow.
+ //
+ // The code here allows LLD to work on all versions of Windows.
+ // However, at Windows 10 1903 it seems that the behavior of
+ // Windows has changed, so that we could simply delete the output
+ // file. This code should be simplified once support for older
+ // versions of Windows is dropped.
+ //
+ // Warning: It seems that the WINVER and _WIN32_WINNT preprocessor
+ // defines affect the behavior of the Windows versions of the calls
+ // we are using here. If this code stops working this is worth
+ // bearing in mind.
+ SmallString<128> tmpName;
+ if (!sys::fs::createUniqueFile(path + "%%%%%%%%.tmp", tmpName)) {
+ if (!sys::fs::rename(path, tmpName))
+ path = tmpName;
+ else
+ sys::fs::remove(tmpName);
+ }
sys::fs::remove(path);
#else
- if (!threadsEnabled || !sys::fs::exists(path) ||
- !sys::fs::is_regular_file(path))
+ if (parallel::strategy.ThreadsRequested == 1)
return;
// We cannot just remove path from a different thread because we are now going
std::string lld::relativeToRoot(StringRef path) {
SmallString<128> abs = path;
if (fs::make_absolute(abs))
- return path;
+ return std::string(path);
path::remove_dots(abs, /*remove_dot_dot=*/true);
// This is Windows specific. root_name() returns a drive letter
std::string lld::quote(StringRef s) {
if (s.contains(' '))
return ("\"" + s + "\"").str();
- return s;
+ return std::string(s);
}
// Converts an Arg to a string representation suitable for a response file.
// To show an Arg in a diagnostic, use Arg::getAsString() instead.
std::string lld::toString(const opt::Arg &arg) {
- std::string k = arg.getSpelling();
+ std::string k = std::string(arg.getSpelling());
if (arg.getNumValues() == 0)
return k;
std::string v = quote(arg.getValue());
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/LLVM.h"
#include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/GlobPattern.h"
#include <algorithm>
#include <mutex>
// does not look like a C++ symbol name to avoid getting unexpected
// result for a C symbol that happens to match a mangled type name.
if (!name.startswith("_Z"))
- return name;
+ return std::string(name);
- return demangle(name);
+ return demangle(std::string(name));
}
-StringMatcher::StringMatcher(ArrayRef<StringRef> pat) {
- for (StringRef s : pat) {
- Expected<GlobPattern> pat = GlobPattern::create(s);
- if (!pat)
- error(toString(pat.takeError()));
- else
- patterns.push_back(*pat);
+SingleStringMatcher::SingleStringMatcher(StringRef Pattern) {
+ if (Pattern.size() > 2 && Pattern.startswith("\"") &&
+ Pattern.endswith("\"")) {
+ ExactMatch = true;
+ ExactPattern = Pattern.substr(1, Pattern.size() - 2);
+ } else {
+ Expected<GlobPattern> Glob = GlobPattern::create(Pattern);
+ if (!Glob) {
+ error(toString(Glob.takeError()));
+ return;
+ }
+ ExactMatch = false;
+ GlobPatternMatcher = *Glob;
}
}
+bool SingleStringMatcher::match(StringRef s) const {
+ return ExactMatch ? (ExactPattern == s) : GlobPatternMatcher.match(s);
+}
+
bool StringMatcher::match(StringRef s) const {
- for (const GlobPattern &pat : patterns)
+ for (const SingleStringMatcher &pat : patterns)
if (pat.match(s))
return true;
return false;
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
-//
-// This file exists as a place for global variables defined in LLVM's
-// CodeGen/CommandFlags.inc. 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/Common/TargetOptionsCommandFlags.h"
-#include "llvm/CodeGen/CommandFlags.inc"
+#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.inc, which
-// would lead to multiple definitions of the command line flags.
+static llvm::codegen::RegisterCodeGenFlags CGF;
+
llvm::TargetOptions lld::initTargetOptionsFromCodeGenFlags() {
- return ::InitTargetOptionsFromCodeGenFlags();
+ return llvm::codegen::InitTargetOptionsFromCodeGenFlags();
}
llvm::Optional<llvm::Reloc::Model> lld::getRelocModelFromCMModel() {
- return getRelocModel();
+ return llvm::codegen::getExplicitRelocModel();
}
llvm::Optional<llvm::CodeModel::Model> lld::getCodeModelFromCMModel() {
- return getCodeModel();
+ return llvm::codegen::getExplicitCodeModel();
}
-std::string lld::getCPUStr() { return ::getCPUStr(); }
+std::string lld::getCPUStr() { return llvm::codegen::getCPUStr(); }
-std::vector<std::string> lld::getMAttrs() { return ::MAttrs; }
+std::vector<std::string> lld::getMAttrs() { return llvm::codegen::getMAttrs(); }
using namespace lld;
using namespace llvm;
-ScopedTimer::ScopedTimer(Timer &t) : t(&t) { t.start(); }
+ScopedTimer::ScopedTimer(Timer &t) : t(&t) {
+ startTime = std::chrono::high_resolution_clock::now();
+}
void ScopedTimer::stop() {
if (!t)
return;
- t->stop();
+ t->addToTotal(std::chrono::high_resolution_clock::now() - startTime);
t = nullptr;
}
ScopedTimer::~ScopedTimer() { stop(); }
-Timer::Timer(llvm::StringRef name) : name(name), parent(nullptr) {}
-Timer::Timer(llvm::StringRef name, Timer &parent)
- : name(name), parent(&parent) {}
-
-void Timer::start() {
- if (parent && total.count() == 0)
- parent->children.push_back(this);
- startTime = std::chrono::high_resolution_clock::now();
-}
-
-void Timer::stop() {
- total += (std::chrono::high_resolution_clock::now() - startTime);
+Timer::Timer(llvm::StringRef name) : name(std::string(name)) {}
+Timer::Timer(llvm::StringRef name, Timer &parent) : name(std::string(name)) {
+ parent.children.push_back(this);
}
Timer &Timer::root() {
// We want to print the grand total under all the intermediate phases, so we
// print all children first, then print the total under that.
for (const auto &child : children)
- child->print(1, totalDuration);
+ if (child->total > 0)
+ child->print(1, totalDuration);
message(std::string(49, '-'));
double Timer::millis() const {
return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(
- total)
+ std::chrono::nanoseconds(total))
.count();
}
if (recurse) {
for (const auto &child : children)
- child->print(depth + 1, totalDuration);
+ if (child->total > 0)
+ child->print(depth + 1, totalDuration);
}
}
// Returns a version string, e.g.:
// lld 9.0.0 (https://github.com/llvm/llvm-project.git 9efdd7ac5e914d3c9fa1ef)
std::string lld::getLLDVersion() {
+#ifdef LLD_VENDOR
+#define LLD_VENDOR_DISPLAY LLD_VENDOR " "
+#else
+#define LLD_VENDOR_DISPLAY
+#endif
#if defined(LLD_REPOSITORY) && defined(LLD_REVISION)
- return "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY " " LLD_REVISION ")";
+ return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING " (" LLD_REPOSITORY
+ " " LLD_REVISION ")";
#else
- return "LLD " LLD_VERSION_STRING;
+ return LLD_VENDOR_DISPLAY "LLD " LLD_VERSION_STRING;
#endif
+#undef LLD_VENDOR_DISPLAY
}
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// Helper functions to identify instructions and conditions needed to trigger
// the Cortex-A53-843419 erratum.
return patchOff;
}
-class Patch843419Section : public SyntheticSection {
+class elf::Patch843419Section : public SyntheticSection {
public:
Patch843419Section(InputSection *p, uint64_t off);
// Return address is the next instruction after the one we have just copied.
uint64_t s = getLDSTAddr() + 4;
uint64_t p = patchSym->getVA() + 4;
- target->relocateOne(buf + 4, R_AARCH64_JUMP26, s - p);
+ target->relocateNoSym(buf + 4, R_AARCH64_JUMP26, s - p);
}
void AArch64Err843419Patcher::init() {
}
return addressesChanged;
}
-} // namespace elf
-} // namespace lld
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// The documented title for Erratum 657417 is:
// "A 32bit branch instruction that spans two 4K regions can result in an
// 00001002 2 - bytes padding
// 00001004 __CortexA8657417_00000FFE: B.w func
-class Patch657417Section : public SyntheticSection {
+class elf::Patch657417Section : public SyntheticSection {
public:
Patch657417Section(InputSection *p, uint64_t off, uint32_t instr, bool isARM);
// been altered to point to us!
uint64_t s = getThumbDestAddr(getBranchAddr(), instr);
uint64_t p = getVA(4);
- target->relocateOne(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p);
+ target->relocateNoSym(buf, isARM ? R_ARM_JUMP24 : R_ARM_THM_JUMP24, s - p);
}
// Given a branch instruction spanning two 4KiB regions, at offset off from the
}
return addressesChanged;
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// Page(Expr) is the page address of the expression Expr, defined
// as (Expr & ~0xFFF). (This applies even if the machine page size
// supported by the platform has a different value.)
-uint64_t getAArch64Page(uint64_t expr) {
+uint64_t elf::getAArch64Page(uint64_t expr) {
return expr & ~static_cast<uint64_t>(0xFFF);
}
uint32_t getThunkSectionSpacing() const override;
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
bool usesOnlyLowPageBits(RelType type) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
case R_AARCH64_CONDBR19:
case R_AARCH64_JUMP26:
case R_AARCH64_TSTBR14:
+ case R_AARCH64_PLT32:
return R_PLT_PC;
case R_AARCH64_PREL16:
case R_AARCH64_PREL32:
uint64_t got = in.gotPlt->getVA();
uint64_t plt = in.plt->getVA();
- relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(got + 16) - getAArch64Page(plt + 4));
- relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
- relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
+ relocateNoSym(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(got + 16) - getAArch64Page(plt + 4));
+ relocateNoSym(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
+ relocateNoSym(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
}
void AArch64::writePlt(uint8_t *buf, const Symbol &sym,
memcpy(buf, inst, sizeof(inst));
uint64_t gotPltEntryAddr = sym.getGotPltVA();
- relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
- relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
- relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
+ relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
+ relocateNoSym(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
+ relocateNoSym(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
}
bool AArch64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
// ELF for the ARM 64-bit architecture, section Call and Jump relocations
// only permits range extension thunks for R_AARCH64_CALL26 and
// R_AARCH64_JUMP26 relocation types.
- if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
+ type != R_AARCH64_PLT32)
return false;
uint64_t dst = expr == R_PLT_PC ? s.getPltVA() : s.getVA(a);
return !inBranchRange(type, branchAddr, dst);
}
bool AArch64::inBranchRange(RelType type, uint64_t src, uint64_t dst) const {
- if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
+ type != R_AARCH64_PLT32)
return true;
// The AArch64 call and unconditional branch instructions have a range of
- // +/- 128 MiB.
- uint64_t range = 128 * 1024 * 1024;
+ // +/- 128 MiB. The PLT32 relocation supports a range up to +/- 2 GiB.
+ uint64_t range =
+ type == R_AARCH64_PLT32 ? (UINT64_C(1) << 31) : (128 * 1024 * 1024);
if (dst > src) {
// Immediate of branch is signed.
range -= 4;
write32le(loc, inst | ((imm & 0xFFFF) << 5));
}
-void AArch64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void AArch64::relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_AARCH64_ABS16:
case R_AARCH64_PREL16:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_AARCH64_ABS32:
case R_AARCH64_PREL32:
- checkIntUInt(loc, val, 32, type);
+ checkIntUInt(loc, val, 32, rel);
+ write32le(loc, val);
+ break;
+ case R_AARCH64_PLT32:
+ checkInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_AARCH64_ABS64:
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSDESC_ADR_PAGE21:
- checkInt(loc, val, 33, type);
+ checkInt(loc, val, 33, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_ADR_PREL_PG_HI21_NC:
write32AArch64Addr(loc, val >> 12);
break;
case R_AARCH64_ADR_PREL_LO21:
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
write32AArch64Addr(loc, val);
break;
case R_AARCH64_JUMP26:
write32le(loc, 0x14000000);
LLVM_FALLTHROUGH;
case R_AARCH64_CALL26:
- checkInt(loc, val, 28, type);
+ checkInt(loc, val, 28, rel);
or32le(loc, (val & 0x0FFFFFFC) >> 2);
break;
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 21, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 21, rel);
or32le(loc, (val & 0x1FFFFC) << 3);
break;
case R_AARCH64_LDST8_ABS_LO12_NC:
break;
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
- checkAlignment(loc, val, 2, type);
+ checkAlignment(loc, val, 2, rel);
or32AArch64Imm(loc, getBits(val, 1, 11));
break;
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
- checkAlignment(loc, val, 4, type);
+ checkAlignment(loc, val, 4, rel);
or32AArch64Imm(loc, getBits(val, 2, 11));
break;
case R_AARCH64_LDST64_ABS_LO12_NC:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
case R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
case R_AARCH64_TLSDESC_LD64_LO12:
- checkAlignment(loc, val, 8, type);
+ checkAlignment(loc, val, 8, rel);
or32AArch64Imm(loc, getBits(val, 3, 11));
break;
case R_AARCH64_LDST128_ABS_LO12_NC:
case R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC:
- checkAlignment(loc, val, 16, type);
+ checkAlignment(loc, val, 16, rel);
or32AArch64Imm(loc, getBits(val, 4, 11));
break;
case R_AARCH64_MOVW_UABS_G0:
- checkUInt(loc, val, 16, type);
+ checkUInt(loc, val, 16, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G0_NC:
or32le(loc, (val & 0xFFFF) << 5);
break;
case R_AARCH64_MOVW_UABS_G1:
- checkUInt(loc, val, 32, type);
+ checkUInt(loc, val, 32, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G1_NC:
or32le(loc, (val & 0xFFFF0000) >> 11);
break;
case R_AARCH64_MOVW_UABS_G2:
- checkUInt(loc, val, 48, type);
+ checkUInt(loc, val, 48, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_UABS_G2_NC:
or32le(loc, (val & 0xFFFF00000000) >> 27);
case R_AARCH64_MOVW_PREL_G0:
case R_AARCH64_MOVW_SABS_G0:
case R_AARCH64_TLSLE_MOVW_TPREL_G0:
- checkInt(loc, val, 17, type);
+ checkInt(loc, val, 17, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G0_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
case R_AARCH64_MOVW_PREL_G1:
case R_AARCH64_MOVW_SABS_G1:
case R_AARCH64_TLSLE_MOVW_TPREL_G1:
- checkInt(loc, val, 33, type);
+ checkInt(loc, val, 33, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G1_NC:
case R_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case R_AARCH64_MOVW_PREL_G2:
case R_AARCH64_MOVW_SABS_G2:
case R_AARCH64_TLSLE_MOVW_TPREL_G2:
- checkInt(loc, val, 49, type);
+ checkInt(loc, val, 49, rel);
LLVM_FALLTHROUGH;
case R_AARCH64_MOVW_PREL_G2_NC:
writeSMovWImm(loc, val >> 32);
writeSMovWImm(loc, val >> 48);
break;
case R_AARCH64_TSTBR14:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
or32le(loc, (val & 0xFFFC) << 3);
break;
case R_AARCH64_TLSLE_ADD_TPREL_HI12:
- checkUInt(loc, val, 24, type);
+ checkUInt(loc, val, 24, rel);
or32AArch64Imm(loc, val >> 12);
break;
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
}
}
-void AArch64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void AArch64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// TLSDESC Global-Dynamic relocation are in the form:
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
// movk x0, #0x10
// nop
// nop
- checkUInt(loc, val, 32, type);
+ checkUInt(loc, val, 32, rel);
- switch (type) {
+ switch (rel.type) {
case R_AARCH64_TLSDESC_ADD_LO12:
case R_AARCH64_TLSDESC_CALL:
write32le(loc, 0xd503201f); // nop
}
}
-void AArch64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+void AArch64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// TLSDESC Global-Dynamic relocation are in the form:
// adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
// ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12]
// nop
// nop
- switch (type) {
+ switch (rel.type) {
case R_AARCH64_TLSDESC_ADD_LO12:
case R_AARCH64_TLSDESC_CALL:
write32le(loc, 0xd503201f); // nop
break;
case R_AARCH64_TLSDESC_ADR_PAGE21:
write32le(loc, 0x90000000); // adrp
- relocateOne(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val);
+ relocateNoSym(loc, R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21, val);
break;
case R_AARCH64_TLSDESC_LD64_LO12:
write32le(loc, 0xf9400000); // ldr
- relocateOne(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val);
+ relocateNoSym(loc, R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-void AArch64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
- checkUInt(loc, val, 32, type);
+void AArch64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ checkUInt(loc, val, 32, rel);
- if (type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
+ if (rel.type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
// Generate MOVZ.
uint32_t regNo = read32le(loc) & 0x1f;
write32le(loc, (0xd2a00000 | regNo) | (((val >> 16) & 0xffff) << 5));
return;
}
- if (type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
+ if (rel.type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
// Generate MOVK.
uint32_t regNo = read32le(loc) & 0x1f;
write32le(loc, (0xf2800000 | regNo) | ((val & 0xffff) << 5));
// the function in an executable being taken by a shared library.
// FIXME: There is a potential optimization to omit the BTI if we detect
// that the address of the PLT entry isn't taken.
+ // The PAC PLT entries require dynamic loader support and this isn't known
+ // from properties in the objects, so we use the command line flag.
btiEntry = btiHeader && !config->shared;
- pacEntry = (config->andFeatures & GNU_PROPERTY_AARCH64_FEATURE_1_PAC);
+ pacEntry = config->zPacPlt;
if (btiEntry || pacEntry) {
pltEntrySize = 24;
}
memcpy(buf, pltData, sizeof(pltData));
- relocateOne(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(got + 16) - getAArch64Page(plt + 8));
- relocateOne(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
- relocateOne(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
+ relocateNoSym(buf + 4, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(got + 16) - getAArch64Page(plt + 8));
+ relocateNoSym(buf + 8, R_AARCH64_LDST64_ABS_LO12_NC, got + 16);
+ relocateNoSym(buf + 12, R_AARCH64_ADD_ABS_LO12_NC, got + 16);
if (!btiHeader)
// We didn't add the BTI c instruction so round out size with NOP.
memcpy(buf + sizeof(pltData), nopData, sizeof(nopData));
uint64_t gotPltEntryAddr = sym.getGotPltVA();
memcpy(buf, addrInst, sizeof(addrInst));
- relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(gotPltEntryAddr) -
- getAArch64Page(pltEntryAddr));
- relocateOne(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
- relocateOne(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
+ relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(gotPltEntryAddr) - getAArch64Page(pltEntryAddr));
+ relocateNoSym(buf + 4, R_AARCH64_LDST64_ABS_LO12_NC, gotPltEntryAddr);
+ relocateNoSym(buf + 8, R_AARCH64_ADD_ABS_LO12_NC, gotPltEntryAddr);
if (pacEntry)
memcpy(buf + sizeof(addrInst), pacBr, sizeof(pacBr));
return &t;
}
-TargetInfo *getAArch64TargetInfo() { return getTargetInfo(); }
-
-} // namespace elf
-} // namespace lld
+TargetInfo *elf::getAArch64TargetInfo() { return getTargetInfo(); }
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class AMDGPU final : public TargetInfo {
public:
AMDGPU();
uint32_t calcEFlags() const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
return ret;
}
-void AMDGPU::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void AMDGPU::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_AMDGPU_ABS32:
case R_AMDGPU_GOTPCREL:
case R_AMDGPU_GOTPCREL32_LO:
return R_AMDGPU_NONE;
}
-TargetInfo *getAMDGPUTargetInfo() {
+TargetInfo *elf::getAMDGPUTargetInfo() {
static AMDGPU target;
return ⌖
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class ARM final : public TargetInfo {
int64_t a) const override;
uint32_t getThunkSectionSpacing() const override;
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
ipltEntrySize = 16;
trapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
needsThunks = true;
+ defaultMaxPageSize = 65536;
}
uint32_t ARM::calcEFlags() const {
return R_TLSGD_PC;
case R_ARM_TLS_LDM32:
return R_TLSLD_PC;
+ case R_ARM_TLS_LDO32:
+ return R_DTPREL;
case R_ARM_BASE_PREL:
// B(S) + A - P
// FIXME: currently B(S) assumed to be .got, this may not hold for all
case R_ARM_THM_MOVW_PREL_NC:
case R_ARM_THM_MOVT_PREL:
return R_PC;
+ case R_ARM_ALU_PC_G0:
+ case R_ARM_LDR_PC_G0:
+ case R_ARM_THM_ALU_PREL_11_0:
+ case R_ARM_THM_PC8:
+ case R_ARM_THM_PC12:
+ return R_ARM_PCA;
+ case R_ARM_MOVW_BREL_NC:
+ case R_ARM_MOVW_BREL:
+ case R_ARM_MOVT_BREL:
+ case R_ARM_THM_MOVW_BREL_NC:
+ case R_ARM_THM_MOVW_BREL:
+ case R_ARM_THM_MOVT_BREL:
+ return R_ARM_SBREL;
case R_ARM_NONE:
return R_NONE;
case R_ARM_TLS_LE32:
}
bool ARM::needsThunk(RelExpr expr, RelType type, const InputFile *file,
- uint64_t branchAddr, const Symbol &s, int64_t /*a*/) const {
+ uint64_t branchAddr, const Symbol &s,
+ int64_t /*a*/) const {
// If S is an undefined weak symbol and does not have a PLT entry then it
// will be resolved as a branch to the next instruction.
if (s.isUndefWeak() && !s.isInPlt())
return distance <= range;
}
-void ARM::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+// Helper to produce message text when LLD detects that a CALL relocation to
+// a non STT_FUNC symbol that may result in incorrect interworking between ARM
+// or Thumb.
+static void stateChangeWarning(uint8_t *loc, RelType relt, const Symbol &s) {
+ assert(!s.isFunc());
+ if (s.isSection()) {
+ // Section symbols must be defined and in a section. Users cannot change
+ // the type. Use the section name as getName() returns an empty string.
+ warn(getErrorLocation(loc) + "branch and link relocation: " +
+ toString(relt) + " to STT_SECTION symbol " +
+ cast<Defined>(s).section->name + " ; interworking not performed");
+ } else {
+ // Warn with hint on how to alter the symbol type.
+ warn(getErrorLocation(loc) + "branch and link relocation: " +
+ toString(relt) + " to non STT_FUNC symbol: " + s.getName() +
+ " interworking not performed; consider using directive '.type " +
+ s.getName() +
+ ", %function' to give symbol type STT_FUNC if"
+ " interworking between ARM and Thumb is required");
+ }
+}
+
+// Utility functions taken from ARMAddressingModes.h, only changes are LLD
+// coding style.
+
+// Rotate a 32-bit unsigned value right by a specified amt of bits.
+static uint32_t rotr32(uint32_t val, uint32_t amt) {
+ assert(amt < 32 && "Invalid rotate amount");
+ return (val >> amt) | (val << ((32 - amt) & 31));
+}
+
+// Rotate a 32-bit unsigned value left by a specified amt of bits.
+static uint32_t rotl32(uint32_t val, uint32_t amt) {
+ assert(amt < 32 && "Invalid rotate amount");
+ return (val << amt) | (val >> ((32 - amt) & 31));
+}
+
+// Try to encode a 32-bit unsigned immediate imm with an immediate shifter
+// operand, this form is an 8-bit immediate rotated right by an even number of
+// bits. We compute the rotate amount to use. If this immediate value cannot be
+// handled with a single shifter-op, determine a good rotate amount that will
+// take a maximal chunk of bits out of the immediate.
+static uint32_t getSOImmValRotate(uint32_t imm) {
+ // 8-bit (or less) immediates are trivially shifter_operands with a rotate
+ // of zero.
+ if ((imm & ~255U) == 0)
+ return 0;
+
+ // Use CTZ to compute the rotate amount.
+ unsigned tz = llvm::countTrailingZeros(imm);
+
+ // Rotate amount must be even. Something like 0x200 must be rotated 8 bits,
+ // not 9.
+ unsigned rotAmt = tz & ~1;
+
+ // If we can handle this spread, return it.
+ if ((rotr32(imm, rotAmt) & ~255U) == 0)
+ return (32 - rotAmt) & 31; // HW rotates right, not left.
+
+ // For values like 0xF000000F, we should ignore the low 6 bits, then
+ // retry the hunt.
+ if (imm & 63U) {
+ unsigned tz2 = countTrailingZeros(imm & ~63U);
+ unsigned rotAmt2 = tz2 & ~1;
+ if ((rotr32(imm, rotAmt2) & ~255U) == 0)
+ return (32 - rotAmt2) & 31; // HW rotates right, not left.
+ }
+
+ // Otherwise, we have no way to cover this span of bits with a single
+ // shifter_op immediate. Return a chunk of bits that will be useful to
+ // handle.
+ return (32 - rotAmt) & 31; // HW rotates right, not left.
+}
+
+void ARM::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_ARM_ABS32:
case R_ARM_BASE_PREL:
case R_ARM_GOTOFF32:
write32le(loc, val);
break;
case R_ARM_PREL31:
- checkInt(loc, val, 31, type);
+ checkInt(loc, val, 31, rel);
write32le(loc, (read32le(loc) & 0x80000000) | (val & ~0x80000000));
break;
- case R_ARM_CALL:
- // R_ARM_CALL is used for BL and BLX instructions, depending on the
- // value of bit 0 of Val, we must select a BL or BLX instruction
- if (val & 1) {
- // If bit 0 of Val is 1 the target is Thumb, we must select a BLX.
+ case R_ARM_CALL: {
+ // R_ARM_CALL is used for BL and BLX instructions, for symbols of type
+ // STT_FUNC we choose whether to write a BL or BLX depending on the
+ // value of bit 0 of Val. With bit 0 == 1 denoting Thumb. If the symbol is
+ // not of type STT_FUNC then we must preserve the original instruction.
+ // PLT entries are always ARM state so we know we don't need to interwork.
+ assert(rel.sym); // R_ARM_CALL is always reached via relocate().
+ bool bit0Thumb = val & 1;
+ bool isBlx = (read32le(loc) & 0xfe000000) == 0xfa000000;
+ // lld 10.0 and before always used bit0Thumb when deciding to write a BLX
+ // even when type not STT_FUNC.
+ if (!rel.sym->isFunc() && isBlx != bit0Thumb)
+ stateChangeWarning(loc, rel.type, *rel.sym);
+ if (rel.sym->isFunc() ? bit0Thumb : isBlx) {
// The BLX encoding is 0xfa:H:imm24 where Val = imm24:H:'1'
- checkInt(loc, val, 26, type);
+ checkInt(loc, val, 26, rel);
write32le(loc, 0xfa000000 | // opcode
((val & 2) << 23) | // H
((val >> 2) & 0x00ffffff)); // imm24
break;
}
- if ((read32le(loc) & 0xfe000000) == 0xfa000000)
- // BLX (always unconditional) instruction to an ARM Target, select an
- // unconditional BL.
- write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
+ // BLX (always unconditional) instruction to an ARM Target, select an
+ // unconditional BL.
+ write32le(loc, 0xeb000000 | (read32le(loc) & 0x00ffffff));
// fall through as BL encoding is shared with B
+ }
LLVM_FALLTHROUGH;
case R_ARM_JUMP24:
case R_ARM_PC24:
case R_ARM_PLT32:
- checkInt(loc, val, 26, type);
+ checkInt(loc, val, 26, rel);
write32le(loc, (read32le(loc) & ~0x00ffffff) | ((val >> 2) & 0x00ffffff));
break;
case R_ARM_THM_JUMP11:
- checkInt(loc, val, 12, type);
+ checkInt(loc, val, 12, rel);
write16le(loc, (read32le(loc) & 0xf800) | ((val >> 1) & 0x07ff));
break;
case R_ARM_THM_JUMP19:
// Encoding T3: Val = S:J2:J1:imm6:imm11:0
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
write16le(loc,
(read16le(loc) & 0xfbc0) | // opcode cond
((val >> 10) & 0x0400) | // S
((val >> 5) & 0x2000) | // J1
((val >> 1) & 0x07ff)); // imm11
break;
- case R_ARM_THM_CALL:
- // R_ARM_THM_CALL is used for BL and BLX instructions, depending on the
- // value of bit 0 of Val, we must select a BL or BLX instruction
- if ((val & 1) == 0) {
- // Ensure BLX destination is 4-byte aligned. As BLX instruction may
- // only be two byte aligned. This must be done before overflow check
+ case R_ARM_THM_CALL: {
+ // R_ARM_THM_CALL is used for BL and BLX instructions, for symbols of type
+ // STT_FUNC we choose whether to write a BL or BLX depending on the
+ // value of bit 0 of Val. With bit 0 == 0 denoting ARM, if the symbol is
+ // not of type STT_FUNC then we must preserve the original instruction.
+ // PLT entries are always ARM state so we know we need to interwork.
+ assert(rel.sym); // R_ARM_THM_CALL is always reached via relocate().
+ bool bit0Thumb = val & 1;
+ bool isBlx = (read16le(loc + 2) & 0x1000) == 0;
+ // lld 10.0 and before always used bit0Thumb when deciding to write a BLX
+ // even when type not STT_FUNC. PLT entries generated by LLD are always ARM.
+ if (!rel.sym->isFunc() && !rel.sym->isInPlt() && isBlx == bit0Thumb)
+ stateChangeWarning(loc, rel.type, *rel.sym);
+ if (rel.sym->isFunc() || rel.sym->isInPlt() ? !bit0Thumb : isBlx) {
+ // We are writing a BLX. Ensure BLX destination is 4-byte aligned. As
+ // the BLX instruction may only be two byte aligned. This must be done
+ // before overflow check.
val = alignTo(val, 4);
+ write16le(loc + 2, read16le(loc + 2) & ~0x1000);
+ } else {
+ write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | 1 << 12);
}
- // Bit 12 is 0 for BLX, 1 for BL
- write16le(loc + 2, (read16le(loc + 2) & ~0x1000) | (val & 1) << 12);
if (!config->armJ1J2BranchEncoding) {
// Older Arm architectures do not support R_ARM_THM_JUMP24 and have
// different encoding rules and range due to J1 and J2 always being 1.
- checkInt(loc, val, 23, type);
+ checkInt(loc, val, 23, rel);
write16le(loc,
0xf000 | // opcode
((val >> 12) & 0x07ff)); // imm11
((val >> 1) & 0x07ff)); // imm11
break;
}
+ }
// Fall through as rest of encoding is the same as B.W
LLVM_FALLTHROUGH;
case R_ARM_THM_JUMP24:
// Encoding B T4, BL T1, BLX T2: Val = S:I1:I2:imm10:imm11:0
- checkInt(loc, val, 25, type);
+ checkInt(loc, val, 25, rel);
write16le(loc,
0xf000 | // opcode
((val >> 14) & 0x0400) | // S
break;
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVW_PREL_NC:
+ case R_ARM_MOVW_BREL_NC:
write32le(loc, (read32le(loc) & ~0x000f0fff) | ((val & 0xf000) << 4) |
(val & 0x0fff));
break;
case R_ARM_MOVT_ABS:
case R_ARM_MOVT_PREL:
+ case R_ARM_MOVT_BREL:
write32le(loc, (read32le(loc) & ~0x000f0fff) |
(((val >> 16) & 0xf000) << 4) | ((val >> 16) & 0xfff));
break;
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVT_PREL:
+ case R_ARM_THM_MOVT_BREL:
// Encoding T1: A = imm4:i:imm3:imm8
write16le(loc,
0xf2c0 | // opcode
break;
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVW_PREL_NC:
+ case R_ARM_THM_MOVW_BREL_NC:
// Encoding T3: A = imm4:i:imm3:imm8
write16le(loc,
0xf240 | // opcode
((val << 4) & 0x7000) | // imm3
(val & 0x00ff)); // imm8
break;
+ case R_ARM_ALU_PC_G0: {
+ // ADR (literal) add = bit23, sub = bit22
+ // literal is a 12-bit modified immediate, made up of a 4-bit even rotate
+ // right and an 8-bit immediate. The code-sequence here is derived from
+ // ARMAddressingModes.h in llvm/Target/ARM/MCTargetDesc. In our case we
+ // want to give an error if we cannot encode the constant.
+ uint32_t opcode = 0x00800000;
+ if (val >> 63) {
+ opcode = 0x00400000;
+ val = ~val + 1;
+ }
+ if ((val & ~255U) != 0) {
+ uint32_t rotAmt = getSOImmValRotate(val);
+ // Error if we cannot encode this with a single shift
+ if (rotr32(~255U, rotAmt) & val)
+ error(getErrorLocation(loc) + "unencodeable immediate " +
+ Twine(val).str() + " for relocation " + toString(rel.type));
+ val = rotl32(val, rotAmt) | ((rotAmt >> 1) << 8);
+ }
+ write32le(loc, (read32le(loc) & 0xff0ff000) | opcode | val);
+ break;
+ }
+ case R_ARM_LDR_PC_G0: {
+ // R_ARM_LDR_PC_G0 is S + A - P, we have ((S + A) | T) - P, if S is a
+ // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
+ // bottom bit to recover S + A - P.
+ if (rel.sym->isFunc())
+ val &= ~0x1;
+ // LDR (literal) u = bit23
+ int64_t imm = val;
+ uint32_t u = 0x00800000;
+ if (imm < 0) {
+ imm = -imm;
+ u = 0;
+ }
+ checkUInt(loc, imm, 12, rel);
+ write32le(loc, (read32le(loc) & 0xff7ff000) | u | imm);
+ break;
+ }
+ case R_ARM_THM_ALU_PREL_11_0: {
+ // ADR encoding T2 (sub), T3 (add) i:imm3:imm8
+ int64_t imm = val;
+ uint16_t sub = 0;
+ if (imm < 0) {
+ imm = -imm;
+ sub = 0x00a0;
+ }
+ checkUInt(loc, imm, 12, rel);
+ write16le(loc, (read16le(loc) & 0xfb0f) | sub | (imm & 0x800) >> 1);
+ write16le(loc + 2,
+ (read16le(loc + 2) & 0x8f00) | (imm & 0x700) << 4 | (imm & 0xff));
+ break;
+ }
+ case R_ARM_THM_PC8:
+ // ADR and LDR literal encoding T1 positive offset only imm8:00
+ // R_ARM_THM_PC8 is S + A - Pa, we have ((S + A) | T) - Pa, if S is a
+ // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
+ // bottom bit to recover S + A - Pa.
+ if (rel.sym->isFunc())
+ val &= ~0x1;
+ checkUInt(loc, val, 10, rel);
+ checkAlignment(loc, val, 4, rel);
+ write16le(loc, (read16le(loc) & 0xff00) | (val & 0x3fc) >> 2);
+ break;
+ case R_ARM_THM_PC12: {
+ // LDR (literal) encoding T2, add = (U == '1') imm12
+ // imm12 is unsigned
+ // R_ARM_THM_PC12 is S + A - Pa, we have ((S + A) | T) - Pa, if S is a
+ // function then addr is 0 (modulo 2) and Pa is 0 (modulo 4) so we can clear
+ // bottom bit to recover S + A - Pa.
+ if (rel.sym->isFunc())
+ val &= ~0x1;
+ int64_t imm12 = val;
+ uint16_t u = 0x0080;
+ if (imm12 < 0) {
+ imm12 = -imm12;
+ u = 0;
+ }
+ checkUInt(loc, imm12, 12, rel);
+ write16le(loc, read16le(loc) | u);
+ write16le(loc + 2, (read16le(loc + 2) & 0xf000) | imm12);
+ break;
+ }
default:
- error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
+ error(getErrorLocation(loc) + "unrecognized relocation " +
+ toString(rel.type));
}
}
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
case R_ARM_MOVW_PREL_NC:
- case R_ARM_MOVT_PREL: {
+ case R_ARM_MOVT_PREL:
+ case R_ARM_MOVW_BREL_NC:
+ case R_ARM_MOVT_BREL: {
uint64_t val = read32le(buf) & 0x000f0fff;
return SignExtend64<16>(((val & 0x000f0000) >> 4) | (val & 0x00fff));
}
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_ABS:
case R_ARM_THM_MOVW_PREL_NC:
- case R_ARM_THM_MOVT_PREL: {
+ case R_ARM_THM_MOVT_PREL:
+ case R_ARM_THM_MOVW_BREL_NC:
+ case R_ARM_THM_MOVT_BREL: {
// Encoding T3: A = imm4:i:imm3:imm8
uint16_t hi = read16le(buf);
uint16_t lo = read16le(buf + 2);
((lo & 0x7000) >> 4) | // imm3
(lo & 0x00ff)); // imm8
}
+ case R_ARM_ALU_PC_G0: {
+ // 12-bit immediate is a modified immediate made up of a 4-bit even
+ // right rotation and 8-bit constant. After the rotation the value
+ // is zero-extended. When bit 23 is set the instruction is an add, when
+ // bit 22 is set it is a sub.
+ uint32_t instr = read32le(buf);
+ uint32_t val = rotr32(instr & 0xff, ((instr & 0xf00) >> 8) * 2);
+ return (instr & 0x00400000) ? -val : val;
+ }
+ case R_ARM_LDR_PC_G0: {
+ // ADR (literal) add = bit23, sub = bit22
+ // LDR (literal) u = bit23 unsigned imm12
+ bool u = read32le(buf) & 0x00800000;
+ uint32_t imm12 = read32le(buf) & 0xfff;
+ return u ? imm12 : -imm12;
+ }
+ case R_ARM_THM_ALU_PREL_11_0: {
+ // Thumb2 ADR, which is an alias for a sub or add instruction with an
+ // unsigned immediate.
+ // ADR encoding T2 (sub), T3 (add) i:imm3:imm8
+ uint16_t hi = read16le(buf);
+ uint16_t lo = read16le(buf + 2);
+ uint64_t imm = (hi & 0x0400) << 1 | // i
+ (lo & 0x7000) >> 4 | // imm3
+ (lo & 0x00ff); // imm8
+ // For sub, addend is negative, add is positive.
+ return (hi & 0x00f0) ? -imm : imm;
+ }
+ case R_ARM_THM_PC8:
+ // ADR and LDR (literal) encoding T1
+ // From ELF for the ARM Architecture the initial signed addend is formed
+ // from an unsigned field using expression (((imm8:00 + 4) & 0x3ff) – 4)
+ // this trick permits the PC bias of -4 to be encoded using imm8 = 0xff
+ return ((((read16le(buf) & 0xff) << 2) + 4) & 0x3ff) - 4;
+ case R_ARM_THM_PC12: {
+ // LDR (literal) encoding T2, add = (U == '1') imm12
+ bool u = read16le(buf) & 0x0080;
+ uint64_t imm12 = read16le(buf + 2) & 0x0fff;
+ return u ? imm12 : -imm12;
+ }
}
}
-TargetInfo *getARMTargetInfo() {
+TargetInfo *elf::getARMTargetInfo() {
static ARM target;
return ⌖
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class AVR final : public TargetInfo {
AVR();
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
RelExpr AVR::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
- return R_ABS;
+ switch (type) {
+ case R_AVR_7_PCREL:
+ case R_AVR_13_PCREL:
+ return R_PC;
+ default:
+ return R_ABS;
+ }
}
-void AVR::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+static void writeLDI(uint8_t *loc, uint64_t val) {
+ write16le(loc, (read16le(loc) & 0xf0f0) | (val & 0xf0) << 4 | (val & 0x0f));
+}
+
+void AVR::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
+ case R_AVR_8:
+ checkUInt(loc, val, 8, rel);
+ *loc = val;
+ break;
+ case R_AVR_16:
+ // Note: this relocation is often used between code and data space, which
+ // are 0x800000 apart in the output ELF file. The bitmask cuts off the high
+ // bit.
+ write16le(loc, val & 0xffff);
+ break;
+ case R_AVR_16_PM:
+ checkAlignment(loc, val, 2, rel);
+ checkUInt(loc, val >> 1, 16, rel);
+ write16le(loc, val >> 1);
+ break;
+ case R_AVR_32:
+ checkUInt(loc, val, 32, rel);
+ write32le(loc, val);
+ break;
+
+ case R_AVR_LDI:
+ checkUInt(loc, val, 8, rel);
+ writeLDI(loc, val & 0xff);
+ break;
+
+ case R_AVR_LO8_LDI_NEG:
+ writeLDI(loc, -val & 0xff);
+ break;
+ case R_AVR_LO8_LDI:
+ writeLDI(loc, val & 0xff);
+ break;
+ case R_AVR_HI8_LDI_NEG:
+ writeLDI(loc, (-val >> 8) & 0xff);
+ break;
+ case R_AVR_HI8_LDI:
+ writeLDI(loc, (val >> 8) & 0xff);
+ break;
+ case R_AVR_HH8_LDI_NEG:
+ writeLDI(loc, (-val >> 16) & 0xff);
+ break;
+ case R_AVR_HH8_LDI:
+ writeLDI(loc, (val >> 16) & 0xff);
+ break;
+ case R_AVR_MS8_LDI_NEG:
+ writeLDI(loc, (-val >> 24) & 0xff);
+ break;
+ case R_AVR_MS8_LDI:
+ writeLDI(loc, (val >> 24) & 0xff);
+ break;
+
+ case R_AVR_LO8_LDI_PM:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (val >> 1) & 0xff);
+ break;
+ case R_AVR_HI8_LDI_PM:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (val >> 9) & 0xff);
+ break;
+ case R_AVR_HH8_LDI_PM:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (val >> 17) & 0xff);
+ break;
+
+ case R_AVR_LO8_LDI_PM_NEG:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (-val >> 1) & 0xff);
+ break;
+ case R_AVR_HI8_LDI_PM_NEG:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (-val >> 9) & 0xff);
+ break;
+ case R_AVR_HH8_LDI_PM_NEG:
+ checkAlignment(loc, val, 2, rel);
+ writeLDI(loc, (-val >> 17) & 0xff);
+ break;
+
+ case R_AVR_PORT5:
+ checkUInt(loc, val, 5, rel);
+ write16le(loc, (read16le(loc) & 0xff07) | (val << 3));
+ break;
+ case R_AVR_PORT6:
+ checkUInt(loc, val, 6, rel);
+ write16le(loc, (read16le(loc) & 0xf9f0) | (val & 0x30) << 5 | (val & 0x0f));
+ break;
+
+ // Since every jump destination is word aligned we gain an extra bit
+ case R_AVR_7_PCREL: {
+ checkInt(loc, val, 7, rel);
+ checkAlignment(loc, val, 2, rel);
+ const uint16_t target = (val - 2) >> 1;
+ write16le(loc, (read16le(loc) & 0xfc07) | ((target & 0x7f) << 3));
+ break;
+ }
+ case R_AVR_13_PCREL: {
+ checkAlignment(loc, val, 2, rel);
+ const uint16_t target = (val - 2) >> 1;
+ write16le(loc, (read16le(loc) & 0xf000) | (target & 0xfff));
+ break;
+ }
+
+ case R_AVR_6:
+ checkInt(loc, val, 6, rel);
+ write16le(loc, (read16le(loc) & 0xd3f8) | (val & 0x20) << 8 |
+ (val & 0x18) << 7 | (val & 0x07));
+ break;
+ case R_AVR_6_ADIW:
+ checkInt(loc, val, 6, rel);
+ write16le(loc, (read16le(loc) & 0xff30) | (val & 0x30) << 2 | (val & 0x0F));
+ break;
+
case R_AVR_CALL: {
uint16_t hi = val >> 17;
uint16_t lo = val >> 1;
break;
}
default:
- error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
+ error(getErrorLocation(loc) + "unrecognized relocation " +
+ toString(rel.type));
}
}
-TargetInfo *getAVRTargetInfo() {
+TargetInfo *elf::getAVRTargetInfo() {
static AVR target;
return ⌖
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class Hexagon final : public TargetInfo {
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
RelType getDynRel(RelType type) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
defaultMaxPageSize = 0x10000;
noneRel = R_HEX_NONE;
tlsGotRel = R_HEX_TPREL_32;
+ tlsModuleIndexRel = R_HEX_DTPMOD_32;
+ tlsOffsetRel = R_HEX_DTPREL_32;
}
uint32_t Hexagon::calcEFlags() const {
case R_HEX_32_6_X:
case R_HEX_HI16:
case R_HEX_LO16:
+ case R_HEX_DTPREL_32:
return R_ABS;
case R_HEX_B9_PCREL:
case R_HEX_B13_PCREL:
case R_HEX_PLT_B22_PCREL:
case R_HEX_B22_PCREL_X:
case R_HEX_B32_PCREL_X:
+ case R_HEX_GD_PLT_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL_X:
+ case R_HEX_GD_PLT_B32_PCREL_X:
return R_PLT_PC;
case R_HEX_IE_32_6_X:
case R_HEX_IE_16_X:
case R_HEX_IE_HI16:
case R_HEX_IE_LO16:
return R_GOT;
+ case R_HEX_GD_GOT_11_X:
+ case R_HEX_GD_GOT_16_X:
+ case R_HEX_GD_GOT_32_6_X:
+ return R_TLSGD_GOTPLT;
case R_HEX_GOTREL_11_X:
case R_HEX_GOTREL_16_X:
case R_HEX_GOTREL_32_6_X:
}
}
+static bool isDuplex(uint32_t insn) {
+ // Duplex forms have a fixed mask and parse bits 15:14 are always
+ // zero. Non-duplex insns will always have at least one bit set in the
+ // parse field.
+ return (0xC000 & insn) == 0;
+}
+
static uint32_t findMaskR6(uint32_t insn) {
// There are (arguably too) many relocation masks for the DSP's
// R_HEX_6_X type. The table below is used to select the correct mask
{0xd7000000, 0x006020e0}, {0xd8000000, 0x006020e0},
{0xdb000000, 0x006020e0}, {0xdf000000, 0x006020e0}};
- // Duplex forms have a fixed mask and parse bits 15:14 are always
- // zero. Non-duplex insns will always have at least one bit set in the
- // parse field.
- if ((0xC000 & insn) == 0x0)
+ if (isDuplex(insn))
return 0x03f00000;
for (InstructionMask i : r6)
if ((0xff000000 & insn) == 0xb0000000)
return 0x0fe03fe0;
+ if (isDuplex(insn))
+ return 0x03f00000;
+
error("unrecognized instruction for R_HEX_16_X relocation: 0x" +
utohexstr(insn));
return 0;
static void or32le(uint8_t *p, int32_t v) { write32le(p, read32le(p) | v); }
-void Hexagon::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void Hexagon::relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_HEX_NONE:
break;
case R_HEX_6_PCREL_X:
or32le(loc, applyMask(0x00203fe0, val & 0x3f));
break;
case R_HEX_11_X:
+ case R_HEX_GD_GOT_11_X:
case R_HEX_IE_GOT_11_X:
case R_HEX_GOT_11_X:
case R_HEX_GOTREL_11_X:
case R_HEX_16_X: // These relocs only have 6 effective bits.
case R_HEX_IE_16_X:
case R_HEX_IE_GOT_16_X:
+ case R_HEX_GD_GOT_16_X:
case R_HEX_GOT_16_X:
case R_HEX_GOTREL_16_X:
case R_HEX_TPREL_16_X:
break;
case R_HEX_32:
case R_HEX_32_PCREL:
+ case R_HEX_DTPREL_32:
or32le(loc, val);
break;
case R_HEX_32_6_X:
+ case R_HEX_GD_GOT_32_6_X:
case R_HEX_GOT_32_6_X:
case R_HEX_GOTREL_32_6_X:
case R_HEX_IE_GOT_32_6_X:
or32le(loc, applyMask(0x0fff3fff, val >> 6));
break;
case R_HEX_B9_PCREL:
- checkInt(loc, val, 11, type);
+ checkInt(loc, val, 11, rel);
or32le(loc, applyMask(0x003000fe, val >> 2));
break;
case R_HEX_B9_PCREL_X:
or32le(loc, applyMask(0x003000fe, val & 0x3f));
break;
case R_HEX_B13_PCREL:
- checkInt(loc, val, 15, type);
+ checkInt(loc, val, 15, rel);
or32le(loc, applyMask(0x00202ffe, val >> 2));
break;
case R_HEX_B15_PCREL:
- checkInt(loc, val, 17, type);
+ checkInt(loc, val, 17, rel);
or32le(loc, applyMask(0x00df20fe, val >> 2));
break;
case R_HEX_B15_PCREL_X:
or32le(loc, applyMask(0x00df20fe, val & 0x3f));
break;
case R_HEX_B22_PCREL:
+ case R_HEX_GD_PLT_B22_PCREL:
case R_HEX_PLT_B22_PCREL:
- checkInt(loc, val, 22, type);
+ checkInt(loc, val, 22, rel);
or32le(loc, applyMask(0x1ff3ffe, val >> 2));
break;
case R_HEX_B22_PCREL_X:
+ case R_HEX_GD_PLT_B22_PCREL_X:
or32le(loc, applyMask(0x1ff3ffe, val & 0x3f));
break;
case R_HEX_B32_PCREL_X:
+ case R_HEX_GD_PLT_B32_PCREL_X:
or32le(loc, applyMask(0x0fff3fff, val >> 6));
break;
case R_HEX_GOTREL_HI16:
// Offset from PLT0 to the GOT.
uint64_t off = in.gotPlt->getVA() - in.plt->getVA();
- relocateOne(buf, R_HEX_B32_PCREL_X, off);
- relocateOne(buf + 4, R_HEX_6_PCREL_X, off);
+ relocateNoSym(buf, R_HEX_B32_PCREL_X, off);
+ relocateNoSym(buf + 4, R_HEX_6_PCREL_X, off);
}
void Hexagon::writePlt(uint8_t *buf, const Symbol &sym,
memcpy(buf, inst, sizeof(inst));
uint64_t gotPltEntryAddr = sym.getGotPltVA();
- relocateOne(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr);
- relocateOne(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
+ relocateNoSym(buf, R_HEX_B32_PCREL_X, gotPltEntryAddr - pltEntryAddr);
+ relocateNoSym(buf + 4, R_HEX_6_PCREL_X, gotPltEntryAddr - pltEntryAddr);
}
RelType Hexagon::getDynRel(RelType type) const {
return R_HEX_NONE;
}
-TargetInfo *getHexagonTargetInfo() {
+TargetInfo *elf::getHexagonTargetInfo() {
static Hexagon target;
return ⌖
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class MSP430 final : public TargetInfo {
MSP430();
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
}
}
-void MSP430::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void MSP430::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_MSP430_8:
- checkIntUInt(loc, val, 8, type);
+ checkIntUInt(loc, val, 8, rel);
*loc = val;
break;
case R_MSP430_16:
case R_MSP430_16_PCREL:
case R_MSP430_16_BYTE:
case R_MSP430_16_PCREL_BYTE:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_MSP430_32:
- checkIntUInt(loc, val, 32, type);
+ checkIntUInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_MSP430_10_PCREL: {
int16_t offset = ((int16_t)val >> 1) - 1;
- checkInt(loc, offset, 10, type);
+ checkInt(loc, offset, 10, rel);
write16le(loc, (read16le(loc) & 0xFC00) | (offset & 0x3FF));
break;
}
default:
- error(getErrorLocation(loc) + "unrecognized relocation " + toString(type));
+ error(getErrorLocation(loc) + "unrecognized relocation " +
+ toString(rel.type));
}
}
-TargetInfo *getMSP430TargetInfo() {
+TargetInfo *elf::getMSP430TargetInfo() {
static MSP430 target;
return ⌖
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
template <class ELFT> class MIPS final : public TargetInfo {
public:
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
uint64_t branchAddr, const Symbol &s,
int64_t a) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
bool usesOnlyLowPageBits(RelType type) const override;
};
} // namespace
write16(buf + 18, 0x0f83); // move $28, $3
write16(buf + 20, 0x472b); // jalrc $25
write16(buf + 22, 0x0c00); // nop
- relocateOne(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
+ relocateNoSym(buf, R_MICROMIPS_PC19_S2, gotPlt - plt);
} else {
write16(buf + 18, 0x45f9); // jalrc $25
write16(buf + 20, 0x0f83); // move $28, $3
write16(buf + 22, 0x0c00); // nop
- relocateOne(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
+ relocateNoSym(buf, R_MICROMIPS_PC23_S2, gotPlt - plt);
}
return;
}
write16(buf + 4, 0xff22); // lw $25, 0($2)
write16(buf + 8, 0x0f02); // move $24, $2
write16(buf + 10, 0x4723); // jrc $25 / jr16 $25
- relocateOne(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
+ relocateNoSym(buf, R_MICROMIPS_PC19_S2, gotPltEntryAddr - pltEntryAddr);
} else {
write16(buf, 0x7900); // addiupc $2, (GOTPLT) - .
write16(buf + 4, 0xff22); // lw $25, 0($2)
write16(buf + 8, 0x4599); // jrc $25 / jr16 $25
write16(buf + 10, 0x0f02); // move $24, $2
- relocateOne(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
+ relocateNoSym(buf, R_MICROMIPS_PC23_S2, gotPltEntryAddr - pltEntryAddr);
}
return;
}
}
template <class ELFT>
-void MIPS<ELFT>::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+void MIPS<ELFT>::relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
const endianness e = ELFT::TargetEndianness;
+ RelType type = rel.type;
if (ELFT::Is64Bits || config->mipsN32Abi)
std::tie(type, val) = calculateMipsRelChain(loc, type, val);
if (config->relocatable) {
writeValue(loc, val + 0x8000, 16, 16);
} else {
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
writeValue(loc, val, 16, 0);
}
break;
if (config->relocatable) {
writeShuffleValue<e>(loc, val + 0x8000, 16, 16);
} else {
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
writeShuffleValue<e>(loc, val, 16, 0);
}
break;
case R_MIPS_TLS_GD:
case R_MIPS_TLS_GOTTPREL:
case R_MIPS_TLS_LDM:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
LLVM_FALLTHROUGH;
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
case R_MICROMIPS_GPREL16:
case R_MICROMIPS_TLS_GD:
case R_MICROMIPS_TLS_LDM:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
writeShuffleValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_CALL16:
writeShuffleValue<e>(loc, val, 16, 0);
break;
case R_MICROMIPS_GPREL7_S2:
- checkInt(loc, val, 7, type);
+ checkInt(loc, val, 7, rel);
writeShuffleValue<e>(loc, val, 7, 2);
break;
case R_MIPS_CALL_HI16:
// Ignore this optimization relocation for now
break;
case R_MIPS_PC16:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 18, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 18, rel);
writeValue(loc, val, 16, 2);
break;
case R_MIPS_PC19_S2:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 21, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 21, rel);
writeValue(loc, val, 19, 2);
break;
case R_MIPS_PC21_S2:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 23, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 23, rel);
writeValue(loc, val, 21, 2);
break;
case R_MIPS_PC26_S2:
- checkAlignment(loc, val, 4, type);
- checkInt(loc, val, 28, type);
+ checkAlignment(loc, val, 4, rel);
+ checkInt(loc, val, 28, rel);
writeValue(loc, val, 26, 2);
break;
case R_MIPS_PC32:
break;
case R_MICROMIPS_26_S1:
case R_MICROMIPS_PC26_S1:
- checkInt(loc, val, 27, type);
+ checkInt(loc, val, 27, rel);
writeShuffleValue<e>(loc, val, 26, 1);
break;
case R_MICROMIPS_PC7_S1:
- checkInt(loc, val, 8, type);
+ checkInt(loc, val, 8, rel);
writeMicroRelocation16<e>(loc, val, 7, 1);
break;
case R_MICROMIPS_PC10_S1:
- checkInt(loc, val, 11, type);
+ checkInt(loc, val, 11, rel);
writeMicroRelocation16<e>(loc, val, 10, 1);
break;
case R_MICROMIPS_PC16_S1:
- checkInt(loc, val, 17, type);
+ checkInt(loc, val, 17, rel);
writeShuffleValue<e>(loc, val, 16, 1);
break;
case R_MICROMIPS_PC18_S3:
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
writeShuffleValue<e>(loc, val, 18, 3);
break;
case R_MICROMIPS_PC19_S2:
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
writeShuffleValue<e>(loc, val, 19, 2);
break;
case R_MICROMIPS_PC21_S1:
- checkInt(loc, val, 22, type);
+ checkInt(loc, val, 22, rel);
writeShuffleValue<e>(loc, val, 21, 1);
break;
case R_MICROMIPS_PC23_S2:
- checkInt(loc, val, 25, type);
+ checkInt(loc, val, 25, rel);
writeShuffleValue<e>(loc, val, 23, 2);
break;
default:
}
// Return true if the symbol is a PIC function.
-template <class ELFT> bool isMipsPIC(const Defined *sym) {
+template <class ELFT> bool elf::isMipsPIC(const Defined *sym) {
if (!sym->isFunc())
return false;
return file->getObj().getHeader()->e_flags & EF_MIPS_PIC;
}
-template <class ELFT> TargetInfo *getMipsTargetInfo() {
+template <class ELFT> TargetInfo *elf::getMipsTargetInfo() {
static MIPS<ELFT> target;
return ⌖
}
-template TargetInfo *getMipsTargetInfo<ELF32LE>();
-template TargetInfo *getMipsTargetInfo<ELF32BE>();
-template TargetInfo *getMipsTargetInfo<ELF64LE>();
-template TargetInfo *getMipsTargetInfo<ELF64BE>();
+template TargetInfo *elf::getMipsTargetInfo<ELF32LE>();
+template TargetInfo *elf::getMipsTargetInfo<ELF32BE>();
+template TargetInfo *elf::getMipsTargetInfo<ELF64LE>();
+template TargetInfo *elf::getMipsTargetInfo<ELF64BE>();
-template bool isMipsPIC<ELF32LE>(const Defined *);
-template bool isMipsPIC<ELF32BE>(const Defined *);
-template bool isMipsPIC<ELF64LE>(const Defined *);
-template bool isMipsPIC<ELF64BE>(const Defined *);
-
-} // namespace elf
-} // namespace lld
+template bool elf::isMipsPIC<ELF32LE>(const Defined *);
+template bool elf::isMipsPIC<ELF32BE>(const Defined *);
+template bool elf::isMipsPIC<ELF64LE>(const Defined *);
+template bool elf::isMipsPIC<ELF64BE>(const Defined *);
using namespace llvm::object;
using namespace llvm::ELF;
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
struct ArchTreeEdge {
return ret;
}
-template <class ELFT> uint32_t calcMipsEFlags() {
+template <class ELFT> uint32_t elf::calcMipsEFlags() {
std::vector<FileFlags> v;
for (InputFile *f : objectFiles)
v.push_back({f, cast<ObjFile<ELFT>>(f)->getObj().getHeader()->e_flags});
}
}
-uint8_t getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag, StringRef fileName) {
+uint8_t elf::getMipsFpAbiFlag(uint8_t oldFlag, uint8_t newFlag,
+ StringRef fileName) {
if (compareMipsFpAbi(newFlag, oldFlag) >= 0)
return newFlag;
if (compareMipsFpAbi(oldFlag, newFlag) < 0)
return false;
}
-bool isMipsN32Abi(const InputFile *f) {
+bool elf::isMipsN32Abi(const InputFile *f) {
switch (config->ekind) {
case ELF32LEKind:
return isN32Abi<ELF32LE>(f);
}
}
-bool isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
+bool elf::isMicroMips() { return config->eflags & EF_MIPS_MICROMIPS; }
-bool isMipsR6() {
+bool elf::isMipsR6() {
uint32_t arch = config->eflags & EF_MIPS_ARCH;
return arch == EF_MIPS_ARCH_32R6 || arch == EF_MIPS_ARCH_64R6;
}
-template uint32_t calcMipsEFlags<ELF32LE>();
-template uint32_t calcMipsEFlags<ELF32BE>();
-template uint32_t calcMipsEFlags<ELF64LE>();
-template uint32_t calcMipsEFlags<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
+template uint32_t elf::calcMipsEFlags<ELF32LE>();
+template uint32_t elf::calcMipsEFlags<ELF32BE>();
+template uint32_t elf::calcMipsEFlags<ELF64LE>();
+template uint32_t elf::calcMipsEFlags<ELF64BE>();
//
//===----------------------------------------------------------------------===//
+#include "SymbolTable.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
#include "Thunks.h"
#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
#include "llvm/Support/Endian.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
static uint64_t ppc64TocOffset = 0x8000;
static uint64_t dynamicThreadPointerOffset = 0x8000;
ADDI = 14
};
-uint64_t getPPC64TocBase() {
+uint64_t elf::getPPC64TocBase() {
// The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The
// TOC starts where the first of these sections starts. We always create a
// .got when we see a relocation that uses it, so for us the start is always
return tocVA + ppc64TocOffset;
}
-unsigned getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
+unsigned elf::getPPC64GlobalEntryToLocalEntryOffset(uint8_t stOther) {
// The offset is encoded into the 3 most significant bits of the st_other
// field, with some special values described in section 3.4.1 of the ABI:
// 0 --> Zero offset between the GEP and LEP, and the function does NOT use
return 0;
}
-bool isPPC64SmallCodeModelTocReloc(RelType type) {
+bool elf::isPPC64SmallCodeModelTocReloc(RelType type) {
// The only small code model relocations that access the .toc section.
return type == R_PPC64_TOC16 || type == R_PPC64_TOC16_DS;
}
+static bool addOptional(StringRef name, uint64_t value,
+ std::vector<Defined *> &defined) {
+ Symbol *sym = symtab->find(name);
+ if (!sym || sym->isDefined())
+ return false;
+ sym->resolve(Defined{/*file=*/nullptr, saver.save(name), STB_GLOBAL,
+ STV_HIDDEN, STT_FUNC, value,
+ /*size=*/0, /*section=*/nullptr});
+ defined.push_back(cast<Defined>(sym));
+ return true;
+}
+
+// If from is 14, write ${prefix}14: firstInsn; ${prefix}15:
+// firstInsn+0x200008; ...; ${prefix}31: firstInsn+(31-14)*0x200008; $tail
+// The labels are defined only if they exist in the symbol table.
+static void writeSequence(MutableArrayRef<uint32_t> buf, const char *prefix,
+ int from, uint32_t firstInsn,
+ ArrayRef<uint32_t> tail) {
+ std::vector<Defined *> defined;
+ char name[16];
+ int first;
+ uint32_t *ptr = buf.data();
+ for (int r = from; r < 32; ++r) {
+ format("%s%d", prefix, r).snprint(name, sizeof(name));
+ if (addOptional(name, 4 * (r - from), defined) && defined.size() == 1)
+ first = r - from;
+ write32(ptr++, firstInsn + 0x200008 * (r - from));
+ }
+ for (uint32_t insn : tail)
+ write32(ptr++, insn);
+ assert(ptr == &*buf.end());
+
+ if (defined.empty())
+ return;
+ // The full section content has the extent of [begin, end). We drop unused
+ // instructions and write [first,end).
+ auto *sec = make<InputSection>(
+ nullptr, SHF_ALLOC, SHT_PROGBITS, 4,
+ makeArrayRef(reinterpret_cast<uint8_t *>(buf.data() + first),
+ 4 * (buf.size() - first)),
+ ".text");
+ inputSections.push_back(sec);
+ for (Defined *sym : defined) {
+ sym->section = sec;
+ sym->value -= 4 * first;
+ }
+}
+
+// Implements some save and restore functions as described by ELF V2 ABI to be
+// compatible with GCC. With GCC -Os, when the number of call-saved registers
+// exceeds a certain threshold, GCC generates _savegpr0_* _restgpr0_* calls and
+// expects the linker to define them. See
+// https://sourceware.org/pipermail/binutils/2002-February/017444.html and
+// https://sourceware.org/pipermail/binutils/2004-August/036765.html . This is
+// weird because libgcc.a would be the natural place. The linker generation
+// approach has the advantage that the linker can generate multiple copies to
+// avoid long branch thunks. However, we don't consider the advantage
+// significant enough to complicate our trunk implementation, so we take the
+// simple approach and synthesize .text sections providing the implementation.
+void elf::addPPC64SaveRestore() {
+ static uint32_t savegpr0[20], restgpr0[21], savegpr1[19], restgpr1[19];
+ constexpr uint32_t blr = 0x4e800020, mtlr_0 = 0x7c0803a6;
+
+ // _restgpr0_14: ld 14, -144(1); _restgpr0_15: ld 15, -136(1); ...
+ // Tail: ld 0, 16(1); mtlr 0; blr
+ writeSequence(restgpr0, "_restgpr0_", 14, 0xe9c1ff70,
+ {0xe8010010, mtlr_0, blr});
+ // _restgpr1_14: ld 14, -144(12); _restgpr1_15: ld 15, -136(12); ...
+ // Tail: blr
+ writeSequence(restgpr1, "_restgpr1_", 14, 0xe9ccff70, {blr});
+ // _savegpr0_14: std 14, -144(1); _savegpr0_15: std 15, -136(1); ...
+ // Tail: std 0, 16(1); blr
+ writeSequence(savegpr0, "_savegpr0_", 14, 0xf9c1ff70, {0xf8010010, blr});
+ // _savegpr1_14: std 14, -144(12); _savegpr1_15: std 15, -136(12); ...
+ // Tail: blr
+ writeSequence(savegpr1, "_savegpr1_", 14, 0xf9ccff70, {blr});
+}
+
// Find the R_PPC64_ADDR64 in .rela.toc with matching offset.
template <typename ELFT>
static std::pair<Defined *, int64_t>
// When accessing a symbol defined in another translation unit, compilers
// reserve a .toc entry, allocate a local label and generate toc-indirect
-// instuctions:
+// instructions:
//
// addis 3, 2, .LC0@toc@ha # R_PPC64_TOC16_HA
// ld 3, .LC0@toc@l(3) # R_PPC64_TOC16_LO_DS, load the address from a .toc entry
// ld/lwa 3, 0(3) # load the value from the address
//
// Returns true if the relaxation is performed.
-bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
- uint8_t *bufLoc) {
+bool elf::tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc) {
assert(config->tocOptimize);
if (rel.addend < 0)
return false;
if (!isInt<32>(tocRelative))
return false;
- // Add PPC64TocOffset that will be subtracted by relocateOne().
- target->relaxGot(bufLoc, type, tocRelative + ppc64TocOffset);
+ // Add PPC64TocOffset that will be subtracted by PPC64::relocate().
+ target->relaxGot(bufLoc, rel, tocRelative + ppc64TocOffset);
return true;
}
uint64_t pltEntryAddr) const override;
void writeIplt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
void writeGotHeader(uint8_t *buf) const override;
bool needsThunk(RelExpr expr, RelType type, const InputFile *file,
uint64_t branchAddr, const Symbol &s,
bool inBranchRange(RelType type, uint64_t src, uint64_t dst) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
- void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxGot(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
uint8_t stOther) const override;
return read32(config->isLE ? loc : loc - 2);
}
+// The prefixed instruction is always a 4 byte prefix followed by a 4 byte
+// instruction. Therefore, the prefix is always in lower memory than the
+// instruction (regardless of endianness).
+// As a result, we need to shift the pieces around on little endian machines.
+static void writePrefixedInstruction(uint8_t *loc, uint64_t insn) {
+ insn = config->isLE ? insn << 32 | insn >> 32 : insn;
+ write64(loc, insn);
+}
+
+static uint64_t readPrefixedInstruction(const uint8_t *loc) {
+ uint64_t fullInstr = read64(loc);
+ return config->isLE ? (fullInstr << 32 | fullInstr >> 32) : fullInstr;
+}
+
PPC64::PPC64() {
copyRel = R_PPC64_COPY;
gotRel = R_PPC64_GLOB_DAT;
return 2;
}
-void PPC64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void PPC64::relaxGot(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_PPC64_TOC16_HA:
// Convert "addis reg, 2, .LC0@toc@h" to "addis reg, 2, var@toc@h" or "nop".
- relocateOne(loc, type, val);
+ relocate(loc, rel, val);
break;
case R_PPC64_TOC16_LO_DS: {
// Convert "ld reg, .LC0@toc@l(reg)" to "addi reg, reg, var@toc@l" or
if (getPrimaryOpCode(insn) != LD)
error("expected a 'ld' for got-indirect to toc-relative relaxing");
writeFromHalf16(loc, (insn & 0x03ffffff) | 0x38000000);
- relocateOne(loc, R_PPC64_TOC16_LO, val);
+ relocateNoSym(loc, R_PPC64_TOC16_LO, val);
break;
}
default:
}
}
-void PPC64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void PPC64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// Reference: 3.7.4.2 of the 64-bit ELF V2 abi supplement.
// The general dynamic code sequence for a global `x` will look like:
// Instruction Relocation Symbol
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, x@tprel@l
- switch (type) {
+ switch (rel.type) {
case R_PPC64_GOT_TLSGD16_HA:
writeFromHalf16(loc, 0x60000000); // nop
break;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO:
writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13
- relocateOne(loc, R_PPC64_TPREL16_HA, val);
+ relocateNoSym(loc, R_PPC64_TPREL16_HA, val);
break;
case R_PPC64_TLSGD:
write32(loc, 0x60000000); // nop
// Since we are relocating a half16 type relocation and Loc + 4 points to
// the start of an instruction we need to advance the buffer by an extra
// 2 bytes on BE.
- relocateOne(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0),
- R_PPC64_TPREL16_LO, val);
+ relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0),
+ R_PPC64_TPREL16_LO, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS GD to LE relaxation");
}
}
-void PPC64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void PPC64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// Reference: 3.7.4.3 of the 64-bit ELF V2 abi supplement.
// The local dynamic code sequence for a global `x` will look like:
// Instruction Relocation Symbol
// bl __tls_get_addr(x@tlsgd) into nop
// nop into addi r3, r3, 4096
- switch (type) {
+ switch (rel.type) {
case R_PPC64_GOT_TLSLD16_HA:
writeFromHalf16(loc, 0x60000000); // nop
break;
case R_PPC64_DTPREL16_DS:
case R_PPC64_DTPREL16_LO:
case R_PPC64_DTPREL16_LO_DS:
- relocateOne(loc, type, val);
+ relocate(loc, rel, val);
break;
default:
llvm_unreachable("unsupported relocation for TLS LD to LE relaxation");
}
}
-unsigned getPPCDFormOp(unsigned secondaryOp) {
+unsigned elf::getPPCDFormOp(unsigned secondaryOp) {
switch (secondaryOp) {
case LBZX:
return LBZ;
}
}
-void PPC64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void PPC64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// The initial exec code sequence for a global `x` will look like:
// Instruction Relocation Symbol
// addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x
// indexed load or store instructions.
unsigned offset = (config->ekind == ELF64BEKind) ? 2 : 0;
- switch (type) {
+ switch (rel.type) {
case R_PPC64_GOT_TPREL16_HA:
write32(loc - offset, 0x60000000); // nop
break;
case R_PPC64_GOT_TPREL16_DS: {
uint32_t regNo = read32(loc - offset) & 0x03E00000; // bits 6-10
write32(loc - offset, 0x3C0D0000 | regNo); // addis RegNo, r13
- relocateOne(loc, R_PPC64_TPREL16_HA, val);
+ relocateNoSym(loc, R_PPC64_TPREL16_HA, val);
break;
}
case R_PPC64_TLS: {
if (dFormOp == 0)
error("unrecognized instruction for IE to LE R_PPC64_TLS");
write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF)));
- relocateOne(loc + offset, R_PPC64_TPREL16_LO, val);
+ relocateNoSym(loc + offset, R_PPC64_TPREL16_LO, val);
break;
}
default:
case R_PPC64_TOC16_HI:
case R_PPC64_TOC16_LO:
return R_GOTREL;
+ case R_PPC64_GOT_PCREL34:
+ return R_GOT_PC;
case R_PPC64_TOC16_HA:
case R_PPC64_TOC16_LO_DS:
return config->tocOptimize ? R_PPC64_RELAX_TOC : R_GOTREL;
case R_PPC64_REL14:
case R_PPC64_REL24:
return R_PPC64_CALL_PLT;
+ case R_PPC64_REL24_NOTOC:
+ return R_PLT_PC;
case R_PPC64_REL16_LO:
case R_PPC64_REL16_HA:
case R_PPC64_REL16_HI:
case R_PPC64_REL32:
case R_PPC64_REL64:
+ case R_PPC64_PCREL34:
return R_PC;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_HA:
}
}
-void PPC64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- // We need to save the original relocation type to use in diagnostics, and
- // use the original type to determine if we should toc-optimize the
- // instructions being relocated.
- RelType originalType = type;
+void PPC64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ RelType type = rel.type;
bool shouldTocOptimize = isTocOptType(type);
// For dynamic thread pointer relative, toc-relative, and got-indirect
// relocations, proceed in terms of the corresponding ADDR16 relocation type.
switch (type) {
case R_PPC64_ADDR14: {
- checkAlignment(loc, val, 4, type);
+ checkAlignment(loc, val, 4, rel);
// Preserve the AA/LK bits in the branch instruction
uint8_t aalk = loc[3];
write16(loc + 2, (aalk & 3) | (val & 0xfffc));
break;
}
case R_PPC64_ADDR16:
- checkIntUInt(loc, val, 16, originalType);
+ checkIntUInt(loc, val, 16, rel);
write16(loc, val);
break;
case R_PPC64_ADDR32:
- checkIntUInt(loc, val, 32, originalType);
+ checkIntUInt(loc, val, 32, rel);
write32(loc, val);
break;
case R_PPC64_ADDR16_DS:
case R_PPC64_TPREL16_DS: {
- checkInt(loc, val, 16, originalType);
+ checkInt(loc, val, 16, rel);
// DQ-form instructions use bits 28-31 as part of the instruction encoding
// DS-form instructions only use bits 30-31.
uint16_t mask = isDQFormInstruction(readFromHalf16(loc)) ? 0xf : 0x3;
- checkAlignment(loc, lo(val), mask + 1, originalType);
+ checkAlignment(loc, lo(val), mask + 1, rel);
write16(loc, (read16(loc) & mask) | lo(val));
} break;
case R_PPC64_ADDR16_HA:
// DS-form instructions only use bits 30-31.
uint32_t insn = readFromHalf16(loc);
uint16_t mask = isDQFormInstruction(insn) ? 0xf : 0x3;
- checkAlignment(loc, lo(val), mask + 1, originalType);
+ checkAlignment(loc, lo(val), mask + 1, rel);
if (config->tocOptimize && shouldTocOptimize && ha(val) == 0) {
// When the high-adjusted part of a toc relocation evaluates to 0, it is
// changed into a nop. The lo part then needs to be updated to use the toc
}
} break;
case R_PPC64_TPREL16:
- checkInt(loc, val, 16, originalType);
+ checkInt(loc, val, 16, rel);
write16(loc, val);
break;
case R_PPC64_REL32:
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32(loc, val);
break;
case R_PPC64_ADDR64:
break;
case R_PPC64_REL14: {
uint32_t mask = 0x0000FFFC;
- checkInt(loc, val, 16, type);
- checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 16, rel);
+ checkAlignment(loc, val, 4, rel);
write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
- case R_PPC64_REL24: {
+ case R_PPC64_REL24:
+ case R_PPC64_REL24_NOTOC: {
uint32_t mask = 0x03FFFFFC;
- checkInt(loc, val, 26, type);
- checkAlignment(loc, val, 4, type);
+ checkInt(loc, val, 26, rel);
+ checkAlignment(loc, val, 4, rel);
write32(loc, (read32(loc) & ~mask) | (val & mask));
break;
}
case R_PPC64_DTPREL64:
write64(loc, val - dynamicThreadPointerOffset);
break;
+ case R_PPC64_PCREL34: {
+ const uint64_t si0Mask = 0x00000003ffff0000;
+ const uint64_t si1Mask = 0x000000000000ffff;
+ const uint64_t fullMask = 0x0003ffff0000ffff;
+ checkInt(loc, val, 34, rel);
+
+ uint64_t instr = readPrefixedInstruction(loc) & ~fullMask;
+ writePrefixedInstruction(loc, instr | ((val & si0Mask) << 16) |
+ (val & si1Mask));
+ break;
+ }
+ case R_PPC64_GOT_PCREL34: {
+ const uint64_t si0Mask = 0x00000003ffff0000;
+ const uint64_t si1Mask = 0x000000000000ffff;
+ const uint64_t fullMask = 0x0003ffff0000ffff;
+ checkInt(loc, val, 34, rel);
+
+ uint64_t instr = readPrefixedInstruction(loc) & ~fullMask;
+ writePrefixedInstruction(loc, instr | ((val & si0Mask) << 16) |
+ (val & si1Mask));
+ break;
+ }
default:
llvm_unreachable("unknown relocation");
}
bool PPC64::needsThunk(RelExpr expr, RelType type, const InputFile *file,
uint64_t branchAddr, const Symbol &s, int64_t a) const {
- if (type != R_PPC64_REL14 && type != R_PPC64_REL24)
+ if (type != R_PPC64_REL14 && type != R_PPC64_REL24 &&
+ type != R_PPC64_REL24_NOTOC)
return false;
+ // FIXME: Remove the fatal error once the call protocol is implemented.
+ if (type == R_PPC64_REL24_NOTOC && s.isInPlt())
+ fatal("unimplemented feature: external function call with the reltype"
+ " R_PPC64_REL24_NOTOC");
+
// If a function is in the Plt it needs to be called with a call-stub.
if (s.isInPlt())
return true;
+ // FIXME: Remove the fatal error once the call protocol is implemented.
+ if (type == R_PPC64_REL24_NOTOC && (s.stOther >> 5) > 1)
+ fatal("unimplemented feature: local function call with the reltype"
+ " R_PPC64_REL24_NOTOC and the callee needs toc-pointer setup");
+
+ // This check looks at the st_other bits of the callee with relocation
+ // R_PPC64_REL14 or R_PPC64_REL24. If the value is 1, then the callee
+ // clobbers the TOC and we need an R2 save stub.
+ if (type != R_PPC64_REL24_NOTOC && (s.stOther >> 5) == 1)
+ return true;
+
// If a symbol is a weak undefined and we are compiling an executable
// it doesn't need a range-extending thunk since it can't be called.
if (s.isUndefWeak() && !config->shared)
int64_t offset = dst - src;
if (type == R_PPC64_REL14)
return isInt<16>(offset);
- if (type == R_PPC64_REL24)
+ if (type == R_PPC64_REL24 || type == R_PPC64_REL24_NOTOC)
return isInt<26>(offset);
llvm_unreachable("unsupported relocation type used in branch");
}
// thread pointer.
// Since the nop must directly follow the call, the R_PPC64_TLSGD relocation is
// used as the relaxation hint for both steps 2 and 3.
-void PPC64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void PPC64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_PPC64_GOT_TLSGD16_HA:
// This is relaxed from addis rT, r2, sym@got@tlsgd@ha to
// addis rT, r2, sym@got@tprel@ha.
- relocateOne(loc, R_PPC64_GOT_TPREL16_HA, val);
+ relocateNoSym(loc, R_PPC64_GOT_TPREL16_HA, val);
return;
case R_PPC64_GOT_TLSGD16:
case R_PPC64_GOT_TLSGD16_LO: {
// ld r3, sym@got@tprel@l(rA)
uint32_t ra = (readFromHalf16(loc) & (0x1f << 16));
writeFromHalf16(loc, 0xe8600000 | ra);
- relocateOne(loc, R_PPC64_GOT_TPREL16_LO_DS, val);
+ relocateNoSym(loc, R_PPC64_GOT_TPREL16_LO_DS, val);
return;
}
case R_PPC64_TLSGD:
return true;
}
-TargetInfo *getPPC64TargetInfo() {
+TargetInfo *elf::getPPC64TargetInfo() {
static PPC64 target;
return ⌖
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
RelType getDynRel(RelType type) const override;
RelExpr getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // end anonymous namespace
noneRel = R_RISCV_NONE;
pltRel = R_RISCV_JUMP_SLOT;
relativeRel = R_RISCV_RELATIVE;
+ iRelativeRel = R_RISCV_IRELATIVE;
if (config->is64) {
symbolicRel = R_RISCV_64;
tlsModuleIndexRel = R_RISCV_TLS_DTPMOD64;
return (v & ((1ULL << (begin + 1)) - 1)) >> end;
}
-void RISCV::relocateOne(uint8_t *loc, const RelType type,
- const uint64_t val) const {
+void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
const unsigned bits = config->wordsize * 8;
- switch (type) {
+ switch (rel.type) {
case R_RISCV_32:
write32le(loc, val);
return;
return;
case R_RISCV_RVC_BRANCH: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 8, type);
- checkAlignment(loc, val, 2, type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 8, rel);
+ checkAlignment(loc, val, 2, rel);
uint16_t insn = read16le(loc) & 0xE383;
uint16_t imm8 = extractBits(val, 8, 8) << 12;
uint16_t imm4_3 = extractBits(val, 4, 3) << 10;
}
case R_RISCV_RVC_JUMP: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 11, type);
- checkAlignment(loc, val, 2, type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 11, rel);
+ checkAlignment(loc, val, 2, rel);
uint16_t insn = read16le(loc) & 0xE003;
uint16_t imm11 = extractBits(val, 11, 11) << 12;
uint16_t imm4 = extractBits(val, 4, 4) << 11;
case R_RISCV_RVC_LUI: {
int64_t imm = SignExtend64(val + 0x800, bits) >> 12;
- checkInt(loc, imm, 6, type);
+ checkInt(loc, imm, 6, rel);
if (imm == 0) { // `c.lui rd, 0` is illegal, convert to `c.li rd, 0`
write16le(loc, (read16le(loc) & 0x0F83) | 0x4000);
} else {
}
case R_RISCV_JAL: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 20, type);
- checkAlignment(loc, val, 2, type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 20, rel);
+ checkAlignment(loc, val, 2, rel);
uint32_t insn = read32le(loc) & 0xFFF;
uint32_t imm20 = extractBits(val, 20, 20) << 31;
}
case R_RISCV_BRANCH: {
- checkInt(loc, static_cast<int64_t>(val) >> 1, 12, type);
- checkAlignment(loc, val, 2, type);
+ checkInt(loc, static_cast<int64_t>(val) >> 1, 12, rel);
+ checkAlignment(loc, val, 2, rel);
uint32_t insn = read32le(loc) & 0x1FFF07F;
uint32_t imm12 = extractBits(val, 12, 12) << 31;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
int64_t hi = SignExtend64(val + 0x800, bits) >> 12;
- checkInt(loc, hi, 20, type);
+ checkInt(loc, hi, 20, rel);
if (isInt<20>(hi)) {
- relocateOne(loc, R_RISCV_PCREL_HI20, val);
- relocateOne(loc + 4, R_RISCV_PCREL_LO12_I, val);
+ relocateNoSym(loc, R_RISCV_PCREL_HI20, val);
+ relocateNoSym(loc + 4, R_RISCV_PCREL_LO12_I, val);
}
return;
}
case R_RISCV_TPREL_HI20:
case R_RISCV_HI20: {
uint64_t hi = val + 0x800;
- checkInt(loc, SignExtend64(hi, bits) >> 12, 20, type);
+ checkInt(loc, SignExtend64(hi, bits) >> 12, 20, rel);
write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000));
return;
}
}
}
-TargetInfo *getRISCVTargetInfo() {
+TargetInfo *elf::getRISCVTargetInfo() {
static RISCV target;
return ⌖
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class SPARCV9 final : public TargetInfo {
const uint8_t *loc) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
case R_SPARC_UA32:
case R_SPARC_64:
case R_SPARC_UA64:
+ case R_SPARC_H44:
+ case R_SPARC_M44:
+ case R_SPARC_L44:
+ case R_SPARC_HH22:
+ case R_SPARC_HM10:
+ case R_SPARC_LM22:
+ case R_SPARC_HI22:
+ case R_SPARC_LO10:
return R_ABS;
case R_SPARC_PC10:
case R_SPARC_PC22:
return R_PLT_PC;
case R_SPARC_NONE:
return R_NONE;
+ case R_SPARC_TLS_LE_HIX22:
+ case R_SPARC_TLS_LE_LOX10:
+ return R_TLS;
default:
error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
") against symbol " + toString(s));
}
}
-void SPARCV9::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void SPARCV9::relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ switch (rel.type) {
case R_SPARC_32:
case R_SPARC_UA32:
// V-word32
- checkUInt(loc, val, 32, type);
+ checkUInt(loc, val, 32, rel);
write32be(loc, val);
break;
case R_SPARC_DISP32:
// V-disp32
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32be(loc, val);
break;
case R_SPARC_WDISP30:
case R_SPARC_WPLT30:
// V-disp30
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32be(loc, (read32be(loc) & ~0x3fffffff) | ((val >> 2) & 0x3fffffff));
break;
case R_SPARC_22:
// V-imm22
- checkUInt(loc, val, 22, type);
+ checkUInt(loc, val, 22, rel);
write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff));
break;
case R_SPARC_GOT22:
case R_SPARC_PC22:
+ case R_SPARC_LM22:
// T-imm22
write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
break;
+ case R_SPARC_HI22:
+ // V-imm22
+ checkUInt(loc, val >> 10, 22, rel);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 10) & 0x003fffff));
+ break;
case R_SPARC_WDISP19:
// V-disp19
- checkInt(loc, val, 21, type);
+ checkInt(loc, val, 21, rel);
write32be(loc, (read32be(loc) & ~0x0007ffff) | ((val >> 2) & 0x0007ffff));
break;
case R_SPARC_GOT10:
// T-simm10
write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff));
break;
+ case R_SPARC_LO10:
+ // T-simm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff));
+ break;
case R_SPARC_64:
case R_SPARC_UA64:
// V-xword64
write64be(loc, val);
break;
+ case R_SPARC_HH22:
+ // V-imm22
+ checkUInt(loc, val >> 42, 22, rel);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 42) & 0x003fffff));
+ break;
+ case R_SPARC_HM10:
+ // T-simm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | ((val >> 32) & 0x000003ff));
+ break;
+ case R_SPARC_H44:
+ // V-imm22
+ checkUInt(loc, val >> 22, 22, rel);
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((val >> 22) & 0x003fffff));
+ break;
+ case R_SPARC_M44:
+ // T-imm10
+ write32be(loc, (read32be(loc) & ~0x000003ff) | ((val >> 12) & 0x000003ff));
+ break;
+ case R_SPARC_L44:
+ // T-imm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x00000fff));
+ break;
+ case R_SPARC_TLS_LE_HIX22:
+ // T-imm22
+ write32be(loc, (read32be(loc) & ~0x003fffff) | ((~val >> 10) & 0x003fffff));
+ break;
+ case R_SPARC_TLS_LE_LOX10:
+ // T-simm13
+ write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff) | 0x1C00);
+ break;
default:
llvm_unreachable("unknown relocation");
}
memcpy(buf, pltData, sizeof(pltData));
uint64_t off = pltEntryAddr - in.plt->getVA();
- relocateOne(buf, R_SPARC_22, off);
- relocateOne(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
+ relocateNoSym(buf, R_SPARC_22, off);
+ relocateNoSym(buf + 4, R_SPARC_WDISP19, -(off + 4 - pltEntrySize));
}
-TargetInfo *getSPARCV9TargetInfo() {
+TargetInfo *elf::getSPARCV9TargetInfo() {
static SPARCV9 target;
return ⌖
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class X86 : public TargetInfo {
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
};
} // namespace
}
}
-void X86::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
- switch (type) {
+void X86::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_386_8:
// 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.
- checkIntUInt(loc, val, 8, type);
+ checkIntUInt(loc, val, 8, rel);
*loc = val;
break;
case R_386_PC8:
- checkInt(loc, val, 8, type);
+ checkInt(loc, val, 8, rel);
*loc = val;
break;
case R_386_16:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_386_PC16:
// current location subtracted from it.
// We just check that Val fits in 17 bits. This misses some cases, but
// should have no false positives.
- checkInt(loc, val, 17, type);
+ checkInt(loc, val, 17, rel);
write16le(loc, val);
break;
case R_386_32:
case R_386_TLS_LE_32:
case R_386_TLS_TPOFF:
case R_386_TLS_TPOFF32:
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32le(loc, val);
break;
default:
}
}
-void X86::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void X86::relaxTlsGdToLe(uint8_t *loc, const Relocation &, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
write32le(loc + 5, val);
}
-void X86::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
+void X86::relaxTlsGdToIe(uint8_t *loc, const Relocation &, uint64_t val) const {
// Convert
// leal x@tlsgd(, %ebx, 1),
// call __tls_get_addr@plt
// In some conditions, relocations can be optimized to avoid using GOT.
// This function does that for Initial Exec to Local Exec case.
-void X86::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void X86::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
// Ulrich's document section 6.2 says that @gotntpoff can
// be used with MOVL or ADDL instructions.
// @indntpoff is similar to @gotntpoff, but for use in
// position dependent code.
uint8_t reg = (loc[-1] >> 3) & 7;
- if (type == R_386_TLS_IE) {
+ if (rel.type == R_386_TLS_IE) {
if (loc[-1] == 0xa1) {
// "movl foo@indntpoff,%eax" -> "movl $foo,%eax"
// This case is different from the generic case below because
loc[-1] = 0xc0 | reg;
}
} else {
- assert(type == R_386_TLS_GOTIE);
+ assert(rel.type == R_386_TLS_GOTIE);
if (loc[-2] == 0x8b) {
// "movl foo@gottpoff(%rip),%reg" -> "movl $foo,%reg"
loc[-2] = 0xc7;
write32le(loc, val);
}
-void X86::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
- if (type == R_386_TLS_LDO_32) {
+void X86::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ if (rel.type == R_386_TLS_LDO_32) {
write32le(loc, val);
return;
}
write32le(buf + 22, -off - 26);
}
-TargetInfo *getX86TargetInfo() {
+TargetInfo *elf::getX86TargetInfo() {
if (config->zRetpolineplt) {
if (config->isPic) {
static RetpolinePic t;
static X86 t;
return &t;
}
-
-} // namespace elf
-} // namespace lld
//===----------------------------------------------------------------------===//
#include "InputFiles.h"
+#include "OutputSections.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Target.h"
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
class X86_64 : public TargetInfo {
void writePltHeader(uint8_t *buf) const override;
void writePlt(uint8_t *buf, const Symbol &sym,
uint64_t pltEntryAddr) const override;
- void relocateOne(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void applyJumpInstrMod(uint8_t *loc, JumpModType type,
+ unsigned size) const override;
RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const override;
- void relaxGot(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const override;
- void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const override;
+ void relaxGot(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
+ void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const override;
bool adjustPrologueForCrossSplitStack(uint8_t *loc, uint8_t *end,
uint8_t stOther) const override;
+ bool deleteFallThruJmpInsn(InputSection &is, InputFile *file,
+ InputSection *nextIS) const override;
};
} // namespace
+// This is vector of NOP instructions of sizes from 1 to 8 bytes. The
+// appropriately sized instructions are used to fill the gaps between sections
+// which are executed during fall through.
+static const std::vector<std::vector<uint8_t>> nopInstructions = {
+ {0x90},
+ {0x66, 0x90},
+ {0x0f, 0x1f, 0x00},
+ {0x0f, 0x1f, 0x40, 0x00},
+ {0x0f, 0x1f, 0x44, 0x00, 0x00},
+ {0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00},
+ {0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00},
+ {0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},
+ {0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}};
+
X86_64::X86_64() {
copyRel = R_X86_64_COPY;
gotRel = R_X86_64_GLOB_DAT;
pltEntrySize = 16;
ipltEntrySize = 16;
trapInstr = {0xcc, 0xcc, 0xcc, 0xcc}; // 0xcc = INT3
+ nopInstrs = nopInstructions;
// Align to the large page size (known as a superpage or huge page).
// FreeBSD automatically promotes large, superpage-aligned allocations.
int X86_64::getTlsGdRelaxSkip(RelType type) const { return 2; }
+// Opcodes for the different X86_64 jmp instructions.
+enum JmpInsnOpcode : uint32_t {
+ J_JMP_32,
+ J_JNE_32,
+ J_JE_32,
+ J_JG_32,
+ J_JGE_32,
+ J_JB_32,
+ J_JBE_32,
+ J_JL_32,
+ J_JLE_32,
+ J_JA_32,
+ J_JAE_32,
+ J_UNKNOWN,
+};
+
+// Given the first (optional) and second byte of the insn's opcode, this
+// returns the corresponding enum value.
+static JmpInsnOpcode getJmpInsnType(const uint8_t *first,
+ const uint8_t *second) {
+ if (*second == 0xe9)
+ return J_JMP_32;
+
+ if (first == nullptr)
+ return J_UNKNOWN;
+
+ if (*first == 0x0f) {
+ switch (*second) {
+ case 0x84:
+ return J_JE_32;
+ case 0x85:
+ return J_JNE_32;
+ case 0x8f:
+ return J_JG_32;
+ case 0x8d:
+ return J_JGE_32;
+ case 0x82:
+ return J_JB_32;
+ case 0x86:
+ return J_JBE_32;
+ case 0x8c:
+ return J_JL_32;
+ case 0x8e:
+ return J_JLE_32;
+ case 0x87:
+ return J_JA_32;
+ case 0x83:
+ return J_JAE_32;
+ }
+ }
+ return J_UNKNOWN;
+}
+
+// Return the relocation index for input section IS with a specific Offset.
+// Returns the maximum size of the vector if no such relocation is found.
+static unsigned getRelocationWithOffset(const InputSection &is,
+ uint64_t offset) {
+ unsigned size = is.relocations.size();
+ for (unsigned i = size - 1; i + 1 > 0; --i) {
+ if (is.relocations[i].offset == offset && is.relocations[i].expr != R_NONE)
+ return i;
+ }
+ return size;
+}
+
+// Returns true if R corresponds to a relocation used for a jump instruction.
+// TODO: Once special relocations for relaxable jump instructions are available,
+// this should be modified to use those relocations.
+static bool isRelocationForJmpInsn(Relocation &R) {
+ return R.type == R_X86_64_PLT32 || R.type == R_X86_64_PC32 ||
+ R.type == R_X86_64_PC8;
+}
+
+// Return true if Relocation R points to the first instruction in the
+// next section.
+// TODO: Delete this once psABI reserves a new relocation type for fall thru
+// jumps.
+static bool isFallThruRelocation(InputSection &is, InputFile *file,
+ InputSection *nextIS, Relocation &r) {
+ if (!isRelocationForJmpInsn(r))
+ return false;
+
+ uint64_t addrLoc = is.getOutputSection()->addr + is.outSecOff + r.offset;
+ uint64_t targetOffset = InputSectionBase::getRelocTargetVA(
+ file, r.type, r.addend, addrLoc, *r.sym, r.expr);
+
+ // If this jmp is a fall thru, the target offset is the beginning of the
+ // next section.
+ uint64_t nextSectionOffset =
+ nextIS->getOutputSection()->addr + nextIS->outSecOff;
+ return (addrLoc + 4 + targetOffset) == nextSectionOffset;
+}
+
+// Return the jmp instruction opcode that is the inverse of the given
+// opcode. For example, JE inverted is JNE.
+static JmpInsnOpcode invertJmpOpcode(const JmpInsnOpcode opcode) {
+ switch (opcode) {
+ case J_JE_32:
+ return J_JNE_32;
+ case J_JNE_32:
+ return J_JE_32;
+ case J_JG_32:
+ return J_JLE_32;
+ case J_JGE_32:
+ return J_JL_32;
+ case J_JB_32:
+ return J_JAE_32;
+ case J_JBE_32:
+ return J_JA_32;
+ case J_JL_32:
+ return J_JGE_32;
+ case J_JLE_32:
+ return J_JG_32;
+ case J_JA_32:
+ return J_JBE_32;
+ case J_JAE_32:
+ return J_JB_32;
+ default:
+ return J_UNKNOWN;
+ }
+}
+
+// Deletes direct jump instruction in input sections that jumps to the
+// following section as it is not required. If there are two consecutive jump
+// instructions, it checks if they can be flipped and one can be deleted.
+// For example:
+// .section .text
+// a.BB.foo:
+// ...
+// 10: jne aa.BB.foo
+// 16: jmp bar
+// aa.BB.foo:
+// ...
+//
+// can be converted to:
+// a.BB.foo:
+// ...
+// 10: je bar #jne flipped to je and the jmp is deleted.
+// aa.BB.foo:
+// ...
+bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file,
+ InputSection *nextIS) const {
+ const unsigned sizeOfDirectJmpInsn = 5;
+
+ if (nextIS == nullptr)
+ return false;
+
+ if (is.getSize() < sizeOfDirectJmpInsn)
+ return false;
+
+ // If this jmp insn can be removed, it is the last insn and the
+ // relocation is 4 bytes before the end.
+ unsigned rIndex = getRelocationWithOffset(is, is.getSize() - 4);
+ if (rIndex == is.relocations.size())
+ return false;
+
+ Relocation &r = is.relocations[rIndex];
+
+ // Check if the relocation corresponds to a direct jmp.
+ const uint8_t *secContents = is.data().data();
+ // If it is not a direct jmp instruction, there is nothing to do here.
+ if (*(secContents + r.offset - 1) != 0xe9)
+ return false;
+
+ if (isFallThruRelocation(is, file, nextIS, r)) {
+ // This is a fall thru and can be deleted.
+ r.expr = R_NONE;
+ r.offset = 0;
+ is.drop_back(sizeOfDirectJmpInsn);
+ is.nopFiller = true;
+ return true;
+ }
+
+ // Now, check if flip and delete is possible.
+ const unsigned sizeOfJmpCCInsn = 6;
+ // To flip, there must be atleast one JmpCC and one direct jmp.
+ if (is.getSize() < sizeOfDirectJmpInsn + sizeOfJmpCCInsn)
+ return 0;
+
+ unsigned rbIndex =
+ getRelocationWithOffset(is, (is.getSize() - sizeOfDirectJmpInsn - 4));
+ if (rbIndex == is.relocations.size())
+ return 0;
+
+ Relocation &rB = is.relocations[rbIndex];
+
+ const uint8_t *jmpInsnB = secContents + rB.offset - 1;
+ JmpInsnOpcode jmpOpcodeB = getJmpInsnType(jmpInsnB - 1, jmpInsnB);
+ if (jmpOpcodeB == J_UNKNOWN)
+ return false;
+
+ if (!isFallThruRelocation(is, file, nextIS, rB))
+ return false;
+
+ // jmpCC jumps to the fall thru block, the branch can be flipped and the
+ // jmp can be deleted.
+ JmpInsnOpcode jInvert = invertJmpOpcode(jmpOpcodeB);
+ if (jInvert == J_UNKNOWN)
+ return false;
+ is.jumpInstrMods.push_back({jInvert, (rB.offset - 1), 4});
+ // Move R's values to rB except the offset.
+ rB = {r.expr, r.type, rB.offset, r.addend, r.sym};
+ // Cancel R
+ r.expr = R_NONE;
+ r.offset = 0;
+ is.drop_back(sizeOfDirectJmpInsn);
+ is.nopFiller = true;
+ return true;
+}
+
RelExpr X86_64::getRelExpr(RelType type, const Symbol &s,
const uint8_t *loc) const {
if (type == R_X86_64_GOTTPOFF)
return R_X86_64_NONE;
}
-void X86_64::relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const {
- if (type == R_X86_64_TLSGD) {
+void X86_64::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ if (rel.type == R_X86_64_TLSGD) {
// Convert
// .byte 0x66
// leaq x@tlsgd(%rip), %rdi
// lea x@tlsgd(%rip), %rax
// call *(%rax)
// to the following two instructions.
- assert(type == R_X86_64_GOTPC32_TLSDESC);
+ assert(rel.type == R_X86_64_GOTPC32_TLSDESC);
if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
"in callq *x@tlsdesc(%rip), %rax");
}
}
-void X86_64::relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const {
- if (type == R_X86_64_TLSGD) {
+void X86_64::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ if (rel.type == R_X86_64_TLSGD) {
// Convert
// .byte 0x66
// leaq x@tlsgd(%rip), %rdi
// lea x@tlsgd(%rip), %rax
// call *(%rax)
// to the following two instructions.
- assert(type == R_X86_64_GOTPC32_TLSDESC);
+ assert(rel.type == R_X86_64_GOTPC32_TLSDESC);
if (memcmp(loc - 3, "\x48\x8d\x05", 3)) {
error(getErrorLocation(loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used "
"in callq *x@tlsdesc(%rip), %rax");
// In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to
// R_X86_64_TPOFF32 so that it does not use GOT.
-void X86_64::relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const {
+void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &,
+ uint64_t val) const {
uint8_t *inst = loc - 3;
uint8_t reg = loc[-1] >> 3;
uint8_t *regSlot = loc - 1;
write32le(loc, val + 4);
}
-void X86_64::relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const {
- if (type == R_X86_64_DTPOFF64) {
+void X86_64::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
+ if (rel.type == R_X86_64_DTPOFF64) {
write64le(loc, val);
return;
}
- if (type == R_X86_64_DTPOFF32) {
+ if (rel.type == R_X86_64_DTPOFF32) {
write32le(loc, val);
return;
}
"expected R_X86_64_PLT32 or R_X86_64_GOTPCRELX after R_X86_64_TLSLD");
}
-void X86_64::relocateOne(uint8_t *loc, RelType type, uint64_t val) const {
+// A JumpInstrMod at a specific offset indicates that the jump instruction
+// opcode at that offset must be modified. This is specifically used to relax
+// jump instructions with basic block sections. This function looks at the
+// JumpMod and effects the change.
+void X86_64::applyJumpInstrMod(uint8_t *loc, JumpModType type,
+ unsigned size) const {
switch (type) {
+ case J_JMP_32:
+ if (size == 4)
+ *loc = 0xe9;
+ else
+ *loc = 0xeb;
+ break;
+ case J_JE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x84;
+ } else
+ *loc = 0x74;
+ break;
+ case J_JNE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x85;
+ } else
+ *loc = 0x75;
+ break;
+ case J_JG_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x8f;
+ } else
+ *loc = 0x7f;
+ break;
+ case J_JGE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x8d;
+ } else
+ *loc = 0x7d;
+ break;
+ case J_JB_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x82;
+ } else
+ *loc = 0x72;
+ break;
+ case J_JBE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x86;
+ } else
+ *loc = 0x76;
+ break;
+ case J_JL_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x8c;
+ } else
+ *loc = 0x7c;
+ break;
+ case J_JLE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x8e;
+ } else
+ *loc = 0x7e;
+ break;
+ case J_JA_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x87;
+ } else
+ *loc = 0x77;
+ break;
+ case J_JAE_32:
+ if (size == 4) {
+ loc[-1] = 0x0f;
+ *loc = 0x83;
+ } else
+ *loc = 0x73;
+ break;
+ case J_UNKNOWN:
+ llvm_unreachable("Unknown Jump Relocation");
+ }
+}
+
+void X86_64::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
+ switch (rel.type) {
case R_X86_64_8:
- checkIntUInt(loc, val, 8, type);
+ checkIntUInt(loc, val, 8, rel);
*loc = val;
break;
case R_X86_64_PC8:
- checkInt(loc, val, 8, type);
+ checkInt(loc, val, 8, rel);
*loc = val;
break;
case R_X86_64_16:
- checkIntUInt(loc, val, 16, type);
+ checkIntUInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_X86_64_PC16:
- checkInt(loc, val, 16, type);
+ checkInt(loc, val, 16, rel);
write16le(loc, val);
break;
case R_X86_64_32:
- checkUInt(loc, val, 32, type);
+ checkUInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_X86_64_32S:
case R_X86_64_TLSLD:
case R_X86_64_DTPOFF32:
case R_X86_64_SIZE32:
- checkInt(loc, val, 32, type);
+ checkInt(loc, val, 32, rel);
write32le(loc, val);
break;
case R_X86_64_64:
write32le(loc, val);
}
-void X86_64::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
+void X86_64::relaxGot(uint8_t *loc, const Relocation &, uint64_t val) const {
const uint8_t op = loc[-2];
const uint8_t modRm = loc[-1];
return &t;
}
-TargetInfo *getX86_64TargetInfo() { return getTargetInfo(); }
-
-} // namespace elf
-} // namespace lld
+TargetInfo *elf::getX86_64TargetInfo() { return getTargetInfo(); }
#include <numeric>
using namespace llvm;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
struct Edge {
// Sort sections by the profile data provided by -callgraph-profile-file
//
// This first builds a call graph based on the profile data then merges sections
-// according to the C³ huristic. All clusters are then sorted by a density
+// according to the C³ heuristic. All clusters are then sorted by a density
// metric to further improve locality.
-DenseMap<const InputSectionBase *, int> computeCallGraphProfileOrder() {
+DenseMap<const InputSectionBase *, int> elf::computeCallGraphProfileOrder() {
return CallGraphSort().run();
}
-
-} // namespace elf
-} // namespace lld
#include "llvm/Support/CachePruning.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/GlobPattern.h"
#include <atomic>
#include <vector>
uint32_t andFeatures = 0;
llvm::CachePruningPolicy thinLTOCachePolicy;
llvm::StringMap<uint64_t> sectionStartMap;
+ llvm::StringRef bfdname;
llvm::StringRef chroot;
llvm::StringRef dynamicLinker;
llvm::StringRef dwoDir;
llvm::StringRef optRemarksPasses;
llvm::StringRef optRemarksFormat;
llvm::StringRef progName;
+ llvm::StringRef printArchiveStats;
llvm::StringRef printSymbolOrder;
llvm::StringRef soName;
llvm::StringRef sysroot;
llvm::StringRef thinLTOCacheDir;
llvm::StringRef thinLTOIndexOnlyArg;
+ llvm::StringRef ltoBasicBlockSections;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOObjectSuffixReplace;
std::pair<llvm::StringRef, llvm::StringRef> thinLTOPrefixReplace;
std::string rpath;
std::vector<llvm::StringRef> filterList;
std::vector<llvm::StringRef> searchPaths;
std::vector<llvm::StringRef> symbolOrderingFile;
+ std::vector<llvm::StringRef> thinLTOModulesToCompile;
std::vector<llvm::StringRef> undefined;
std::vector<SymbolVersion> dynamicList;
std::vector<uint8_t> buildIdVector;
bool checkSections;
bool compressDebugSections;
bool cref;
+ std::vector<std::pair<llvm::GlobPattern, uint64_t>> deadRelocInNonAlloc;
bool defineCommon;
bool demangle = true;
bool dependentLibraries;
bool exportDynamic;
bool fixCortexA53Errata843419;
bool fixCortexA8;
- bool forceBTI;
bool formatBinary = false;
bool gcSections;
bool gdbIndex;
bool gnuHash = false;
bool gnuUnique;
- bool hasDynamicList = false;
bool hasDynSymTab;
bool ignoreDataAddressEquality;
bool ignoreFunctionAddressEquality;
bool ltoCSProfileGenerate;
bool ltoDebugPassManager;
+ bool ltoEmitAsm;
bool ltoNewPassManager;
+ bool ltoUniqueBasicBlockSectionNames;
+ bool ltoWholeProgramVisibility;
bool mergeArmExidx;
bool mipsN32Abi = false;
bool mmapOutputFile;
bool nostdlib;
bool oFormatBinary;
bool omagic;
+ bool optimizeBBJumps;
bool optRemarksWithHotness;
- bool pacPlt;
bool picThunk;
bool pie;
bool printGcSections;
bool relocatable;
bool relrPackDynRelocs;
bool saveTemps;
+ llvm::Optional<uint32_t> shuffleSectionSeed;
bool singleRoRx;
bool shared;
+ bool symbolic;
bool isStatic = false;
bool sysvHash = false;
bool target1Rel;
bool trace;
bool thinLTOEmitImportsFiles;
bool thinLTOIndexOnly;
+ bool timeTraceEnabled;
bool tocOptimize;
bool undefinedVersion;
+ bool unique;
bool useAndroidRelrTags = false;
bool warnBackrefs;
+ std::vector<llvm::GlobPattern> warnBackrefsExclude;
bool warnCommon;
bool warnIfuncTextrel;
bool warnMissingEntry;
bool writeAddends;
bool zCombreloc;
bool zCopyreloc;
+ bool zForceBti;
bool zForceIbt;
bool zGlobal;
bool zHazardplt;
bool zNodlopen;
bool zNow;
bool zOrigin;
+ bool zPacPlt;
bool zRelro;
bool zRodynamic;
bool zShstk;
+ uint8_t zStartStopVisibility;
bool zText;
bool zRetpolineplt;
bool zWxneeded;
ELFKind ekind = ELFNoneKind;
uint16_t emachine = llvm::ELF::EM_NONE;
llvm::Optional<uint64_t> imageBase;
- // commonPageSize and maxPageSize are influenced by nmagic or omagic
- // so may be set to 1 if either of those options is given.
uint64_t commonPageSize;
uint64_t maxPageSize;
- // textAlignPageSize is the target max page size for the purpose
- // of aligning text sections, which may be unaligned if given nmagic
- uint64_t textAlignPageSize;
uint64_t mipsGotSize;
uint64_t zStackSize;
unsigned ltoPartitions;
unsigned ltoo;
unsigned optimize;
- unsigned thinLTOJobs;
+ StringRef thinLTOJobs;
+ unsigned timeTraceGranularity;
int32_t splitStackAdjustSize;
// The following config options do not directly correspond to any
using namespace llvm;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
template <class ELFT> LLDDwarfObj<ELFT>::LLDDwarfObj(ObjFile<ELFT> *obj) {
- for (InputSectionBase *sec : obj->getSections()) {
+ // Get the ELF sections to retrieve sh_flags. See the SHF_GROUP comment below.
+ ArrayRef<typename ELFT::Shdr> objSections =
+ CHECK(obj->getObj().sections(), obj);
+ assert(objSections.size() == obj->getSections().size());
+ for (auto it : llvm::enumerate(obj->getSections())) {
+ InputSectionBase *sec = it.value();
if (!sec)
continue;
.Case(".debug_addr", &addrSection)
.Case(".debug_gnu_pubnames", &gnuPubnamesSection)
.Case(".debug_gnu_pubtypes", &gnuPubtypesSection)
- .Case(".debug_info", &infoSection)
+ .Case(".debug_loclists", &loclistsSection)
.Case(".debug_ranges", &rangesSection)
.Case(".debug_rnglists", &rnglistsSection)
.Case(".debug_str_offsets", &strOffsetsSection)
strSection = toStringRef(sec->data());
else if (sec->name == ".debug_line_str")
lineStrSection = toStringRef(sec->data());
+ else if (sec->name == ".debug_info" &&
+ !(objSections[it.index()].sh_flags & ELF::SHF_GROUP)) {
+ // In DWARF v5, -fdebug-types-section places type units in .debug_info
+ // sections in COMDAT groups. They are not compile units and thus should
+ // be ignored for .gdb_index/diagnostics purposes.
+ //
+ // We use a simple heuristic: the compile unit does not have the SHF_GROUP
+ // flag. If we place compile units in COMDAT groups in the future, we may
+ // need to perform a lightweight parsing. We drop the SHF_GROUP flag when
+ // the InputSection was created, so we need to retrieve sh_flags from the
+ // associated ELF section header.
+ infoSection.Data = toStringRef(sec->data());
+ infoSection.sec = sec;
+ }
}
}
// its zero value will terminate the decoding of .debug_ranges prematurely.
Symbol &s = file->getRelocTargetSym(rel);
uint64_t val = 0;
- if (auto *dr = dyn_cast<Defined>(&s)) {
+ if (auto *dr = dyn_cast<Defined>(&s))
val = dr->value;
- // FIXME: We should be consistent about always adding the file
- // offset or not.
- if (dr->section->flags & ELF::SHF_ALLOC)
- val += cast<InputSection>(dr->section)->getOffsetInFile();
- }
-
DataRefImpl d;
d.p = getAddend<ELFT>(rel);
return RelocAddrEntry{secIndex, RelocationRef(d, nullptr),
return findAux(*sec.sec, pos, sec.sec->template rels<ELFT>());
}
-template class LLDDwarfObj<ELF32LE>;
-template class LLDDwarfObj<ELF32BE>;
-template class LLDDwarfObj<ELF64LE>;
-template class LLDDwarfObj<ELF64BE>;
-
-} // namespace elf
-} // namespace lld
+template class elf::LLDDwarfObj<ELF32LE>;
+template class elf::LLDDwarfObj<ELF32BE>;
+template class elf::LLDDwarfObj<ELF64LE>;
+template class elf::LLDDwarfObj<ELF64BE>;
f(infoSection);
}
+ InputSection *getInfoSection() const {
+ return cast<InputSection>(infoSection.sec);
+ }
+
+ const llvm::DWARFSection &getLoclistsSection() const override {
+ return loclistsSection;
+ }
+
const llvm::DWARFSection &getRangesSection() const override {
return rangesSection;
}
return addrSection;
}
- const llvm::DWARFSection &getGnuPubnamesSection() const override {
+ const LLDDWARFSection &getGnuPubnamesSection() const override {
return gnuPubnamesSection;
}
- const llvm::DWARFSection &getGnuPubtypesSection() const override {
+ const LLDDWARFSection &getGnuPubtypesSection() const override {
return gnuPubtypesSection;
}
LLDDWARFSection gnuPubnamesSection;
LLDDWARFSection gnuPubtypesSection;
LLDDWARFSection infoSection;
+ LLDDWARFSection loclistsSection;
LLDDWARFSection rangesSection;
LLDDWARFSection rnglistsSection;
LLDDWARFSection strOffsetsSection;
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "lld/Common/TargetOptionsCommandFlags.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/LEB128.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
#include <utility>
using namespace llvm::object;
using namespace llvm::sys;
using namespace llvm::support;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
-
-Configuration *config;
-LinkerDriver *driver;
+Configuration *elf::config;
+LinkerDriver *elf::driver;
static void setConfigs(opt::InputArgList &args);
static void readConfigs(opt::InputArgList &args);
-bool link(ArrayRef<const char *> args, bool canExitEarly, raw_ostream &stdoutOS,
- raw_ostream &stderrOS) {
+bool elf::link(ArrayRef<const char *> args, bool canExitEarly,
+ raw_ostream &stdoutOS, raw_ostream &stderrOS) {
lld::stdoutOS = &stdoutOS;
lld::stderrOS = &stderrOS;
inputSections.clear();
outputSections.clear();
+ archiveFiles.clear();
binaryFiles.clear();
bitcodeFiles.clear();
+ lazyObjFiles.clear();
objectFiles.clear();
sharedFiles.clear();
+ backwardReferences.clear();
config = make<Configuration>();
driver = make<LinkerDriver>();
.Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
.Case("elf32lriscv", {ELF32LEKind, EM_RISCV})
.Cases("elf32ppc", "elf32ppclinux", {ELF32BEKind, EM_PPC})
- .Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9})
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
.Case("elf64lriscv", {ELF64LEKind, EM_RISCV})
.Cases("elf_amd64", "elf_x86_64", {ELF64LEKind, EM_X86_64})
.Case("elf_i386", {ELF32LEKind, EM_386})
.Case("elf_iamcu", {ELF32LEKind, EM_IAMCU})
+ .Case("elf64_sparc", {ELF64BEKind, EM_SPARCV9})
.Default({ELFNoneKind, EM_NONE});
if (ret.first == ELFNoneKind)
error("-z force-ibt may not be used with -z retpolineplt");
if (config->emachine != EM_AARCH64) {
- if (config->pacPlt)
+ if (config->zPacPlt)
error("-z pac-plt only supported on AArch64");
- if (config->forceBTI)
+ if (config->zForceBti)
error("-z force-bti only supported on AArch64");
}
}
return GnuStackKind::NoExec;
}
+static uint8_t getZStartStopVisibility(opt::InputArgList &args) {
+ for (auto *arg : args.filtered_reverse(OPT_z)) {
+ std::pair<StringRef, StringRef> kv = StringRef(arg->getValue()).split('=');
+ if (kv.first == "start-stop-visibility") {
+ if (kv.second == "default")
+ return STV_DEFAULT;
+ else if (kv.second == "internal")
+ return STV_INTERNAL;
+ else if (kv.second == "hidden")
+ return STV_HIDDEN;
+ else if (kv.second == "protected")
+ return STV_PROTECTED;
+ error("unknown -z start-stop-visibility= value: " + StringRef(kv.second));
+ }
+ }
+ return STV_PROTECTED;
+}
+
static bool isKnownZFlag(StringRef s) {
return s == "combreloc" || s == "copyreloc" || s == "defs" ||
s == "execstack" || s == "force-bti" || s == "force-ibt" ||
s == "nocombreloc" || s == "nocopyreloc" || s == "nodefaultlib" ||
s == "nodelete" || s == "nodlopen" || s == "noexecstack" ||
s == "nognustack" || s == "nokeep-text-section-prefix" ||
- s == "norelro" || s == "noretpolineplt" ||
- s == "noseparate-code" || s == "notext" ||
- s == "now" || s == "origin" || s == "pac-plt" || s == "relro" ||
- s == "retpolineplt" || s == "rodynamic" || s == "shstk" ||
- s == "text" || s == "undefs" || s == "wxneeded" ||
- s.startswith("common-page-size=") || s.startswith("max-page-size=") ||
- s.startswith("stack-size=");
+ s == "norelro" || s == "noseparate-code" || s == "notext" ||
+ s == "now" || s == "origin" || s == "pac-plt" || s == "rel" ||
+ s == "rela" || s == "relro" || s == "retpolineplt" ||
+ s == "rodynamic" || s == "shstk" || s == "text" || s == "undefs" ||
+ s == "wxneeded" || s.startswith("common-page-size=") ||
+ s.startswith("dead-reloc-in-nonalloc=") ||
+ s.startswith("max-page-size=") || s.startswith("stack-size=") ||
+ s.startswith("start-stop-visibility=");
}
// Report an error for an unknown -z option.
if (args.hasArg(OPT_version))
return;
- initLLVM();
- createFiles(args);
- if (errorCount())
- return;
+ // Initialize time trace profiler.
+ if (config->timeTraceEnabled)
+ timeTraceProfilerInitialize(config->timeTraceGranularity, config->progName);
- inferMachineType();
- setConfigs(args);
- checkOptions();
- if (errorCount())
- return;
+ {
+ llvm::TimeTraceScope timeScope("ExecuteLinker");
- // The Target instance handles target-specific stuff, such as applying
- // relocations or writing a PLT section. It also contains target-dependent
- // values such as a default image base address.
- target = getTarget();
+ initLLVM();
+ createFiles(args);
+ if (errorCount())
+ return;
- switch (config->ekind) {
- case ELF32LEKind:
- link<ELF32LE>(args);
- return;
- case ELF32BEKind:
- link<ELF32BE>(args);
- return;
- case ELF64LEKind:
- link<ELF64LE>(args);
- return;
- case ELF64BEKind:
- link<ELF64BE>(args);
- return;
- default:
- llvm_unreachable("unknown Config->EKind");
+ inferMachineType();
+ setConfigs(args);
+ checkOptions();
+ if (errorCount())
+ return;
+
+ // The Target instance handles target-specific stuff, such as applying
+ // relocations or writing a PLT section. It also contains target-dependent
+ // values such as a default image base address.
+ target = getTarget();
+
+ switch (config->ekind) {
+ case ELF32LEKind:
+ link<ELF32LE>(args);
+ break;
+ case ELF32BEKind:
+ link<ELF32BE>(args);
+ break;
+ case ELF64LEKind:
+ link<ELF64LE>(args);
+ break;
+ case ELF64BEKind:
+ link<ELF64BE>(args);
+ break;
+ default:
+ llvm_unreachable("unknown Config->EKind");
+ }
+ }
+
+ if (config->timeTraceEnabled) {
+ if (auto E = timeTraceProfilerWrite(args.getLastArgValue(OPT_time_trace_file_eq).str(),
+ config->outputFile)) {
+ handleAllErrors(std::move(E), [&](const StringError &SE) {
+ error(SE.getMessage());
+ });
+ return;
+ }
+
+ timeTraceProfilerCleanup();
}
}
}
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)
return names.takeVector();
}
+static bool getIsRela(opt::InputArgList &args) {
+ // If -z rel or -z rela is specified, use the last option.
+ for (auto *arg : args.filtered_reverse(OPT_z)) {
+ StringRef s(arg->getValue());
+ if (s == "rel")
+ return false;
+ if (s == "rela")
+ return true;
+ }
+
+ // Otherwise use the psABI defined relocation entry format.
+ uint16_t m = config->emachine;
+ return m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON || m == EM_PPC ||
+ m == EM_PPC64 || m == EM_RISCV || m == EM_X86_64;
+}
+
static void parseClangOption(StringRef opt, const Twine &msg) {
std::string err;
raw_string_ostream os(err);
args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false);
errorHandler().vsDiagnostics =
args.hasArg(OPT_visual_studio_diagnostics_format, false);
- threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true);
config->allowMultipleDefinition =
args.hasFlag(OPT_allow_multiple_definition,
config->cref = args.hasFlag(OPT_cref, OPT_no_cref, false);
config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common,
!args.hasArg(OPT_relocatable));
+ config->optimizeBBJumps =
+ args.hasFlag(OPT_optimize_bb_jumps, OPT_no_optimize_bb_jumps, false);
config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true);
config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true);
config->disableVerify = args.hasArg(OPT_disable_verify);
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, false);
config->filterList = args::getStrings(args, OPT_filter);
config->fini = args.getLastArgValue(OPT_fini, "_fini");
- config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419);
- config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8);
- config->forceBTI = hasZOption(args, "force-bti");
+ config->fixCortexA53Errata843419 = args.hasArg(OPT_fix_cortex_a53_843419) &&
+ !args.hasArg(OPT_relocatable);
+ config->fixCortexA8 =
+ args.hasArg(OPT_fix_cortex_a8) && !args.hasArg(OPT_relocatable);
config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false);
config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true);
config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false);
config->ignoreDataAddressEquality =
args.hasArg(OPT_ignore_data_address_equality);
config->ignoreFunctionAddressEquality =
- args.hasFlag(OPT_ignore_function_address_equality,
- OPT_no_ignore_function_address_equality, true);
+ args.hasArg(OPT_ignore_function_address_equality);
config->init = args.getLastArgValue(OPT_init, "_init");
config->ltoAAPipeline = args.getLastArgValue(OPT_lto_aa_pipeline);
config->ltoCSProfileGenerate = args.hasArg(OPT_lto_cs_profile_generate);
config->ltoCSProfileFile = args.getLastArgValue(OPT_lto_cs_profile_file);
config->ltoDebugPassManager = args.hasArg(OPT_lto_debug_pass_manager);
+ config->ltoEmitAsm = args.hasArg(OPT_lto_emit_asm);
config->ltoNewPassManager = args.hasArg(OPT_lto_new_pass_manager);
config->ltoNewPmPasses = args.getLastArgValue(OPT_lto_newpm_passes);
+ config->ltoWholeProgramVisibility =
+ args.hasArg(OPT_lto_whole_program_visibility);
config->ltoo = args::getInteger(args, OPT_lto_O, 2);
config->ltoObjPath = args.getLastArgValue(OPT_lto_obj_path_eq);
config->ltoPartitions = args::getInteger(args, OPT_lto_partitions, 1);
config->ltoSampleProfile = args.getLastArgValue(OPT_lto_sample_profile);
+ config->ltoBasicBlockSections =
+ args.getLastArgValue(OPT_lto_basicblock_sections);
+ config->ltoUniqueBasicBlockSectionNames =
+ args.hasFlag(OPT_lto_unique_bb_section_names,
+ OPT_no_lto_unique_bb_section_names, false);
config->mapFile = args.getLastArgValue(OPT_Map);
config->mipsGotSize = args::getInteger(args, OPT_mips_got_size, 0xfff0);
config->mergeArmExidx =
config->optimize = args::getInteger(args, OPT_O, 1);
config->orphanHandling = getOrphanHandling(args);
config->outputFile = args.getLastArgValue(OPT_o);
- config->pacPlt = hasZOption(args, "pac-plt");
-#ifdef __OpenBSD__
- config->pie = args.hasFlag(OPT_pie, OPT_no_pie,
- !args.hasArg(OPT_shared) && !args.hasArg(OPT_relocatable));
-#else
config->pie = args.hasFlag(OPT_pie, OPT_no_pie, false);
-#endif
config->printIcfSections =
args.hasFlag(OPT_print_icf_sections, OPT_no_print_icf_sections, false);
config->printGcSections =
args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false);
+ config->printArchiveStats = args.getLastArgValue(OPT_print_archive_stats);
config->printSymbolOrder =
args.getLastArgValue(OPT_print_symbol_order);
config->rpath = getRpath(args);
config->relocatable = args.hasArg(OPT_relocatable);
config->saveTemps = args.hasArg(OPT_save_temps);
+ if (args.hasArg(OPT_shuffle_sections))
+ config->shuffleSectionSeed = args::getInteger(args, OPT_shuffle_sections, 0);
config->searchPaths = args::getStrings(args, OPT_library_path);
config->sectionStartMap = getSectionStartMap(args);
config->shared = args.hasArg(OPT_shared);
- config->singleRoRx = args.hasArg(OPT_no_rosegment);
+ config->singleRoRx = !args.hasFlag(OPT_rosegment, OPT_no_rosegment, true);
config->soName = args.getLastArgValue(OPT_soname);
config->sortSection = getSortSection(args);
config->splitStackAdjustSize = args::getInteger(args, OPT_split_stack_adjust_size, 16384);
config->thinLTOIndexOnly = args.hasArg(OPT_thinlto_index_only) ||
args.hasArg(OPT_thinlto_index_only_eq);
config->thinLTOIndexOnlyArg = args.getLastArgValue(OPT_thinlto_index_only_eq);
- config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
config->thinLTOObjectSuffixReplace =
getOldNewOptions(args, OPT_thinlto_object_suffix_replace_eq);
config->thinLTOPrefixReplace =
getOldNewOptions(args, OPT_thinlto_prefix_replace_eq);
+ config->thinLTOModulesToCompile =
+ args::getStrings(args, OPT_thinlto_single_module_eq);
+ config->timeTraceEnabled = args.hasArg(OPT_time_trace);
+ config->timeTraceGranularity =
+ args::getInteger(args, OPT_time_trace_granularity, 500);
config->trace = args.hasArg(OPT_trace);
config->undefined = args::getStrings(args, OPT_undefined);
config->undefinedVersion =
args.hasFlag(OPT_undefined_version, OPT_no_undefined_version, true);
+ config->unique = args.hasArg(OPT_unique);
config->useAndroidRelrTags = args.hasFlag(
OPT_use_android_relr_tags, OPT_no_use_android_relr_tags, false);
config->unresolvedSymbols = getUnresolvedSymbolPolicy(args);
args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true);
config->zCombreloc = getZFlag(args, "combreloc", "nocombreloc", true);
config->zCopyreloc = getZFlag(args, "copyreloc", "nocopyreloc", true);
+ config->zForceBti = hasZOption(args, "force-bti");
config->zForceIbt = hasZOption(args, "force-ibt");
config->zGlobal = hasZOption(args, "global");
config->zGnustack = getZGnuStack(args);
config->zNodlopen = hasZOption(args, "nodlopen");
config->zNow = getZFlag(args, "now", "lazy", false);
config->zOrigin = hasZOption(args, "origin");
+ config->zPacPlt = hasZOption(args, "pac-plt");
config->zRelro = getZFlag(args, "relro", "norelro", true);
-#ifndef __OpenBSD__
- config->zRetpolineplt = getZFlag(args, "retpolineplt", "noretpolineplt", false);
-#else
- config->zRetpolineplt = getZFlag(args, "retpolineplt", "noretpolineplt", true);
-#endif
+ config->zRetpolineplt = hasZOption(args, "retpolineplt");
config->zRodynamic = hasZOption(args, "rodynamic");
config->zSeparate = getZSeparate(args);
config->zShstk = hasZOption(args, "shstk");
config->zStackSize = args::getZOptionValue(args, OPT_z, "stack-size", 0);
+ config->zStartStopVisibility = getZStartStopVisibility(args);
config->zText = getZFlag(args, "text", "notext", true);
config->zWxneeded = hasZOption(args, "wxneeded");
+ for (opt::Arg *arg : args.filtered(OPT_z)) {
+ std::pair<StringRef, StringRef> option =
+ StringRef(arg->getValue()).split('=');
+ if (option.first != "dead-reloc-in-nonalloc")
+ continue;
+ constexpr StringRef errPrefix = "-z dead-reloc-in-nonalloc=: ";
+ std::pair<StringRef, StringRef> kv = option.second.split('=');
+ if (kv.first.empty() || kv.second.empty()) {
+ error(errPrefix + "expected <section_glob>=<value>");
+ continue;
+ }
+ uint64_t v;
+ if (!to_integer(kv.second, v))
+ error(errPrefix + "expected a non-negative integer, but got '" +
+ kv.second + "'");
+ else if (Expected<GlobPattern> pat = GlobPattern::create(kv.first))
+ config->deadRelocInNonAlloc.emplace_back(std::move(*pat), v);
+ else
+ error(errPrefix + toString(pat.takeError()));
+ }
+
// Parse LTO options.
if (auto *arg = args.getLastArg(OPT_plugin_opt_mcpu_eq))
parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
arg->getSpelling());
- for (auto *arg : args.filtered(OPT_plugin_opt))
- parseClangOption(arg->getValue(), arg->getSpelling());
+ for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq_minus))
+ parseClangOption(std::string("-") + arg->getValue(), arg->getSpelling());
+
+ // GCC collect2 passes -plugin-opt=path/to/lto-wrapper with an absolute or
+ // relative path. Just ignore. If not ended with "lto-wrapper", consider it an
+ // unsupported LLVMgold.so option and error.
+ for (opt::Arg *arg : args.filtered(OPT_plugin_opt_eq))
+ if (!StringRef(arg->getValue()).endswith("lto-wrapper"))
+ error(arg->getSpelling() + ": unknown plugin option '" + arg->getValue() +
+ "'");
// Parse -mllvm options.
for (auto *arg : args.filtered(OPT_mllvm))
parseClangOption(arg->getValue(), arg->getSpelling());
+ // --threads= takes a positive integer and provides the default value for
+ // --thinlto-jobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v;
+ }
+ if (auto *arg = args.getLastArg(OPT_thinlto_jobs))
+ config->thinLTOJobs = arg->getValue();
+
if (config->ltoo > 3)
error("invalid optimization level for LTO: " + Twine(config->ltoo));
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 (!get_threadpool_strategy(config->thinLTOJobs))
+ error("--thinlto-jobs: invalid job count: " + config->thinLTOJobs);
if (config->splitStackAdjustSize < 0)
error("--split-stack-adjust-size: size must be >= 0");
{s, /*isExternCpp=*/false, /*hasWildcard=*/false});
}
- // Parses -dynamic-list and -export-dynamic-symbol. They make some
- // symbols private. Note that -export-dynamic takes precedence over them
- // as it says all symbols should be exported.
- if (!config->exportDynamic) {
- for (auto *arg : args.filtered(OPT_dynamic_list))
- if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
- readDynamicList(*buffer);
-
- for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
- config->dynamicList.push_back(
- {arg->getValue(), /*isExternCpp=*/false, /*hasWildcard=*/false});
+ for (opt::Arg *arg : args.filtered(OPT_warn_backrefs_exclude)) {
+ StringRef pattern(arg->getValue());
+ if (Expected<GlobPattern> pat = GlobPattern::create(pattern))
+ config->warnBackrefsExclude.push_back(std::move(*pat));
+ else
+ error(arg->getSpelling() + ": " + toString(pat.takeError()));
}
- // If --export-dynamic-symbol=foo is given and symbol foo is defined in
- // an object file in an archive file, that object file should be pulled
- // out and linked. (It doesn't have to behave like that from technical
- // point of view, but this is needed for compatibility with GNU.)
+ // When producing an executable, --dynamic-list specifies non-local defined
+ // symbols whith are required to be exported. When producing a shared object,
+ // symbols not specified by --dynamic-list are non-preemptible.
+ config->symbolic =
+ args.hasArg(OPT_Bsymbolic) || args.hasArg(OPT_dynamic_list);
+ for (auto *arg : args.filtered(OPT_dynamic_list))
+ if (Optional<MemoryBufferRef> buffer = readFile(arg->getValue()))
+ readDynamicList(*buffer);
+
+ // --export-dynamic-symbol specifies additional --dynamic-list symbols if any
+ // other option expresses a symbolic intention: -no-pie, -pie, -Bsymbolic,
+ // -Bsymbolic-functions (if STT_FUNC), --dynamic-list.
for (auto *arg : args.filtered(OPT_export_dynamic_symbol))
- config->undefined.push_back(arg->getValue());
+ config->dynamicList.push_back(
+ {arg->getValue(), /*isExternCpp=*/false,
+ /*hasWildcard=*/hasWildcard(arg->getValue())});
for (auto *arg : args.filtered(OPT_version_script))
if (Optional<std::string> path = searchScript(arg->getValue())) {
// ELF defines two different ways to store relocation addends as shown below:
//
- // Rel: Addends are stored to the location where relocations are applied.
+ // Rel: Addends are stored to the location where relocations are applied. It
+ // cannot pack the full range of addend values for all relocation types, but
+ // this only affects relocation types that we don't support emitting as
+ // dynamic relocations (see getDynRel).
// Rela: Addends are stored as part of relocation entry.
//
// In other words, Rela makes it easy to read addends at the price of extra
- // 4 or 8 byte for each relocation entry. We don't know why ELF defined two
- // different mechanisms in the first place, but this is how the spec is
- // defined.
+ // 4 or 8 byte for each relocation entry.
//
- // You cannot choose which one, Rel or Rela, you want to use. Instead each
- // ABI defines which one you need to use. The following expression expresses
- // that.
- config->isRela = m == EM_AARCH64 || m == EM_AMDGPU || m == EM_HEXAGON ||
- m == EM_PPC || m == EM_PPC64 || m == EM_RISCV ||
- m == EM_X86_64;
+ // We pick the format for dynamic relocations according to the psABI for each
+ // processor, but a contrary choice can be made if the dynamic loader
+ // supports.
+ config->isRela = getIsRela(args);
// If the output uses REL relocations we must store the dynamic relocation
// addends to the output sections. We also store addends for RELA relocations
}
// Parse -z max-page-size=<value>. The default value is defined by
-// each target. Is set to 1 if given nmagic or omagic.
+// each target.
static uint64_t getMaxPageSize(opt::InputArgList &args) {
uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size",
target->defaultMaxPageSize);
}
// Parse -z common-page-size=<value>. The default value is defined by
-// each target. Is set to 1 if given nmagic or omagic.
+// each target.
static uint64_t getCommonPageSize(opt::InputArgList &args) {
uint64_t val = args::getZOptionValue(args, OPT_z, "common-page-size",
target->defaultCommonPageSize);
return val;
}
-// Parse -z max-page-size=<value>. The default value is defined by
-// each target.
-static uint64_t getRealMaxPageSize(opt::InputArgList &args) {
- uint64_t val = args::getZOptionValue(args, OPT_z, "max-page-size",
- target->defaultMaxPageSize);
- if (!isPowerOf2_64(val))
- error("max-page-size: value isn't a power of 2");
- return val;
-}
-
// Parses -image-base option.
static Optional<uint64_t> getImageBase(opt::InputArgList &args) {
// Because we are using "Config->maxPageSize" here, this function has to be
visit(file);
}
-// Force Sym to be entered in the output. Used for -u or equivalent.
+// Force Sym to be entered in the output.
static void handleUndefined(Symbol *sym) {
// Since a symbol may not be used inside the program, LTO may
// eliminate it. Mark the symbol as "used" to prevent it.
Undefined{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0});
}
+static Symbol *addUnusedUndefined(StringRef name) {
+ Undefined sym{nullptr, name, STB_GLOBAL, STV_DEFAULT, 0};
+ sym.isUsedInRegularObj = false;
+ return symtab->addSymbol(sym);
+}
+
// This function is where all the optimizations of link-time
// optimization takes place. When LTO is in use, some input files are
// not in native object file format but in the LLVM bitcode format.
// Because all bitcode files that the program consists of are passed to
// the compiler at once, it can do a whole-program optimization.
template <class ELFT> void LinkerDriver::compileBitcodeFiles() {
+ llvm::TimeTraceScope timeScope("LTO");
// Compile bitcode files and replace bitcode symbols.
lto.reset(new BitcodeCompiler);
for (BitcodeFile *file : bitcodeFiles)
for (InputFile *file : lto->compile()) {
auto *obj = cast<ObjFile<ELFT>>(file);
obj->parse(/*ignoreComdats=*/true);
- for (Symbol *sym : obj->getGlobalSymbols())
- sym->parseSymbolVersion();
+
+ // Parse '@' in symbol names for non-relocatable output.
+ if (!config->relocatable)
+ for (Symbol *sym : obj->getGlobalSymbols())
+ sym->parseSymbolVersion();
objectFiles.push_back(file);
}
}
uint32_t ret = -1;
for (InputFile *f : objectFiles) {
uint32_t features = cast<ObjFile<ELFT>>(f)->andFeatures;
- if (config->forceBTI && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
- warn(toString(f) + ": -z force-bti: file does not have BTI property");
+ if (config->zForceBti && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)) {
+ warn(toString(f) + ": -z force-bti: file does not have "
+ "GNU_PROPERTY_AARCH64_FEATURE_1_BTI property");
features |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
} else if (config->zForceIbt &&
!(features & GNU_PROPERTY_X86_FEATURE_1_IBT)) {
"GNU_PROPERTY_X86_FEATURE_1_IBT property");
features |= GNU_PROPERTY_X86_FEATURE_1_IBT;
}
+ if (config->zPacPlt && !(features & GNU_PROPERTY_AARCH64_FEATURE_1_PAC)) {
+ warn(toString(f) + ": -z pac-plt: file does not have "
+ "GNU_PROPERTY_AARCH64_FEATURE_1_PAC property");
+ features |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
+ }
ret &= features;
}
- // Force enable pointer authentication Plt, we don't warn in this case as
- // this does not require support in the object for correctness.
- if (config->pacPlt)
- ret |= GNU_PROPERTY_AARCH64_FEATURE_1_PAC;
// Force enable Shadow Stack.
if (config->zShstk)
ret |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
// Do actual linking. Note that when this function is called,
// all linker scripts have already been parsed.
template <class ELFT> void LinkerDriver::link(opt::InputArgList &args) {
+ llvm::TimeTraceScope timeScope("Link", StringRef("LinkerDriver::Link"));
// If a -hash-style option was not given, set to a default value,
// which varies depending on the target.
if (!args.hasArg(OPT_hash_style)) {
for (auto *arg : args.filtered(OPT_trace_symbol))
symtab->insert(arg->getValue())->traced = true;
+ // Handle -u/--undefined before input files. If both a.a and b.so define foo,
+ // -u foo a.a b.so will fetch a.a.
+ for (StringRef name : config->undefined)
+ addUnusedUndefined(name);
+
// Add all files to the symbol table. This will add almost all
// symbols that we need to the symbol table. This process might
// add files to the link, via autolinking, these files are always
// appended to the Files vector.
- for (size_t i = 0; i < files.size(); ++i)
- parseFile(files[i]);
+ {
+ llvm::TimeTraceScope timeScope("Parse input files");
+ for (size_t i = 0; i < files.size(); ++i)
+ parseFile(files[i]);
+ }
// Now that we have every file, we can decide if we will need a
// dynamic symbol table.
for (StringRef name : script->referencedSymbols)
addUndefined(name);
- // Handle the `--undefined <sym>` options.
- for (StringRef arg : config->undefined)
- if (Symbol *sym = symtab->find(arg))
- handleUndefined(sym);
+ // Prevent LTO from removing any definition referenced by -u.
+ for (StringRef name : config->undefined)
+ if (Defined *sym = dyn_cast_or_null<Defined>(symtab->find(name)))
+ sym->isUsedInRegularObj = true;
// If an entry symbol is in a static archive, pull out that file now.
if (Symbol *sym = symtab->find(config->entry))
handleUndefinedGlob(pat);
// Mark -init and -fini symbols so that the LTO doesn't eliminate them.
- if (Symbol *sym = symtab->find(config->init))
+ if (Symbol *sym = dyn_cast_or_null<Defined>(symtab->find(config->init)))
sym->isUsedInRegularObj = true;
- if (Symbol *sym = symtab->find(config->fini))
+ if (Symbol *sym = dyn_cast_or_null<Defined>(symtab->find(config->fini)))
sym->isUsedInRegularObj = true;
// If any of our inputs are bitcode files, the LTO code generator may create
if (errorCount())
return;
- // Now when we read all script files, we want to finalize order of linker
- // script commands, which can be not yet final because of INSERT commands.
- script->processInsertCommands();
-
// We want to declare linker script's symbols early,
// so that we can version them.
// They also might be exported if referenced by DSOs.
// With this the symbol table should be complete. After this, no new names
// except a few linker-synthesized ones will be added to the symbol table.
compileBitcodeFiles<ELFT>();
+
+ // Symbol resolution finished. Report backward reference problems.
+ reportBackrefs();
if (errorCount())
return;
// If -thinlto-index-only is given, we should create only "index
// files" and not object files. Index file creation is already done
// in addCombinedLTOObject, so we are done if that's the case.
- if (config->thinLTOIndexOnly)
- return;
-
- // Likewise, --plugin-opt=emit-llvm is an option to make LTO create
- // an output file in bitcode and exit, so that you can just get a
- // combined bitcode file.
- if (config->emitLLVM)
+ // Likewise, --plugin-opt=emit-llvm and --plugin-opt=emit-asm are the
+ // options to create output files in bitcode or assembly code
+ // repsectively. No object files are generated.
+ // Also bail out here when only certain thinLTO modules are specified for
+ // compilation. The intermediate object file are the expected output.
+ if (config->thinLTOIndexOnly || config->emitLLVM || config->ltoEmitAsm ||
+ !config->thinLTOModulesToCompile.empty())
return;
// Apply symbol renames for -wrap.
// optimizations such as DATA_SEGMENT_ALIGN in linker scripts. LLD's use of it
// is limited to writing trap instructions on the last executable segment.
config->commonPageSize = getCommonPageSize(args);
- // textAlignPageSize is the alignment page size to use when aligning PT_LOAD
- // sections. This is the same as maxPageSize except under -omagic, where data
- // sections are non-aligned (maxPageSize set to 1) but text sections are aligned
- // to the target page size.
- config->textAlignPageSize = config->omagic ? getRealMaxPageSize(args) : config->maxPageSize;
config->imageBase = getImageBase(args);
// Write the result to the file.
writeResult<ELFT>();
}
-
-} // namespace elf
-} // namespace lld
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
using namespace llvm;
using namespace llvm::sys;
using namespace llvm::opt;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// Create OptTable
return cl::TokenizeWindowsCommandLine;
return cl::TokenizeGNUCommandLine;
}
- if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32)
+ if (Triple(sys::getProcessTriple()).isOSWindows())
return cl::TokenizeWindowsCommandLine;
return cl::TokenizeGNUCommandLine;
}
return args;
}
-void printHelp() {
+void elf::printHelp() {
ELFOptTable().PrintHelp(
lld::outs(), (config->progName + " [options] file...").str().c_str(),
"lld", false /*ShowHidden*/, true /*ShowAllAliases*/);
static std::string rewritePath(StringRef s) {
if (fs::exists(s))
return relativeToRoot(s);
- return s;
+ return std::string(s);
}
// Reconstructs command line arguments so that so that you can re-run
// the same command with the same inputs. This is for --reproduce.
-std::string createResponseFile(const opt::InputArgList &args) {
+std::string elf::createResponseFile(const opt::InputArgList &args) {
SmallString<0> data;
raw_svector_ostream os(data);
os << "--chroot .\n";
os << toString(*arg) << "\n";
}
}
- return data.str();
+ return std::string(data.str());
}
// Find a file by concatenating given paths. If a resulting path
path::append(s, path1, path2);
if (fs::exists(s))
- return s.str().str();
+ return std::string(s);
return None;
}
-Optional<std::string> findFromSearchPaths(StringRef path) {
+Optional<std::string> elf::findFromSearchPaths(StringRef path) {
for (StringRef dir : config->searchPaths)
if (Optional<std::string> s = findFile(dir, path))
return s;
// This is for -l<basename>. We'll look for lib<basename>.so or lib<basename>.a from
// search paths.
-Optional<std::string> searchLibraryBaseName(StringRef name) {
+Optional<std::string> elf::searchLibraryBaseName(StringRef name) {
for (StringRef dir : config->searchPaths) {
- if (!config->isStatic) {
+ if (!config->isStatic)
if (Optional<std::string> s = findFile(dir, "lib" + name + ".so"))
return s;
-
- // Handle OpenBSD-style maj/min shlib scheme
- llvm::SmallString<128> Scratch;
- const StringRef LibName = ("lib" + name + ".so.").toStringRef(Scratch);
- int MaxMaj = -1, MaxMin = -1;
- std::error_code EC;
- for (fs::directory_iterator LI(dir, EC), LE;
- LI != LE; LI = LI.increment(EC)) {
- StringRef FilePath = LI->path();
- StringRef FileName = path::filename(FilePath);
- if (!(FileName.startswith(LibName)))
- continue;
- std::pair<StringRef, StringRef> MajMin =
- FileName.substr(LibName.size()).split('.');
- int Maj, Min;
- if (MajMin.first.getAsInteger(10, Maj) || Maj < 0)
- continue;
- if (MajMin.second.getAsInteger(10, Min) || Min < 0)
- continue;
- if (Maj > MaxMaj)
- MaxMaj = Maj, MaxMin = Min;
- if (MaxMaj == Maj && Min > MaxMin)
- MaxMin = Min;
- }
- if (MaxMaj >= 0)
- return findFile(dir, LibName + Twine(MaxMaj) + "." + Twine(MaxMin));
- }
if (Optional<std::string> s = findFile(dir, "lib" + name + ".a"))
return s;
}
}
// This is for -l<namespec>.
-Optional<std::string> searchLibrary(StringRef name) {
+Optional<std::string> elf::searchLibrary(StringRef name) {
if (name.startswith(":"))
return findFromSearchPaths(name.substr(1));
return searchLibraryBaseName(name);
// If a linker/version script doesn't exist in the current directory, we also
// look for the script in the '-L' search paths. This matches the behaviour of
// '-T', --version-script=, and linker script INPUT() command in ld.bfd.
-Optional<std::string> searchScript(StringRef name) {
+Optional<std::string> elf::searchScript(StringRef name) {
if (fs::exists(name))
return name.str();
return findFromSearchPaths(name);
}
-
-} // namespace elf
-} // namespace lld
using namespace llvm::ELF;
using namespace llvm::dwarf;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
class EhReader {
public:
};
}
-size_t readEhRecordSize(InputSectionBase *s, size_t off) {
+size_t elf::readEhRecordSize(InputSectionBase *s, size_t off) {
return EhReader(s, s->data().slice(off)).readEhRecordSize();
}
d = d.slice(size);
}
-uint8_t getFdeEncoding(EhSectionPiece *p) {
+uint8_t elf::getFdeEncoding(EhSectionPiece *p) {
return EhReader(p->sec, p->data()).getFdeEncoding();
}
}
return DW_EH_PE_absptr;
}
-
-} // namespace elf
-} // namespace lld
#include "Symbols.h"
#include "SyntheticSections.h"
#include "Writer.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Object/ELF.h"
+#include "llvm/Support/Parallel.h"
+#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/xxhash.h"
#include <algorithm>
#include <atomic>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
template <class ELFT> class ICF {
public:
void ICF<ELFT>::forEachClass(llvm::function_ref<void(size_t, size_t)> fn) {
// If threading is disabled or the number of sections are
// too small to use threading, call Fn sequentially.
- if (!threadsEnabled || sections.size() < 1024) {
+ if (parallel::strategy.ThreadsRequested == 1 || sections.size() < 1024) {
forEachClassRange(0, sections.size(), fn);
++cnt;
return;
}
// Initially, we use hash values to partition sections.
- parallelForEach(sections, [&](InputSection *s) {
- s->eqClass[0] = xxHash64(s->data());
- });
+ parallelForEach(
+ sections, [&](InputSection *s) { s->eqClass[0] = xxHash64(s->data()); });
for (unsigned cnt = 0; cnt != 2; ++cnt) {
parallelForEach(sections, [&](InputSection *s) {
}
// ICF entry point function.
-template <class ELFT> void doIcf() { ICF<ELFT>().run(); }
-
-template void doIcf<ELF32LE>();
-template void doIcf<ELF32BE>();
-template void doIcf<ELF64LE>();
-template void doIcf<ELF64BE>();
+template <class ELFT> void elf::doIcf() {
+ llvm::TimeTraceScope timeScope("ICF");
+ ICF<ELFT>().run();
+}
-} // namespace elf
-} // namespace lld
+template void elf::doIcf<ELF32LE>();
+template void elf::doIcf<ELF32BE>();
+template void elf::doIcf<ELF64LE>();
+template void elf::doIcf<ELF64BE>();
using namespace llvm::sys;
using namespace llvm::sys::fs;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
+
+bool InputFile::isInGroup;
+uint32_t InputFile::nextGroupId;
+
+std::vector<ArchiveFile *> elf::archiveFiles;
+std::vector<BinaryFile *> elf::binaryFiles;
+std::vector<BitcodeFile *> elf::bitcodeFiles;
+std::vector<LazyObjFile *> elf::lazyObjFiles;
+std::vector<InputFile *> elf::objectFiles;
+std::vector<SharedFile *> elf::sharedFiles;
+
+std::unique_ptr<TarWriter> elf::tar;
-namespace lld {
// Returns "<internal>", "foo.a(bar.o)" or "baz.o".
-std::string toString(const elf::InputFile *f) {
+std::string lld::toString(const InputFile *f) {
if (!f)
return "<internal>";
if (f->toStringCache.empty()) {
if (f->archiveName.empty())
- f->toStringCache = f->getName();
+ f->toStringCache = std::string(f->getName());
else
f->toStringCache = (f->archiveName + "(" + f->getName() + ")").str();
}
return f->toStringCache;
}
-namespace elf {
-bool InputFile::isInGroup;
-uint32_t InputFile::nextGroupId;
-std::vector<BinaryFile *> binaryFiles;
-std::vector<BitcodeFile *> bitcodeFiles;
-std::vector<LazyObjFile *> lazyObjFiles;
-std::vector<InputFile *> objectFiles;
-std::vector<SharedFile *> sharedFiles;
-
-std::unique_ptr<TarWriter> tar;
-
static ELFKind getELFKind(MemoryBufferRef mb, StringRef archiveName) {
unsigned char size;
unsigned char endian;
++nextGroupId;
}
-Optional<MemoryBufferRef> readFile(StringRef path) {
+Optional<MemoryBufferRef> elf::readFile(StringRef path) {
// The --chroot option changes our virtual root directory.
// This is useful when you are dealing with files created by --reproduce.
if (!config->chroot.empty() && path.startswith("/"))
return true;
}
- if (!config->emulation.empty()) {
- error(toString(file) + " is incompatible with " + config->emulation);
+ StringRef target =
+ !config->bfdname.empty() ? config->bfdname : config->emulation;
+ if (!target.empty()) {
+ error(toString(file) + " is incompatible with " + target);
return false;
}
existing = objectFiles[0];
else if (!sharedFiles.empty())
existing = sharedFiles[0];
- else
+ else if (!bitcodeFiles.empty())
existing = bitcodeFiles[0];
+ else
+ llvm_unreachable("Must have -m, OUTPUT_FORMAT or existing input file to "
+ "determine target emulation");
error(toString(file) + " is incompatible with " + toString(existing));
return false;
// .a file
if (auto *f = dyn_cast<ArchiveFile>(file)) {
+ archiveFiles.push_back(f);
f->parse();
return;
}
}
// Add symbols in File to the symbol table.
-void parseFile(InputFile *file) {
+void elf::parseFile(InputFile *file) {
switch (config->ekind) {
case ELF32LEKind:
doParseFile<ELF32LE>(file);
// Concatenates arguments to construct a string representing an error location.
static std::string createFileLineMsg(StringRef path, unsigned line) {
- std::string filename = path::filename(path);
+ std::string filename = std::string(path::filename(path));
std::string lineno = ":" + std::to_string(line);
if (filename == path)
return filename + lineno;
return createFileLineMsg(fileLine->first, fileLine->second);
// File.sourceFile contains STT_FILE symbol, and that is a last resort.
- return file.sourceFile;
+ return std::string(file.sourceFile);
}
std::string InputFile::getSrcMsg(const Symbol &sym, InputSectionBase &sec,
}
}
-template <class ELFT> void ObjFile<ELFT>::initializeDwarf() {
- dwarf = make<DWARFCache>(std::make_unique<DWARFContext>(
- std::make_unique<LLDDwarfObj<ELFT>>(this)));
+template <class ELFT> DWARFCache *ObjFile<ELFT>::getDwarf() {
+ llvm::call_once(initDwarf, [this]() {
+ dwarf = std::make_unique<DWARFCache>(std::make_unique<DWARFContext>(
+ std::make_unique<LLDDwarfObj<ELFT>>(this), "",
+ [&](Error err) { warn(getName() + ": " + toString(std::move(err))); },
+ [&](Error warning) {
+ warn(getName() + ": " + toString(std::move(warning)));
+ }));
+ });
+
+ return dwarf.get();
}
// Returns the pair of file name and line number describing location of data
template <class ELFT>
Optional<std::pair<std::string, unsigned>>
ObjFile<ELFT>::getVariableLoc(StringRef name) {
- llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
-
- return dwarf->getVariableLoc(name);
+ return getDwarf()->getVariableLoc(name);
}
// Returns source line information for a given offset
template <class ELFT>
Optional<DILineInfo> ObjFile<ELFT>::getDILineInfo(InputSectionBase *s,
uint64_t offset) {
- llvm::call_once(initDwarfLine, [this]() { initializeDwarf(); });
-
// Detect SectionIndex for specified section.
uint64_t sectionIndex = object::SectionedAddress::UndefSection;
ArrayRef<InputSectionBase *> sections = s->file->getSections();
}
}
- // Use fake address calculated by adding section file offset and offset in
- // section. See comments for ObjectInfo class.
- return dwarf->getDILineInfo(s->getOffsetInFile() + offset, sectionIndex);
+ return getDwarf()->getDILineInfo(offset, sectionIndex);
}
ELFFileBase::ELFFileBase(Kind k, MemoryBufferRef mb) : InputFile(k, mb) {
template <class ELFT>
bool ObjFile<ELFT>::shouldMerge(const Elf_Shdr &sec, StringRef name) {
+ if (!(sec.sh_flags & SHF_MERGE))
+ return false;
+
// On a regular link we don't merge sections if -O0 (default is -O1). This
// sometimes makes the linker significantly faster, although the output will
// be bigger.
Twine(sec.sh_size) + ") must be a multiple of sh_entsize (" +
Twine(entSize) + ")");
- uint64_t flags = sec.sh_flags;
- if (!(flags & SHF_MERGE))
- return false;
- if (flags & SHF_WRITE)
+ if (sec.sh_flags & SHF_WRITE)
fatal(toString(this) + ":(" + name +
"): writable SHF_MERGE section is not supported");
break;
case SHT_SYMTAB:
case SHT_STRTAB:
+ case SHT_REL:
+ case SHT_RELA:
case SHT_NULL:
break;
default:
}
}
- // This block handles SHF_LINK_ORDER.
+ // We have a second loop. It is used to:
+ // 1) handle SHF_LINK_ORDER sections.
+ // 2) create SHT_REL[A] sections. In some cases the section header index of a
+ // relocation section may be smaller than that of the relocated section. In
+ // such cases, the relocation section would attempt to reference a target
+ // section that has not yet been created. For simplicity, delay creation of
+ // relocation sections until now.
for (size_t i = 0, e = objSections.size(); i < e; ++i) {
if (this->sections[i] == &InputSection::discarded)
continue;
const Elf_Shdr &sec = objSections[i];
+
+ if (sec.sh_type == SHT_REL || sec.sh_type == SHT_RELA)
+ this->sections[i] = createInputSection(sec);
+
if (!(sec.sh_flags & SHF_LINK_ORDER))
continue;
// the input objects have been compiled.
static void updateARMVFPArgs(const ARMAttributeParser &attributes,
const InputFile *f) {
- if (!attributes.hasAttribute(ARMBuildAttrs::ABI_VFP_args))
+ Optional<unsigned> attr =
+ attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
+ if (!attr.hasValue())
// If an ABI tag isn't present then it is implicitly given the value of 0
// which maps to ARMBuildAttrs::BaseAAPCS. However many assembler files,
// including some in glibc that don't use FP args (and should have value 3)
// as a clash.
return;
- unsigned vfpArgs = attributes.getAttributeValue(ARMBuildAttrs::ABI_VFP_args);
+ unsigned vfpArgs = attr.getValue();
ARMVFPArgKind arg;
switch (vfpArgs) {
case ARMBuildAttrs::BaseAAPCS:
// is compiled with an architecture that supports these features then lld is
// permitted to use them.
static void updateSupportedARMFeatures(const ARMAttributeParser &attributes) {
- if (!attributes.hasAttribute(ARMBuildAttrs::CPU_arch))
+ Optional<unsigned> attr =
+ attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
+ if (!attr.hasValue())
return;
- auto arch = attributes.getAttributeValue(ARMBuildAttrs::CPU_arch);
+ auto arch = attr.getValue();
switch (arch) {
case ARMBuildAttrs::Pre_v4:
case ARMBuildAttrs::v4:
break;
ARMAttributeParser attributes;
ArrayRef<uint8_t> contents = check(this->getObj().getSectionContents(&sec));
- attributes.Parse(contents, /*isLittle*/ config->ekind == ELF32LEKind);
+ if (Error e = attributes.parse(contents, config->ekind == ELF32LEKind
+ ? support::little
+ : support::big)) {
+ auto *isec = make<InputSection>(*this, sec, name);
+ warn(toString(isec) + ": " + llvm::toString(std::move(e)));
+ break;
+ }
updateSupportedARMFeatures(attributes);
updateARMVFPArgs(attributes, this);
ArrayRef<Elf_Sym> eSyms = this->getELFSyms<ELFT>();
this->symbols.resize(eSyms.size());
- // Our symbol table may have already been partially initialized
+ // Fill in InputFile::symbols. Some entries have been initialized
// because of LazyObjFile.
- for (size_t i = 0, end = eSyms.size(); i != end; ++i)
- if (!this->symbols[i] && eSyms[i].getBinding() != STB_LOCAL)
- this->symbols[i] =
- symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this));
-
- // Fill this->Symbols. A symbol is either local or global.
for (size_t i = 0, end = eSyms.size(); i != end; ++i) {
+ if (this->symbols[i])
+ continue;
const Elf_Sym &eSym = eSyms[i];
-
- // Read symbol attributes.
uint32_t secIdx = getSectionIndex(eSym);
if (secIdx >= this->sections.size())
fatal(toString(this) + ": invalid section index: " + Twine(secIdx));
+ if (eSym.getBinding() != STB_LOCAL) {
+ if (i < firstGlobal)
+ error(toString(this) + ": non-local symbol (" + Twine(i) +
+ ") found at index < .symtab's sh_info (" + Twine(firstGlobal) +
+ ")");
+ this->symbols[i] =
+ symtab->insert(CHECK(eSyms[i].getName(this->stringTable), this));
+ continue;
+ }
+
+ // Handle local symbols. Local symbols are not added to the symbol
+ // table because they are not visible from other object files. We
+ // allocate symbol instances and add their pointers to symbols.
+ if (i >= firstGlobal)
+ errorOrWarn(toString(this) + ": STB_LOCAL symbol (" + Twine(i) +
+ ") found at index >= .symtab's sh_info (" +
+ Twine(firstGlobal) + ")");
InputSectionBase *sec = this->sections[secIdx];
+ uint8_t type = eSym.getType();
+ if (type == STT_FILE)
+ sourceFile = CHECK(eSym.getName(this->stringTable), this);
+ if (this->stringTable.size() <= eSym.st_name)
+ fatal(toString(this) + ": invalid symbol name offset");
+ StringRefZ name = this->stringTable.data() + eSym.st_name;
+
+ if (eSym.st_shndx == SHN_UNDEF)
+ this->symbols[i] =
+ make<Undefined>(this, name, STB_LOCAL, eSym.st_other, type);
+ else if (sec == &InputSection::discarded)
+ this->symbols[i] =
+ make<Undefined>(this, name, STB_LOCAL, eSym.st_other, type,
+ /*discardedSecIdx=*/secIdx);
+ else
+ this->symbols[i] = make<Defined>(this, name, STB_LOCAL, eSym.st_other,
+ type, eSym.st_value, eSym.st_size, sec);
+ }
+
+ // Symbol resolution of non-local symbols.
+ for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) {
+ const Elf_Sym &eSym = eSyms[i];
uint8_t binding = eSym.getBinding();
+ if (binding == STB_LOCAL)
+ continue; // Errored above.
+
+ uint32_t secIdx = getSectionIndex(eSym);
+ InputSectionBase *sec = this->sections[secIdx];
uint8_t stOther = eSym.st_other;
uint8_t type = eSym.getType();
uint64_t value = eSym.st_value;
uint64_t size = eSym.st_size;
StringRefZ name = this->stringTable.data() + eSym.st_name;
- // Handle local symbols. Local symbols are not added to the symbol
- // table because they are not visible from other object files. We
- // allocate symbol instances and add their pointers to Symbols.
- if (binding == STB_LOCAL) {
- if (eSym.getType() == STT_FILE)
- sourceFile = CHECK(eSym.getName(this->stringTable), this);
-
- if (this->stringTable.size() <= eSym.st_name)
- fatal(toString(this) + ": invalid symbol name offset");
-
- if (eSym.st_shndx == SHN_UNDEF)
- this->symbols[i] = make<Undefined>(this, name, binding, stOther, type);
- else if (sec == &InputSection::discarded)
- this->symbols[i] = make<Undefined>(this, name, binding, stOther, type,
- /*DiscardedSecIdx=*/secIdx);
- else
- this->symbols[i] =
- make<Defined>(this, name, binding, stOther, type, value, size, sec);
- continue;
- }
-
// Handle global undefined symbols.
if (eSym.st_shndx == SHN_UNDEF) {
this->symbols[i]->resolve(Undefined{this, name, binding, stOther, type});
// COMDAT member sections, and if a comdat group is discarded, some
// defined symbol in a .eh_frame becomes dangling symbols.
if (sec == &InputSection::discarded) {
- this->symbols[i]->resolve(
- Undefined{this, name, binding, stOther, type, secIdx});
+ Undefined und{this, name, binding, stOther, type, secIdx};
+ Symbol *sym = this->symbols[i];
+ // !ArchiveFile::parsed or LazyObjFile::fetched means that the file
+ // containing this object has not finished processing, i.e. this symbol is
+ // a result of a lazy symbol fetch. We should demote the lazy symbol to an
+ // Undefined so that any relocations outside of the group to it will
+ // trigger a discarded section error.
+ if ((sym->symbolKind == Symbol::LazyArchiveKind &&
+ !cast<ArchiveFile>(sym->file)->parsed) ||
+ (sym->symbolKind == Symbol::LazyObjectKind &&
+ cast<LazyObjFile>(sym->file)->fetched))
+ sym->replace(und);
+ else
+ sym->resolve(und);
continue;
}
void ArchiveFile::parse() {
for (const Archive::Symbol &sym : file->symbols())
symtab->addSymbol(LazyArchive{*this, sym});
+
+ // Inform a future invocation of ObjFile<ELFT>::initializeSymbols() that this
+ // archive has been processed.
+ parsed = true;
}
// Returns a buffer pointing to a member file containing a given symbol.
if (tar && c.getParent()->isThin())
tar->append(relativeToRoot(CHECK(c.getFullName(), this)), mb.getBuffer());
- InputFile *file = createObjectFile(
- mb, getName(), c.getParent()->isThin() ? 0 : c.getChildOffset());
+ InputFile *file = createObjectFile(mb, getName(), c.getChildOffset());
file->groupId = groupId;
parseFile(file);
}
+size_t ArchiveFile::getMemberCount() const {
+ size_t count = 0;
+ Error err = Error::success();
+ for (const Archive::Child &c : file->children(err)) {
+ (void)c;
+ ++count;
+ }
+ // This function is used by --print-archive-stats=, where an error does not
+ // really matter.
+ consumeError(std::move(err));
+ return count;
+}
+
unsigned SharedFile::vernauxNum;
// Parse the version definitions in the object file if present, and return a
return verdefs;
}
+// Parse SHT_GNU_verneed to properly set the name of a versioned undefined
+// symbol. We detect fatal issues which would cause vulnerabilities, but do not
+// implement sophisticated error checking like in llvm-readobj because the value
+// of such diagnostics is low.
+template <typename ELFT>
+std::vector<uint32_t> SharedFile::parseVerneed(const ELFFile<ELFT> &obj,
+ const typename ELFT::Shdr *sec) {
+ if (!sec)
+ return {};
+ std::vector<uint32_t> verneeds;
+ ArrayRef<uint8_t> data = CHECK(obj.getSectionContents(sec), this);
+ const uint8_t *verneedBuf = data.begin();
+ for (unsigned i = 0; i != sec->sh_info; ++i) {
+ if (verneedBuf + sizeof(typename ELFT::Verneed) > data.end())
+ fatal(toString(this) + " has an invalid Verneed");
+ auto *vn = reinterpret_cast<const typename ELFT::Verneed *>(verneedBuf);
+ const uint8_t *vernauxBuf = verneedBuf + vn->vn_aux;
+ for (unsigned j = 0; j != vn->vn_cnt; ++j) {
+ if (vernauxBuf + sizeof(typename ELFT::Vernaux) > data.end())
+ fatal(toString(this) + " has an invalid Vernaux");
+ auto *aux = reinterpret_cast<const typename ELFT::Vernaux *>(vernauxBuf);
+ if (aux->vna_name >= this->stringTable.size())
+ fatal(toString(this) + " has a Vernaux with an invalid vna_name");
+ uint16_t version = aux->vna_other & VERSYM_VERSION;
+ if (version >= verneeds.size())
+ verneeds.resize(version + 1);
+ verneeds[version] = aux->vna_name;
+ vernauxBuf += aux->vna_next;
+ }
+ verneedBuf += vn->vn_next;
+ }
+ return verneeds;
+}
+
// We do not usually care about alignments of data in shared object
// files because the loader takes care of it. However, if we promote a
// DSO symbol to point to .bss due to copy relocation, we need to keep
const Elf_Shdr *versymSec = nullptr;
const Elf_Shdr *verdefSec = nullptr;
+ const Elf_Shdr *verneedSec = nullptr;
// Search for .dynsym, .dynamic, .symtab, .gnu.version and .gnu.version_d.
for (const Elf_Shdr &sec : sections) {
case SHT_GNU_verdef:
verdefSec = &sec;
break;
+ case SHT_GNU_verneed:
+ verneedSec = &sec;
+ break;
}
}
sharedFiles.push_back(this);
verdefs = parseVerdefs<ELFT>(obj.base(), verdefSec);
+ std::vector<uint32_t> verneeds = parseVerneed<ELFT>(obj, verneedSec);
// Parse ".gnu.version" section which is a parallel array for the symbol
// table. If a given file doesn't have a ".gnu.version" section, we use
// VER_NDX_GLOBAL.
size_t size = numELFSyms - firstGlobal;
- std::vector<uint32_t> versyms(size, VER_NDX_GLOBAL);
+ std::vector<uint16_t> versyms(size, VER_NDX_GLOBAL);
if (versymSec) {
ArrayRef<Elf_Versym> versym =
CHECK(obj.template getSectionContentsAsArray<Elf_Versym>(versymSec),
continue;
}
+ uint16_t idx = versyms[i] & ~VERSYM_HIDDEN;
if (sym.isUndefined()) {
+ // For unversioned undefined symbols, VER_NDX_GLOBAL makes more sense but
+ // as of binutils 2.34, GNU ld produces VER_NDX_LOCAL.
+ if (idx != VER_NDX_LOCAL && idx != VER_NDX_GLOBAL) {
+ if (idx >= verneeds.size()) {
+ error("corrupt input file: version need index " + Twine(idx) +
+ " for symbol " + name + " is out of bounds\n>>> defined in " +
+ toString(this));
+ continue;
+ }
+ StringRef verName = this->stringTable.data() + verneeds[idx];
+ versionedNameBuffer.clear();
+ name =
+ saver.save((name + "@" + verName).toStringRef(versionedNameBuffer));
+ }
Symbol *s = symtab->addSymbol(
Undefined{this, name, sym.getBinding(), sym.st_other, sym.getType()});
s->exportDynamic = true;
// MIPS BFD linker puts _gp_disp symbol into DSO files and incorrectly
// assigns VER_NDX_LOCAL to this section global symbol. Here is a
// workaround for this bug.
- uint32_t idx = versyms[i] & ~VERSYM_HIDDEN;
if (config->emachine == EM_MIPS && idx == VER_NDX_LOCAL &&
name == "_gp_disp")
continue;
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,
uint64_t offsetInArchive)
: InputFile(BitcodeKind, mb) {
- this->archiveName = archiveName;
+ this->archiveName = std::string(archiveName);
std::string path = mb.getBufferIdentifier().str();
if (config->thinLTOIndexOnly)
// into consideration at LTO time (which very likely causes undefined
// symbols later in the link stage). So we append file offset to make
// filename unique.
- StringRef name = archiveName.empty()
- ? saver.save(path)
- : saver.save(archiveName + "(" + path + " at " +
- utostr(offsetInArchive) + ")");
+ StringRef name =
+ archiveName.empty()
+ ? saver.save(path)
+ : saver.save(archiveName + "(" + path::filename(path) + " at " +
+ utostr(offsetInArchive) + ")");
MemoryBufferRef mbref(mb.getBuffer(), name);
obj = CHECK(lto::InputFile::create(mbref), this);
STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr});
}
-InputFile *createObjectFile(MemoryBufferRef mb, StringRef archiveName,
- uint64_t offsetInArchive) {
+InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName,
+ uint64_t offsetInArchive) {
if (isBitcode(mb))
return make<BitcodeFile>(mb, archiveName, offsetInArchive);
}
void LazyObjFile::fetch() {
- if (mb.getBuffer().empty())
+ if (fetched)
return;
+ fetched = true;
InputFile *file = createObjectFile(mb, archiveName, offsetInArchive);
file->groupId = groupId;
- mb = {};
-
// Copy symbol vector so that the new InputFile doesn't have to
// insert the same defined symbols to the symbol table again.
file->symbols = std::move(symbols);
continue;
sym->resolve(LazyObject{*this, sym->getName()});
- // MemoryBuffer is emptied if this file is instantiated as ObjFile.
- if (mb.getBuffer().empty())
+ // If fetched, stop iterating because this->symbols has been transferred
+ // to the instantiated ObjFile.
+ if (fetched)
return;
}
return;
}
}
-std::string replaceThinLTOSuffix(StringRef path) {
+std::string elf::replaceThinLTOSuffix(StringRef path) {
StringRef suffix = config->thinLTOObjectSuffixReplace.first;
StringRef repl = config->thinLTOObjectSuffixReplace.second;
if (path.consume_back(suffix))
return (path + repl).str();
- return path;
+ return std::string(path);
}
template void BitcodeFile::parse<ELF32LE>();
template void LazyObjFile::parse<ELF64LE>();
template void LazyObjFile::parse<ELF64BE>();
-template class ObjFile<ELF32LE>;
-template class ObjFile<ELF32BE>;
-template class ObjFile<ELF64LE>;
-template class ObjFile<ELF64BE>;
+template class elf::ObjFile<ELF32LE>;
+template class elf::ObjFile<ELF32BE>;
+template class elf::ObjFile<ELF64LE>;
+template class elf::ObjFile<ELF64BE>;
template void SharedFile::parse<ELF32LE>();
template void SharedFile::parse<ELF32BE>();
template void SharedFile::parse<ELF64LE>();
template void SharedFile::parse<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
std::string toString(const elf::InputFile *f);
namespace elf {
-class InputFile;
-class InputSectionBase;
using llvm::object::Archive;
ArrayRef<Symbol *> getGlobalSymbols();
ObjFile(MemoryBufferRef m, StringRef archiveName) : ELFFileBase(ObjKind, m) {
- this->archiveName = archiveName;
+ this->archiveName = std::string(archiveName);
}
void parse(bool ignoreComdats = false);
// SHT_LLVM_CALL_GRAPH_PROFILE table
ArrayRef<Elf_CGProfile> cgProfile;
+ // Get cached DWARF information.
+ DWARFCache *getDwarf();
+
private:
void initializeSections(bool ignoreComdats);
void initializeSymbols();
void initializeJustSymbols();
- void initializeDwarf();
+
InputSectionBase *getRelocTarget(const Elf_Shdr &sec);
InputSectionBase *createInputSection(const Elf_Shdr &sec);
StringRef getSectionName(const Elf_Shdr &sec);
// reporting. Linker may find reasonable number of errors in a
// single object file, so we cache debugging information in order to
// parse it only once for each object file we link.
- DWARFCache *dwarf;
- llvm::once_flag initDwarfLine;
+ std::unique_ptr<DWARFCache> dwarf;
+ llvm::once_flag initDwarf;
};
// LazyObjFile is analogous to ArchiveFile in the sense that
LazyObjFile(MemoryBufferRef m, StringRef archiveName,
uint64_t offsetInArchive)
: InputFile(LazyObjKind, m), offsetInArchive(offsetInArchive) {
- this->archiveName = archiveName;
+ this->archiveName = std::string(archiveName);
}
static bool classof(const InputFile *f) { return f->kind() == LazyObjKind; }
template <class ELFT> void parse();
void fetch();
+ bool fetched = false;
+
private:
uint64_t offsetInArchive;
};
// more than once.)
void fetch(const Archive::Symbol &sym);
+ size_t getMemberCount() const;
+ size_t getFetchedMemberCount() const { return seen.size(); }
+
+ bool parsed = false;
+
private:
std::unique_ptr<Archive> file;
llvm::DenseSet<uint64_t> seen;
class SharedFile : public ELFFileBase {
public:
SharedFile(MemoryBufferRef m, StringRef defaultSoName)
- : ELFFileBase(SharedKind, m), soName(defaultSoName),
+ : ELFFileBase(SharedKind, m), soName(std::string(defaultSoName)),
isNeeded(!config->asNeeded) {}
// This is actually a vector of Elf_Verdef pointers.
// Used for --as-needed
bool isNeeded;
+
+private:
+ template <typename ELFT>
+ std::vector<uint32_t> parseVerneed(const llvm::object::ELFFile<ELFT> &obj,
+ const typename ELFT::Shdr *sec);
};
class BinaryFile : public InputFile {
std::string replaceThinLTOSuffix(StringRef path);
+extern std::vector<ArchiveFile *> archiveFiles;
extern std::vector<BinaryFile *> binaryFiles;
extern std::vector<BitcodeFile *> bitcodeFiles;
extern std::vector<LazyObjFile *> lazyObjFiles;
return cast_or_null<ObjFile<ELFT>>(file);
}
+ // If basic block sections are enabled, many code sections could end up with
+ // one or two jump instructions at the end that could be relaxed to a smaller
+ // instruction. The members below help trimming the trailing jump instruction
+ // and shrinking a section.
+ unsigned bytesDropped = 0;
+
+ void drop_back(uint64_t num) { bytesDropped += num; }
+
+ void push_back(uint64_t num) {
+ assert(bytesDropped >= num);
+ bytesDropped -= num;
+ }
+
+ void trim() {
+ if (bytesDropped) {
+ rawData = rawData.drop_back(bytesDropped);
+ bytesDropped = 0;
+ }
+ }
+
ArrayRef<uint8_t> data() const {
if (uncompressedSize >= 0)
uncompress();
// the mmap'ed output buffer.
template <class ELFT> void relocate(uint8_t *buf, uint8_t *bufEnd);
void relocateAlloc(uint8_t *buf, uint8_t *bufEnd);
+ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type,
+ int64_t A, uint64_t P, const Symbol &Sym,
+ RelExpr Expr);
// The native ELF reloc data type is not very convenient to handle.
// So we convert ELF reloc records to our own records in Relocations.cpp.
// This vector contains such "cooked" relocations.
std::vector<Relocation> relocations;
+ // Indicates that this section needs to be padded with a NOP filler if set to
+ // true.
+ bool nopFiller = false;
+
+ // These are modifiers to jump instructions that are necessary when basic
+ // block sections are enabled. Basic block sections creates opportunities to
+ // relax jump instructions at basic block boundaries after reordering the
+ // basic blocks.
+ std::vector<JumpInstrMod> jumpInstrMods;
+
// A function compiled with -fsplit-stack calling a function
// compiled without -fsplit-stack needs its prologue adjusted. Find
// such functions and adjust their prologues. This is very similar
// The list of all input sections.
extern std::vector<InputSectionBase *> inputSections;
+// The set of TOC entries (.toc + addend) for which we should not apply
+// toc-indirect to toc-relative relaxation. const Symbol * refers to the
+// STT_SECTION symbol associated to the .toc input section.
+extern llvm::DenseSet<std::pair<const Symbol *, uint64_t>> ppc64noTocRelax;
+
} // namespace elf
std::string toString(const elf::InputSectionBase *);
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
// Creates an empty file to store a list of object files for final
// linking of distributed ThinLTO.
}
static std::string getThinLTOOutputFile(StringRef modulePath) {
- return lto::getThinLTOOutputFile(modulePath,
- config->thinLTOPrefixReplace.first,
- config->thinLTOPrefixReplace.second);
+ return lto::getThinLTOOutputFile(
+ std::string(modulePath), std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second));
}
static lto::Config createConfig() {
c.Options.FunctionSections = true;
c.Options.DataSections = true;
+ // Check if basic block sections must be used.
+ // Allowed values for --lto-basicblock-sections are "all", "labels",
+ // "<file name specifying basic block ids>", or none. This is the equivalent
+ // of -fbasic-block-sections= flag in clang.
+ if (!config->ltoBasicBlockSections.empty()) {
+ if (config->ltoBasicBlockSections == "all") {
+ c.Options.BBSections = BasicBlockSection::All;
+ } else if (config->ltoBasicBlockSections == "labels") {
+ c.Options.BBSections = BasicBlockSection::Labels;
+ } else if (config->ltoBasicBlockSections == "none") {
+ c.Options.BBSections = BasicBlockSection::None;
+ } else {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
+ MemoryBuffer::getFile(config->ltoBasicBlockSections.str());
+ if (!MBOrErr) {
+ error("cannot open " + config->ltoBasicBlockSections + ":" +
+ MBOrErr.getError().message());
+ } else {
+ c.Options.BBSectionsFuncListBuf = std::move(*MBOrErr);
+ }
+ c.Options.BBSections = BasicBlockSection::List;
+ }
+ }
+
+ c.Options.UniqueBasicBlockSectionNames =
+ config->ltoUniqueBasicBlockSectionNames;
+
if (auto relocModel = getRelocModelFromCMModel())
c.RelocModel = *relocModel;
else if (config->relocatable)
c.PTO.SLPVectorization = c.OptLevel > 1;
// Set up a custom pipeline if we've been asked to.
- c.OptPipeline = config->ltoNewPmPasses;
- c.AAPipeline = config->ltoAAPipeline;
+ c.OptPipeline = std::string(config->ltoNewPmPasses);
+ c.AAPipeline = std::string(config->ltoAAPipeline);
// Set up optimization remarks if we've been asked to.
- c.RemarksFilename = config->optRemarksFilename;
- c.RemarksPasses = config->optRemarksPasses;
+ c.RemarksFilename = std::string(config->optRemarksFilename);
+ c.RemarksPasses = std::string(config->optRemarksPasses);
c.RemarksWithHotness = config->optRemarksWithHotness;
- c.RemarksFormat = config->optRemarksFormat;
+ c.RemarksFormat = std::string(config->optRemarksFormat);
- c.SampleProfile = config->ltoSampleProfile;
+ c.SampleProfile = std::string(config->ltoSampleProfile);
c.UseNewPM = config->ltoNewPassManager;
c.DebugPassManager = config->ltoDebugPassManager;
- c.DwoDir = config->dwoDir;
+ c.DwoDir = std::string(config->dwoDir);
- c.CSIRProfile = config->ltoCSProfileFile;
+ c.HasWholeProgramVisibility = config->ltoWholeProgramVisibility;
+ c.AlwaysEmitRegularLTOObj = !config->ltoObjPath.empty();
+
+ for (const llvm::StringRef &name : config->thinLTOModulesToCompile)
+ c.ThinLTOModulesToCompile.emplace_back(name);
+
+ c.TimeTraceEnabled = config->timeTraceEnabled;
+ c.TimeTraceGranularity = config->timeTraceGranularity;
+
+ c.CSIRProfile = std::string(config->ltoCSProfileFile);
c.RunCSIRInstr = config->ltoCSProfileGenerate;
if (config->emitLLVM) {
};
}
+ if (config->ltoEmitAsm)
+ c.CGFileType = CGFT_AssemblyFile;
+
if (config->saveTemps)
checkError(c.addSaveTemps(config->outputFile.str() + ".",
/*UseInputModulePath*/ true));
if (config->thinLTOIndexOnly) {
auto onIndexWrite = [&](StringRef s) { thinIndices.erase(s); };
backend = lto::createWriteIndexesThinBackend(
- config->thinLTOPrefixReplace.first, config->thinLTOPrefixReplace.second,
+ std::string(config->thinLTOPrefixReplace.first),
+ std::string(config->thinLTOPrefixReplace.second),
config->thinLTOEmitImportsFiles, indexFile.get(), onIndexWrite);
- } else if (config->thinLTOJobs != -1U) {
- backend = lto::createInProcessThinBackend(config->thinLTOJobs);
+ } else {
+ backend = lto::createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
}
ltoObj = std::make_unique<lto::LTO>(createConfig(), backend,
// distributed build system that depends on that behavior.
static void thinLTOCreateEmptyIndexFiles() {
for (LazyObjFile *f : lazyObjFiles) {
- if (!isBitcode(f->mb))
+ if (f->fetched || !isBitcode(f->mb))
continue;
std::string path = replaceThinLTOSuffix(getThinLTOOutputFile(f->getName()));
std::unique_ptr<raw_fd_ostream> os = openFile(path + ".thinlto.bc");
},
cache));
- // Emit empty index files for non-indexed files
- for (StringRef s : thinIndices) {
- std::string path = getThinLTOOutputFile(s);
- openFile(path + ".thinlto.bc");
- if (config->thinLTOEmitImportsFiles)
- openFile(path + ".imports");
+ // Emit empty index files for non-indexed files but not in single-module mode.
+ if (config->thinLTOModulesToCompile.empty()) {
+ for (StringRef s : thinIndices) {
+ std::string path = getThinLTOOutputFile(s);
+ openFile(path + ".thinlto.bc");
+ if (config->thinLTOEmitImportsFiles)
+ openFile(path + ".imports");
+ }
}
if (config->thinLTOIndexOnly) {
}
if (config->saveTemps) {
- saveBuffer(buf[0], config->outputFile + ".lto.o");
+ if (!buf[0].empty())
+ saveBuffer(buf[0], config->outputFile + ".lto.o");
for (unsigned i = 1; i != maxTasks; ++i)
saveBuffer(buf[i], config->outputFile + Twine(i) + ".lto.o");
}
+ if (config->ltoEmitAsm) {
+ saveBuffer(buf[0], config->outputFile);
+ for (unsigned i = 1; i != maxTasks; ++i)
+ saveBuffer(buf[i], config->outputFile + Twine(i));
+ return {};
+ }
+
std::vector<InputFile *> ret;
for (unsigned i = 0; i != maxTasks; ++i)
if (!buf[i].empty())
ret.push_back(createObjectFile(*file));
return ret;
}
-
-} // namespace elf
-} // namespace lld
#include "Writer.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include <algorithm>
#include <cassert>
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
-LinkerScript *script;
+LinkerScript *elf::script;
static uint64_t getOutputSectionVA(SectionBase *sec) {
OutputSection *os = sec->getOutputSection();
if (!secRef)
secRef = sec;
}
- sec->location = location;
+ sec->location = std::string(location);
return sec;
}
static void expandMemoryRegion(MemoryRegion *memRegion, uint64_t size,
StringRef regionName, StringRef secName) {
memRegion->curPos += size;
- uint64_t newSize = memRegion->curPos - memRegion->origin;
- if (newSize > memRegion->length)
+ uint64_t newSize = memRegion->curPos - (memRegion->origin)().getValue();
+ uint64_t length = (memRegion->length)().getValue();
+ if (newSize > length)
error("section '" + secName + "' will not fit in region '" + regionName +
- "': overflowed by " + Twine(newSize - memRegion->length) + " bytes");
+ "': overflowed by " + Twine(newSize - length) + " bytes");
}
void LinkerScript::expandMemoryRegions(uint64_t size) {
// write expressions like this: `alignment = 16; . = ALIGN(., alignment)`.
uint64_t symValue = value.sec ? 0 : value.getValue();
- Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, STT_NOTYPE,
+ Defined newSym(nullptr, cmd->name, STB_GLOBAL, visibility, value.type,
symValue, 0, sec);
Symbol *sym = symtab->insert(cmd->name);
return changed;
}
-// This method is used to handle INSERT AFTER statement. Here we rebuild
-// the list of script commands to mix sections inserted into.
+// Process INSERT [AFTER|BEFORE] commands. For each command, we move the
+// specified output section to the designated place.
void LinkerScript::processInsertCommands() {
- std::vector<BaseCommand *> v;
- auto insert = [&](std::vector<BaseCommand *> &from) {
- v.insert(v.end(), from.begin(), from.end());
- from.clear();
- };
-
- for (BaseCommand *base : sectionCommands) {
- if (auto *os = dyn_cast<OutputSection>(base)) {
- insert(insertBeforeCommands[os->name]);
- v.push_back(base);
- insert(insertAfterCommands[os->name]);
+ for (const InsertCommand &cmd : insertCommands) {
+ // If cmd.os is empty, it may have been discarded by
+ // adjustSectionsBeforeSorting(). We do not handle such output sections.
+ auto from = llvm::find(sectionCommands, cmd.os);
+ if (from == sectionCommands.end())
continue;
+ sectionCommands.erase(from);
+
+ auto insertPos = llvm::find_if(sectionCommands, [&cmd](BaseCommand *base) {
+ auto *to = dyn_cast<OutputSection>(base);
+ return to != nullptr && to->name == cmd.where;
+ });
+ if (insertPos == sectionCommands.end()) {
+ error("unable to insert " + cmd.os->name +
+ (cmd.isAfter ? " after " : " before ") + cmd.where);
+ } else {
+ if (cmd.isAfter)
+ ++insertPos;
+ sectionCommands.insert(insertPos, cmd.os);
}
- v.push_back(base);
}
-
- for (auto &cmds : {insertBeforeCommands, insertAfterCommands})
- for (const std::pair<StringRef, std::vector<BaseCommand *>> &p : cmds)
- if (!p.second.empty())
- error("unable to INSERT AFTER/BEFORE " + p.first +
- ": section not defined");
-
- sectionCommands = std::move(v);
}
// Symbols defined in script should not be inlined by LTO. At the same time
cmd->sym->section = v.sec;
cmd->sym->value = v.getSectionOffset();
}
+ cmd->sym->type = v.type;
}
static std::string getFilename(InputFile *file) {
if (!file)
return "";
if (file->archiveName.empty())
- return file->getName();
- return (file->archiveName + "(" + file->getName() + ")").str();
+ return std::string(file->getName());
+ return (file->archiveName + ':' + file->getName()).str();
}
bool LinkerScript::shouldKeep(InputSectionBase *s) {
for (InputSectionDescription *id : keptSections)
if (id->filePat.match(filename))
for (SectionPattern &p : id->sectionPatterns)
- if (p.sectionPat.match(s->name))
+ if (p.sectionPat.match(s->name) &&
+ (s->flags & id->withFlags) == id->withFlags &&
+ (s->flags & id->withoutFlags) == 0)
return true;
return false;
}
// Compute and remember which sections the InputSectionDescription matches.
std::vector<InputSectionBase *>
-LinkerScript::computeInputSections(const InputSectionDescription *cmd) {
+LinkerScript::computeInputSections(const InputSectionDescription *cmd,
+ ArrayRef<InputSectionBase *> sections) {
std::vector<InputSectionBase *> ret;
// Collects all sections that satisfy constraints of Cmd.
for (const SectionPattern &pat : cmd->sectionPatterns) {
size_t sizeBefore = ret.size();
- for (InputSectionBase *sec : inputSections) {
+ for (InputSectionBase *sec : sections) {
if (!sec->isLive() || sec->parent)
continue;
cast<InputSection>(sec)->getRelocatedSection())
continue;
+ // Check the name early to improve performance in the common case.
+ if (!pat.sectionPat.match(sec->name))
+ continue;
+
std::string filename = getFilename(sec->file);
if (!cmd->filePat.match(filename) ||
pat.excludedFilePat.match(filename) ||
- !pat.sectionPat.match(sec->name))
+ (sec->flags & cmd->withFlags) != cmd->withFlags ||
+ (sec->flags & cmd->withoutFlags) != 0)
continue;
ret.push_back(sec);
discard(ds);
}
+void LinkerScript::discardSynthetic(OutputSection &outCmd) {
+ for (Partition &part : partitions) {
+ if (!part.armExidx || !part.armExidx->isLive())
+ continue;
+ std::vector<InputSectionBase *> secs(part.armExidx->exidxSections.begin(),
+ part.armExidx->exidxSections.end());
+ for (BaseCommand *base : outCmd.sectionCommands)
+ if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
+ std::vector<InputSectionBase *> matches =
+ computeInputSections(cmd, secs);
+ for (InputSectionBase *s : matches)
+ discard(s);
+ }
+ }
+}
+
std::vector<InputSectionBase *>
LinkerScript::createInputSectionList(OutputSection &outCmd) {
std::vector<InputSectionBase *> ret;
for (BaseCommand *base : outCmd.sectionCommands) {
if (auto *cmd = dyn_cast<InputSectionDescription>(base)) {
- cmd->sectionBases = computeInputSections(cmd);
+ cmd->sectionBases = computeInputSections(cmd, inputSections);
for (InputSectionBase *s : cmd->sectionBases)
s->parent = &outCmd;
ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end());
if (sec->name == "/DISCARD/") {
for (InputSectionBase *s : v)
discard(s);
+ discardSynthetic(*sec);
sec->sectionCommands.clear();
continue;
}
auto *firstIsec = cast<InputSectionBase>(
cast<InputSectionDescription>(sec->sectionCommands[0])
->sectionBases[0]);
- if (firstIsec->getLinkOrderDep()->getOutputSection() !=
- isec->getLinkOrderDep()->getOutputSection())
+ OutputSection *firstIsecOut =
+ firstIsec->flags & SHF_LINK_ORDER
+ ? firstIsec->getLinkOrderDep()->getOutputSection()
+ : nullptr;
+ if (firstIsecOut != isec->getLinkOrderDep()->getOutputSection())
continue;
}
std::function<void(InputSectionBase *)> add;
add = [&](InputSectionBase *s) {
if (s->isLive() && !s->parent) {
- StringRef name = getOutputSectionName(s);
-
- if (config->orphanHandling == OrphanHandlingPolicy::Error)
- error(toString(s) + " is being placed in '" + name + "'");
- else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
- warn(toString(s) + " is being placed in '" + name + "'");
+ orphanSections.push_back(s);
- if (OutputSection *sec = findByName(sectionCommands, name)) {
+ StringRef name = getOutputSectionName(s);
+ if (config->unique) {
+ v.push_back(createSection(s, name));
+ } else if (OutputSection *sec = findByName(sectionCommands, name)) {
sec->recordSection(s);
} else {
if (OutputSection *os = addInputSec(map, s, name))
sectionCommands.insert(sectionCommands.begin(), v.begin(), v.end());
}
+void LinkerScript::diagnoseOrphanHandling() const {
+ for (const InputSectionBase *sec : orphanSections) {
+ // Input SHT_REL[A] retained by --emit-relocs are ignored by
+ // computeInputSections(). Don't warn/error.
+ if (isa<InputSection>(sec) &&
+ cast<InputSection>(sec)->getRelocatedSection())
+ continue;
+
+ StringRef name = getOutputSectionName(sec);
+ if (config->orphanHandling == OrphanHandlingPolicy::Error)
+ error(toString(sec) + " is being placed in '" + name + "'");
+ else if (config->orphanHandling == OrphanHandlingPolicy::Warn)
+ warn(toString(sec) + " is being placed in '" + name + "'");
+ }
+}
+
uint64_t LinkerScript::advance(uint64_t size, unsigned alignment) {
bool isTbss =
(ctx->outSec->flags & SHF_TLS) && ctx->outSec->type == SHT_NOBITS;
void LinkerScript::switchTo(OutputSection *sec) {
ctx->outSec = sec;
- uint64_t before = advance(0, 1);
- ctx->outSec->addr = advance(0, ctx->outSec->alignment);
- expandMemoryRegions(ctx->outSec->addr - before);
+ uint64_t pos = advance(0, 1);
+ if (sec->addrExpr && script->hasSectionsCommand) {
+ // The alignment is ignored.
+ ctx->outSec->addr = pos;
+ } else {
+ // ctx->outSec->alignment is the max of ALIGN and the maximum of input
+ // section alignments.
+ ctx->outSec->addr = advance(0, ctx->outSec->alignment);
+ expandMemoryRegions(ctx->outSec->addr - pos);
+ }
}
// This function searches for a memory region to place the given output
if (!(sec->flags & SHF_ALLOC))
dot = 0;
+ const bool sameMemRegion = ctx->memRegion == sec->memRegion;
+ const bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr;
ctx->memRegion = sec->memRegion;
ctx->lmaRegion = sec->lmaRegion;
if (ctx->memRegion)
expandMemoryRegion(ctx->memRegion, dot - ctx->memRegion->curPos,
ctx->memRegion->name, sec->name);
+ switchTo(sec);
+
+ // ctx->lmaOffset is LMA minus VMA. If LMA is explicitly specified via AT() or
+ // AT>, recompute ctx->lmaOffset; otherwise, if both previous/current LMA
+ // region is the default, and the two sections are in the same memory region,
+ // reuse previous lmaOffset; otherwise, reset lmaOffset to 0. This emulates
+ // heuristics described in
+ // https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html
if (sec->lmaExpr)
ctx->lmaOffset = sec->lmaExpr().getValue() - dot;
+ else if (MemoryRegion *mr = sec->lmaRegion)
+ ctx->lmaOffset = alignTo(mr->curPos, sec->alignment) - dot;
+ else if (!sameMemRegion || !prevLMARegionIsDefault)
+ ctx->lmaOffset = 0;
- if (MemoryRegion *mr = sec->lmaRegion)
- ctx->lmaOffset = mr->curPos - dot;
-
- switchTo(sec);
-
- // If neither AT nor AT> is specified for an allocatable section, the linker
- // will set the LMA such that the difference between VMA and LMA for the
- // section is the same as the preceding output section in the same region
- // https://sourceware.org/binutils/docs-2.20/ld/Output-Section-LMA.html
- // This, however, should only be done by the first "non-header" section
- // in the segment.
+ // Propagate ctx->lmaOffset to the first "non-header" section.
if (PhdrEntry *l = ctx->outSec->ptLoad)
if (sec == findFirstSection(l))
l->lmaOffset = ctx->lmaOffset;
// We do not want to keep any special flags for output section
// in case it is empty.
- bool isEmpty = getInputSections(sec).empty();
+ bool isEmpty = (getFirstInputSection(sec) == nullptr);
if (isEmpty)
sec->flags = flags & ((sec->nonAlloc ? 0 : (uint64_t)SHF_ALLOC) |
SHF_WRITE | SHF_EXECINSTR);
LinkerScript::AddressState::AddressState() {
for (auto &mri : script->memoryRegions) {
MemoryRegion *mr = mri.second;
- mr->curPos = mr->origin;
+ mr->curPos = (mr->origin)().getValue();
}
}
}
if (Symbol *sym = symtab->find(name)) {
- if (auto *ds = dyn_cast<Defined>(sym))
- return {ds->section, false, ds->value, loc};
+ if (auto *ds = dyn_cast<Defined>(sym)) {
+ ExprValue v{ds->section, false, ds->value, loc};
+ // Retain the original st_type, so that the alias will get the same
+ // behavior in relocation processing. Any operation will reset st_type to
+ // STT_NOTYPE.
+ v.type = ds->type;
+ return v;
+ }
if (isa<SharedSymbol>(sym))
if (!errorOnMissingSection)
return {nullptr, false, 0, loc};
if (Optional<size_t> idx = getPhdrIndex(phdrsCommands, s))
ret.push_back(*idx);
else if (s != "NONE")
- error(cmd->location + ": section header '" + s +
+ error(cmd->location + ": program header '" + s +
"' is not listed in PHDRS");
}
return ret;
}
-
-} // namespace elf
-} // namespace lld
uint64_t val;
uint64_t alignment = 1;
+ // The original st_type if the expression represents a symbol. Any operation
+ // resets type to STT_NOTYPE.
+ uint8_t type = llvm::ELF::STT_NOTYPE;
+
// Original source location. Used for error messages.
std::string loc;
};
std::string commandString;
// Address of this assignment command.
- unsigned addr;
+ uint64_t addr;
// Size of this assignment command. This is usually 0, but if
// you move '.' this may be greater than 0.
- unsigned size;
+ uint64_t size;
};
// Linker scripts allow additional constraints to be put on output sections.
// target memory. Instances of the struct are created by parsing the
// MEMORY command.
struct MemoryRegion {
- MemoryRegion(StringRef name, uint64_t origin, uint64_t length, uint32_t flags,
+ MemoryRegion(StringRef name, Expr origin, Expr length, uint32_t flags,
uint32_t negFlags)
- : name(name), origin(origin), length(length), flags(flags),
+ : name(std::string(name)), origin(origin), length(length), flags(flags),
negFlags(negFlags) {}
std::string name;
- uint64_t origin;
- uint64_t length;
+ Expr origin;
+ Expr length;
uint32_t flags;
uint32_t negFlags;
uint64_t curPos = 0;
};
struct InputSectionDescription : BaseCommand {
- InputSectionDescription(StringRef filePattern)
- : BaseCommand(InputSectionKind), filePat(filePattern) {}
+ InputSectionDescription(StringRef filePattern, uint64_t withFlags = 0,
+ uint64_t withoutFlags = 0)
+ : BaseCommand(InputSectionKind), filePat(filePattern),
+ withFlags(withFlags), withoutFlags(withoutFlags) {}
static bool classof(const BaseCommand *c) {
return c->kind == InputSectionKind;
}
- StringMatcher filePat;
+ SingleStringMatcher filePat;
// Input sections that matches at least one of SectionPatterns
// will be associated with this InputSectionDescription.
// they were created in. This is used to insert newly created ThunkSections
// into Sections at the end of a createThunks() pass.
std::vector<std::pair<ThunkSection *, uint32_t>> thunkSections;
+
+ // SectionPatterns can be filtered with the INPUT_SECTION_FLAGS command.
+ uint64_t withFlags;
+ uint64_t withoutFlags;
};
// Represents BYTE(), SHORT(), LONG(), or QUAD().
unsigned size;
};
+struct InsertCommand {
+ OutputSection *os;
+ bool isAfter;
+ StringRef where;
+};
+
struct PhdrsCommand {
StringRef name;
unsigned type = llvm::ELF::PT_NULL;
void expandMemoryRegions(uint64_t size);
std::vector<InputSectionBase *>
- computeInputSections(const InputSectionDescription *);
+ computeInputSections(const InputSectionDescription *,
+ ArrayRef<InputSectionBase *>);
std::vector<InputSectionBase *> createInputSectionList(OutputSection &cmd);
+ void discardSynthetic(OutputSection &);
+
std::vector<size_t> getPhdrIndices(OutputSection *sec);
MemoryRegion *findMemoryRegion(OutputSection *sec);
ExprValue getSymbolValue(StringRef name, const Twine &loc);
void addOrphanSections();
+ void diagnoseOrphanHandling() const;
void adjustSectionsBeforeSorting();
void adjustSectionsAfterSorting();
// A list of symbols referenced by the script.
std::vector<llvm::StringRef> referencedSymbols;
- // Used to implement INSERT [AFTER|BEFORE]. Contains commands that need
- // to be inserted into SECTIONS commands list.
- llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertAfterCommands;
- llvm::DenseMap<StringRef, std::vector<BaseCommand *>> insertBeforeCommands;
+ // Used to implement INSERT [AFTER|BEFORE]. Contains output sections that need
+ // to be reordered.
+ std::vector<InsertCommand> insertCommands;
+
+ // Sections that will be warned/errored by --orphan-handling.
+ std::vector<const InputSectionBase *> orphanSections;
};
extern LinkerScript *script;
#include "Symbols.h"
#include "SyntheticSections.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/SetVector.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace llvm::object;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
using SymbolMapTy = DenseMap<const SectionBase *, SmallVector<Defined *, 4>>;
static constexpr char indent8[] = " "; // 8 spaces
}
}
-void writeMapFile() {
+void elf::writeMapFile() {
if (config->mapFile.empty())
return;
//
// In this case, strlen is defined by libc.so.6 and used by other two
// files.
-void writeCrossReferenceTable() {
+void elf::writeCrossReferenceTable() {
if (!config->cref)
return;
}
}
-} // namespace elf
-} // namespace lld
+void elf::writeArchiveStats() {
+ if (config->printArchiveStats.empty())
+ return;
+
+ std::error_code ec;
+ raw_fd_ostream os(config->printArchiveStats, ec, sys::fs::OF_None);
+ if (ec) {
+ error("--print-archive-stats=: cannot open " + config->printArchiveStats +
+ ": " + ec.message());
+ return;
+ }
+
+ os << "members\tfetched\tarchive\n";
+ for (const ArchiveFile *f : archiveFiles)
+ os << f->getMemberCount() << '\t' << f->getFetchedMemberCount() << '\t'
+ << f->getName() << '\n';
+}
namespace elf {
void writeMapFile();
void writeCrossReferenceTable();
+void writeArchiveStats();
} // namespace elf
} // namespace lld
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Object/ELF.h"
+#include "llvm/Support/TimeProfiler.h"
#include <functional>
#include <vector>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
+using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace endian = llvm::support::endian;
-
-namespace lld {
-namespace elf {
namespace {
template <class ELFT> class MarkLive {
public:
if (firstRelI == (unsigned)-1)
continue;
- if (endian::read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
+ if (read32<ELFT::TargetEndianness>(piece.data().data() + 4) == 0) {
// This is a CIE, we only need to worry about the first relocation. It is
// known to point to the personality function.
resolveReloc(eh, rels[firstRelI], false);
// Before calling this function, Live bits are off for all
// input sections. This function make some or all of them on
// so that they are emitted to the output file.
-template <class ELFT> void markLive() {
+template <class ELFT> void elf::markLive() {
+ llvm::TimeTraceScope timeScope("markLive");
// If -gc-sections is not given, no sections are removed.
if (!config->gcSections) {
for (InputSectionBase *sec : inputSections)
message("removing unused section " + toString(sec));
}
-template void markLive<ELF32LE>();
-template void markLive<ELF32BE>();
-template void markLive<ELF64LE>();
-template void markLive<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
+template void elf::markLive<ELF32LE>();
+template void elf::markLive<ELF32BE>();
+template void elf::markLive<ELF64LE>();
+template void elf::markLive<ELF64BE>();
include "llvm/Option/OptParser.td"
+// Convenience classes for long options which only accept two dashes. For lld
+// specific or newer long options, we prefer two dashes to avoid collision with
+// short options. For many others, we have to accept both forms to be compatible
+// with GNU ld.
+class FF<string name> : Flag<["--"], name>;
+class JJ<string name>: Joined<["--"], name>;
+
+multiclass EEq<string name, string help> {
+ def NAME: Separate<["--"], name>;
+ def NAME # _eq: Joined<["--"], name # "=">, Alias<!cast<Separate>(NAME)>,
+ HelpText<help>;
+}
+
+multiclass BB<string name, string help1, string help2> {
+ def NAME: Flag<["--"], name>, HelpText<help1>;
+ def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
+}
+
// For options whose names are multiple letters, either one dash or
// two can precede the option name except those that start with 'o'.
class F<string name>: Flag<["--", "-"], name>;
defm defsym: Eq<"defsym", "Define a symbol alias">, MetaVarName<"<symbol>=<value>">;
+defm optimize_bb_jumps: BB<"optimize-bb-jumps",
+ "Remove direct jumps at the end to the next basic block",
+ "Do not remove any direct jumps at the end to the next basic block (default)">;
+
defm split_stack_adjust_size
: Eq<"split-stack-adjust-size",
"Specify adjustment to stack size when a split-stack function calls a "
"Allow unresolved references in shared libraries (default when linking a shared library)",
"Do not allow unresolved references in shared libraries (default when linking an executable)">;
-defm apply_dynamic_relocs: B<"apply-dynamic-relocs",
+defm apply_dynamic_relocs: BB<"apply-dynamic-relocs",
"Apply link-time values for dynamic relocations",
"Do not apply link-time values for dynamic relocations (default)">;
-defm dependent_libraries: B<"dependent-libraries",
+defm dependent_libraries: BB<"dependent-libraries",
"Process dependent library specifiers from input files (default)",
"Ignore dependent library specifiers from input files">;
defm call_graph_ordering_file:
Eq<"call-graph-ordering-file", "Layout sections to optimize the given callgraph">;
-defm call_graph_profile_sort: B<"call-graph-profile-sort",
+defm call_graph_profile_sort: BB<"call-graph-profile-sort",
"Reorder sections with call graph profile (default)",
"Do not reorder sections with call graph profile">;
defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">;
-defm dynamic_list: Eq<"dynamic-list", "Read a list of dynamic symbols">;
+defm dynamic_list : Eq<"dynamic-list",
+ "Read a list of dynamic symbols. (executable) Put matched non-local defined"
+ "symbols to the dynamic symbol table. (shared object) References to matched"
+ "non-local STV_DEFAULT symbols shouldn't be bound to definitions within the "
+ "shared object. Implies -Bsymbolic but does not set DF_SYMBOLIC">,
+ MetaVarName<"<file>">;
defm eh_frame_hdr: B<"eh-frame-hdr",
"Request creation of .eh_frame_hdr section and PT_GNU_EH_FRAME segment header",
defm exclude_libs: Eq<"exclude-libs", "Exclude static libraries from automatic export">;
-defm execute_only: B<"execute-only",
+defm execute_only: BB<"execute-only",
"Mark executable sections unreadable",
"Mark executable sections readable (default)">;
"Put symbols in the dynamic symbol table",
"Do not put symbols in the dynamic symbol table (default)">;
-defm export_dynamic_symbol:
- Eq<"export-dynamic-symbol", "Put a symbol in the dynamic symbol table">;
+defm export_dynamic_symbol : EEq<"export-dynamic-symbol",
+ "(executable) Put matched symbols in the dynamic symbol table. "
+ "(shared object) References to matched non-local STV_DEFAULT symbols "
+ "shouldn't be bound to definitions within the shared object. "
+ "Does not imply -Bsymbolic.">,
+ MetaVarName<"glob">;
defm fatal_warnings: B<"fatal-warnings",
"Treat warnings as errors",
"Enable garbage collection of unused sections",
"Disable garbage collection of unused sections (default)">;
-defm gdb_index: B<"gdb-index",
+defm gdb_index: BB<"gdb-index",
"Generate .gdb_index section",
"Do not generate .gdb_index section (default)">;
-defm gnu_unique: B<"gnu-unique",
+defm gnu_unique: BB<"gnu-unique",
"Enable STB_GNU_UNIQUE symbol binding (default)",
"Disable STB_GNU_UNIQUE symbol binding">;
def icf_none: F<"icf=none">, HelpText<"Disable identical code folding (default)">;
-defm ignore_function_address_equality: B<"ignore-function-address-equality",
- "lld can break the address equality of functions",
- "lld cannot break the address equality of functions">;
+def ignore_function_address_equality: F<"ignore-function-address-equality">,
+ HelpText<"lld can break the address equality of functions">;
def ignore_data_address_equality: F<"ignore-data-address-equality">,
HelpText<"lld can break the address equality of data">;
"Enable merging .ARM.exidx entries (default)",
"Disable merging .ARM.exidx entries">;
-defm mmap_output_file: B<"mmap-output-file",
+defm mmap_output_file: BB<"mmap-output-file",
"Mmap the output file for writing (default)",
"Do not mmap the output file for writing">;
def no_omagic: F<"no-omagic">, MetaVarName<"<magic>">,
HelpText<"Do not set the text data sections to be writable, page align sections (default)">;
-def no_rosegment: F<"no-rosegment">,
- HelpText<"Do not put read-only non-executable sections in their own segment">;
-
def no_undefined: F<"no-undefined">,
HelpText<"Report unresolved symbols even if the linker is creating a shared library">;
def oformat: Separate<["--"], "oformat">, MetaVarName<"<format>">,
HelpText<"Specify the binary format for the output object file">;
-def omagic: Flag<["--"], "omagic">, MetaVarName<"<magic>">,
+def omagic: FF<"omagic">, MetaVarName<"<magic>">,
HelpText<"Set the text and data sections to be readable and writable, do not page align sections, link against static libraries">;
defm orphan_handling:
Eq<"pack-dyn-relocs", "Pack dynamic relocations in the given format">,
MetaVarName<"[none,android,relr,android+relr]">;
-defm use_android_relr_tags: B<"use-android-relr-tags",
+defm use_android_relr_tags: BB<"use-android-relr-tags",
"Use SHT_ANDROID_RELR / DT_ANDROID_RELR* tags instead of SHT_RELR / DT_RELR*",
"Use SHT_RELR / DT_RELR* tags (default)">;
"List identical folded sections",
"Do not list identical folded sections (default)">;
+def print_archive_stats: J<"print-archive-stats=">,
+ HelpText<"Write archive usage statistics to the specified file. "
+ "Print the numbers of members and fetched members for each archive">;
+
defm print_symbol_order: Eq<"print-symbol-order",
"Print a symbol order specified by --call-graph-ordering-file into the specified file">;
defm reproduce: Eq<"reproduce", "Write a tar file containing input files and command line options to reproduce link">;
+defm rosegment: BB<"rosegment",
+ "Put read-only non-executable sections in their own segment (default)",
+ "Do not put read-only non-executable sections in their own segment">;
+
defm rpath: Eq<"rpath", "Add a DT_RUNPATH to the output">;
def relocatable: F<"relocatable">, HelpText<"Create relocatable object file">;
Eq<"target2", "Interpret R_ARM_TARGET2 as <type>, where <type> is one of rel, abs, or got-rel">,
MetaVarName<"<type>">;
-defm threads: B<"threads",
- "Run the linker multi-threaded (default)",
- "Do not run the linker multi-threaded">;
+defm threads
+ : Eq<"threads",
+ "Number of threads. '1' disables multi-threading. By default all "
+ "available hardware threads are used">;
+
+def time_trace: F<"time-trace">, HelpText<"Record time trace">;
+def time_trace_file_eq: J<"time-trace-file=">, HelpText<"Specify time trace output file">;
+
+defm time_trace_granularity: Eq<"time-trace-granularity",
+ "Minimum time granularity (in microseconds) traced by time profiler">;
defm toc_optimize : B<"toc-optimize",
"(PowerPC64) Enable TOC related optimizations (default)",
defm undefined_glob: Eq<"undefined-glob", "Force undefined symbol during linking">,
MetaVarName<"<pattern>">;
+def unique: F<"unique">, HelpText<"Creates a separate output section for every orphan input section">;
+
defm unresolved_symbols:
Eq<"unresolved-symbols", "Determine how to handle unresolved symbols">;
defm version_script: Eq<"version-script", "Read a version script">;
-defm warn_backrefs: B<"warn-backrefs",
+defm warn_backrefs: BB<"warn-backrefs",
"Warn about backward symbol references to fetch archive members",
"Do not warn about backward symbol references to fetch archive members (default)">;
+defm warn_backrefs_exclude
+ : EEq<"warn-backrefs-exclude",
+ "Glob describing an archive (or an object file within --start-lib) "
+ "which should be ignored for --warn-backrefs.">,
+ MetaVarName<"<glob>">;
+
defm warn_common: B<"warn-common",
"Warn about duplicate common symbols",
"Do not warn about duplicate common symbols (default)">;
-defm warn_ifunc_textrel: B<"warn-ifunc-textrel",
+defm warn_ifunc_textrel: BB<"warn-ifunc-textrel",
"Warn about using ifunc symbols with text relocations",
"Do not warn about using ifunc symbols with text relocations (default)">;
-defm warn_symbol_ordering: B<"warn-symbol-ordering",
+defm warn_symbol_ordering: BB<"warn-symbol-ordering",
"Warn about problems with the symbol ordering file (default)",
"Do not warn about problems with the symbol ordering file">;
def: Separate<["-"], "b">, Alias<format>, HelpText<"Alias for --format">;
def: JoinedOrSeparate<["-"], "l">, Alias<library>, HelpText<"Alias for --library">;
def: JoinedOrSeparate<["-"], "L">, Alias<library_path>, HelpText<"Alias for --library-path">;
-def: F<"nopie">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
def: F<"no-pic-executable">, Alias<no_pie>, HelpText<"Alias for --no-pie">;
def: Flag<["-"], "n">, Alias<nmagic>, HelpText<"Alias for --nmagic">;
def: Flag<["-"], "N">, Alias<omagic>, HelpText<"Alias for --omagic">;
def: Flag<["-"], "V">, Alias<version>, HelpText<"Alias for --version">;
// LTO-related options.
-def lto_aa_pipeline: J<"lto-aa-pipeline=">,
+def lto_aa_pipeline: JJ<"lto-aa-pipeline=">,
HelpText<"AA pipeline to run during LTO. Used in conjunction with -lto-newpm-passes">;
-def lto_debug_pass_manager: F<"lto-debug-pass-manager">,
+def lto_debug_pass_manager: FF<"lto-debug-pass-manager">,
HelpText<"Debug new pass manager">;
-def lto_new_pass_manager: F<"lto-new-pass-manager">,
+def lto_emit_asm: FF<"lto-emit-asm">,
+ HelpText<"Emit assembly code">;
+def lto_new_pass_manager: FF<"lto-new-pass-manager">,
HelpText<"Use new pass manager">;
-def lto_newpm_passes: J<"lto-newpm-passes=">,
+def lto_newpm_passes: JJ<"lto-newpm-passes=">,
HelpText<"Passes to run during LTO">;
-def lto_O: J<"lto-O">, MetaVarName<"<opt-level>">,
+def lto_O: JJ<"lto-O">, MetaVarName<"<opt-level>">,
HelpText<"Optimization level for LTO">;
-def lto_partitions: J<"lto-partitions=">,
+def lto_partitions: JJ<"lto-partitions=">,
HelpText<"Number of LTO codegen partitions">;
-def lto_cs_profile_generate: F<"lto-cs-profile-generate">,
+def lto_cs_profile_generate: FF<"lto-cs-profile-generate">,
HelpText<"Perform context sensitive PGO instrumentation">;
-def lto_cs_profile_file: J<"lto-cs-profile-file=">,
+def lto_cs_profile_file: JJ<"lto-cs-profile-file=">,
HelpText<"Context sensitive profile file path">;
-def lto_obj_path_eq: J<"lto-obj-path=">;
-def lto_sample_profile: J<"lto-sample-profile=">,
+def lto_obj_path_eq: JJ<"lto-obj-path=">;
+def lto_sample_profile: JJ<"lto-sample-profile=">,
HelpText<"Sample profile file path">;
+def lto_whole_program_visibility: FF<"lto-whole-program-visibility">,
+ HelpText<"Asserts that the LTO link has whole program visibility">;
def disable_verify: F<"disable-verify">;
defm mllvm: Eq<"mllvm", "Additional arguments to forward to LLVM's option processing">;
def opt_remarks_filename: Separate<["--"], "opt-remarks-filename">,
HelpText<"YAML output file for optimization remarks">;
def opt_remarks_passes: Separate<["--"], "opt-remarks-passes">,
HelpText<"Regex for the passes that need to be serialized to the output file">;
-def opt_remarks_with_hotness: Flag<["--"], "opt-remarks-with-hotness">,
+def opt_remarks_with_hotness: FF<"opt-remarks-with-hotness">,
HelpText<"Include hotness information in the optimization remarks file">;
def opt_remarks_format: Separate<["--"], "opt-remarks-format">,
HelpText<"The format used for serializing remarks (default: YAML)">;
-defm plugin_opt: Eq<"plugin-opt", "specifies LTO options for compatibility with GNU linkers">;
def save_temps: F<"save-temps">;
-def thinlto_cache_dir: J<"thinlto-cache-dir=">,
+def lto_basicblock_sections: JJ<"lto-basicblock-sections=">,
+ HelpText<"Enable basic block sections for LTO">;
+defm lto_unique_bb_section_names: BB<"lto-unique-bb-section-names",
+ "Give unique names to every basic block section for LTO",
+ "Do not give unique names to every basic block section for LTO (default)">;
+def shuffle_sections: JJ<"shuffle-sections=">, MetaVarName<"<seed>">,
+ HelpText<"Shuffle input sections using the given seed. If 0, use a random seed">;
+def thinlto_cache_dir: JJ<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">;
-defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
-def thinlto_emit_imports_files: F<"thinlto-emit-imports-files">;
-def thinlto_index_only: F<"thinlto-index-only">;
-def thinlto_index_only_eq: J<"thinlto-index-only=">;
-def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
-def thinlto_object_suffix_replace_eq: J<"thinlto-object-suffix-replace=">;
-def thinlto_prefix_replace_eq: J<"thinlto-prefix-replace=">;
-
-def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for -lto-O">;
+defm thinlto_cache_policy: EEq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
+def thinlto_emit_imports_files: FF<"thinlto-emit-imports-files">;
+def thinlto_index_only: FF<"thinlto-index-only">;
+def thinlto_index_only_eq: JJ<"thinlto-index-only=">;
+def thinlto_jobs: JJ<"thinlto-jobs=">,
+ HelpText<"Number of ThinLTO jobs. Default to --threads=">;
+def thinlto_object_suffix_replace_eq: JJ<"thinlto-object-suffix-replace=">;
+def thinlto_prefix_replace_eq: JJ<"thinlto-prefix-replace=">;
+def thinlto_single_module_eq: JJ<"thinlto-single-module=">,
+ HelpText<"Specific a single module to compile in ThinLTO mode, for debugging only">;
+
+def: J<"plugin-opt=O">, Alias<lto_O>, HelpText<"Alias for --lto-O">;
def: F<"plugin-opt=debug-pass-manager">,
- Alias<lto_debug_pass_manager>, HelpText<"Alias for -lto-debug-pass-manager">;
-def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for -disable-verify">;
+ Alias<lto_debug_pass_manager>, HelpText<"Alias for --lto-debug-pass-manager">;
+def: F<"plugin-opt=disable-verify">, Alias<disable_verify>, HelpText<"Alias for --disable-verify">;
def plugin_opt_dwo_dir_eq: J<"plugin-opt=dwo_dir=">,
HelpText<"Directory to store .dwo files when LTO and debug fission are used">;
+def plugin_opt_emit_asm: F<"plugin-opt=emit-asm">,
+ Alias<lto_emit_asm>, HelpText<"Alias for --lto-emit-asm">;
def plugin_opt_emit_llvm: F<"plugin-opt=emit-llvm">;
-def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for -thinlto-jobs">;
-def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for -lto-partitions">;
+def: J<"plugin-opt=jobs=">, Alias<thinlto_jobs>, HelpText<"Alias for --thinlto-jobs">;
+def: J<"plugin-opt=lto-partitions=">, Alias<lto_partitions>, HelpText<"Alias for --lto-partitions">;
def plugin_opt_mcpu_eq: J<"plugin-opt=mcpu=">;
def: F<"plugin-opt=new-pass-manager">,
- Alias<lto_new_pass_manager>, HelpText<"Alias for -lto-new-pass-manager">;
+ Alias<lto_new_pass_manager>, HelpText<"Alias for --lto-new-pass-manager">;
def: F<"plugin-opt=cs-profile-generate">,
- Alias<lto_cs_profile_generate>, HelpText<"Alias for -lto-cs-profile-generate">;
+ Alias<lto_cs_profile_generate>, HelpText<"Alias for --lto-cs-profile-generate">;
def: J<"plugin-opt=cs-profile-path=">,
- Alias<lto_cs_profile_file>, HelpText<"Alias for -lto-cs-profile-file">;
+ Alias<lto_cs_profile_file>, HelpText<"Alias for --lto-cs-profile-file">;
def: J<"plugin-opt=obj-path=">,
Alias<lto_obj_path_eq>,
- HelpText<"Alias for -lto-obj-path=">;
+ HelpText<"Alias for --lto-obj-path=">;
def: J<"plugin-opt=sample-profile=">,
- Alias<lto_sample_profile>, HelpText<"Alias for -lto-sample-profile">;
-def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for -save-temps">;
+ Alias<lto_sample_profile>, HelpText<"Alias for --lto-sample-profile">;
+def: F<"plugin-opt=save-temps">, Alias<save_temps>, HelpText<"Alias for --save-temps">;
def: F<"plugin-opt=thinlto-emit-imports-files">,
Alias<thinlto_emit_imports_files>,
- HelpText<"Alias for -thinlto-emit-imports-files">;
+ HelpText<"Alias for --thinlto-emit-imports-files">;
def: F<"plugin-opt=thinlto-index-only">,
Alias<thinlto_index_only>,
- HelpText<"Alias for -thinlto-index-only">;
+ HelpText<"Alias for --thinlto-index-only">;
def: J<"plugin-opt=thinlto-index-only=">,
Alias<thinlto_index_only_eq>,
- HelpText<"Alias for -thinlto-index-only=">;
+ HelpText<"Alias for --thinlto-index-only=">;
def: J<"plugin-opt=thinlto-object-suffix-replace=">,
Alias<thinlto_object_suffix_replace_eq>,
- HelpText<"Alias for -thinlto-object-suffix-replace=">;
+ HelpText<"Alias for --thinlto-object-suffix-replace=">;
def: J<"plugin-opt=thinlto-prefix-replace=">,
Alias<thinlto_prefix_replace_eq>,
- HelpText<"Alias for -thinlto-prefix-replace=">;
+ HelpText<"Alias for --thinlto-prefix-replace=">;
// Ignore LTO plugin-related options.
// clang -flto passes -plugin and -plugin-opt to the linker. This is required
// --version output.
defm plugin: Eq<"plugin", "Ignored for compatibility with GNU linkers">;
-def plugin_opt_fresolution_eq: J<"plugin-opt=-fresolution=">;
-def plugin_opt_pass_through_eq: J<"plugin-opt=-pass-through=">;
-def plugin_opt_thinlto: J<"plugin-opt=thinlto">;
-def plugin_opt_slash: J<"plugin-opt=/">;
+def plugin_opt_eq_minus: J<"plugin-opt=-">,
+ HelpText<"Specify an LLVM option for compatibility with LLVMgold.so">;
+def: J<"plugin-opt=thinlto">;
+
+// Ignore GCC collect2 LTO plugin related options. Note that we don't support
+// GCC LTO, but GCC collect2 passes these options even in non-LTO mode.
+def: J<"plugin-opt=-fresolution=">;
+def: J<"plugin-opt=-pass-through=">;
+// This may be either an unhandled LLVMgold.so feature or GCC passed
+// -plugin-opt=path/to/{liblto_plugin.so,lto-wrapper}
+def plugin_opt_eq : J<"plugin-opt=">;
// Options listed below are silently ignored for now for compatibility.
def: F<"detect-odr-violations">;
def: F<"no-ctors-in-init-array">;
def: F<"no-keep-memory">;
def: F<"no-pipeline-knowledge">;
+def: F<"no-relax">;
def: F<"no-warn-mismatch">;
def: Flag<["-"], "p">;
def: Separate<["--", "-"], "rpath-link">;
#include "Target.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/SHA1.h"
#include <regex>
using namespace llvm::object;
using namespace llvm::support::endian;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
uint8_t *Out::bufferStart;
uint8_t Out::first;
PhdrEntry *Out::tlsPhdr;
OutputSection *Out::initArray;
OutputSection *Out::finiArray;
-std::vector<OutputSection *> outputSections;
+std::vector<OutputSection *> elf::outputSections;
uint32_t OutputSection::getPhdrFlags() const {
uint32_t ret = 0;
// 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.
+//
+// NOTE: clang since rL252300 emits SHT_X86_64_UNWIND .eh_frame sections. Allow
+// them to be merged into SHT_PROGBITS .eh_frame (GNU as .cfi_*).
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;
+ type == SHT_NOTE ||
+ (type == SHT_X86_64_UNWIND && config->emachine == EM_X86_64);
}
// Record that isec will be placed in the OutputSection. isec does not become
in[i] = v[i].second;
}
-uint64_t getHeaderSize() {
+uint64_t elf::getHeaderSize() {
if (config->oFormatBinary)
return 0;
return Out::elfHeader->size + Out::programHeaders->size;
sortByOrder(isd->sections, order);
}
+static void nopInstrFill(uint8_t *buf, size_t size) {
+ if (size == 0)
+ return;
+ unsigned i = 0;
+ if (size == 0)
+ return;
+ std::vector<std::vector<uint8_t>> nopFiller = *target->nopInstrs;
+ unsigned num = size / nopFiller.back().size();
+ for (unsigned c = 0; c < num; ++c) {
+ memcpy(buf + i, nopFiller.back().data(), nopFiller.back().size());
+ i += nopFiller.back().size();
+ }
+ unsigned remaining = size - i;
+ if (!remaining)
+ return;
+ assert(nopFiller[remaining - 1].size() == remaining);
+ memcpy(buf + i, nopFiller[remaining - 1].data(), remaining);
+}
+
// Fill [Buf, Buf + Size) with Filler.
// This is used for linker script "=fillexp" command.
static void fill(uint8_t *buf, size_t size,
end = buf + size;
else
end = buf + sections[i + 1]->outSecOff;
- fill(start, end - start, filler);
+ if (isec->nopFiller) {
+ assert(target->nopInstrs);
+ nopInstrFill(start, end - start);
+ } else
+ fill(start, end - start, filler);
}
});
}
void OutputSection::finalize() {
- std::vector<InputSection *> v = getInputSections(this);
- InputSection *first = v.empty() ? nullptr : v[0];
+ InputSection *first = getFirstInputSection(this);
if (flags & SHF_LINK_ORDER) {
// We must preserve the link order dependency of sections with the
// If an input string is in the form of "foo.N" where N is a number,
// return N. Otherwise, returns 65536, which is one greater than the
// lowest priority.
-int getPriority(StringRef s) {
+int elf::getPriority(StringRef s) {
size_t pos = s.rfind('.');
if (pos == StringRef::npos)
return 65536;
return v;
}
-std::vector<InputSection *> getInputSections(OutputSection *os) {
+InputSection *elf::getFirstInputSection(const OutputSection *os) {
+ for (BaseCommand *base : os->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(base))
+ if (!isd->sections.empty())
+ return isd->sections[0];
+ return nullptr;
+}
+
+std::vector<InputSection *> elf::getInputSections(const OutputSection *os) {
std::vector<InputSection *> ret;
for (BaseCommand *base : os->sectionCommands)
if (auto *isd = dyn_cast<InputSectionDescription>(base))
template void OutputSection::maybeCompress<ELF32BE>();
template void OutputSection::maybeCompress<ELF64LE>();
template void OutputSection::maybeCompress<ELF64BE>();
-
-} // namespace elf
-} // namespace lld
int getPriority(StringRef s);
-std::vector<InputSection *> getInputSections(OutputSection* os);
+InputSection *getFirstInputSection(const OutputSection *os);
+std::vector<InputSection *> getInputSections(const OutputSection *os);
// All output sections that are handled by the linker specially are
// globally accessible. Writer initializes them, so don't use them
// Represents a relocation type, such as R_X86_64_PC32 or R_ARM_THM_CALL.
using RelType = uint32_t;
+using JumpModType = uint32_t;
// List of target-independent relocation types. Relocations read
// from files are converted to these types so that the main code
R_AARCH64_PAGE_PC,
R_AARCH64_RELAX_TLS_GD_TO_IE_PAGE_PC,
R_AARCH64_TLSDESC_PAGE,
+ R_ARM_PCA,
R_ARM_SBREL,
R_MIPS_GOTREL,
R_MIPS_GOT_GP,
Symbol *sym;
};
+// Manipulate jump instructions with these modifiers. These are used to relax
+// jump instruction opcodes at basic block boundaries and are particularly
+// useful when basic block sections are enabled.
+struct JumpInstrMod {
+ JumpModType original;
+ uint64_t offset;
+ unsigned size;
+};
+
// This function writes undefined symbol diagnostics to an internal buffer.
// Call reportUndefinedSymbols() after calling scanRelocations() to emit
// the diagnostics.
template <class ELFT> void reportUndefinedSymbols();
+void hexagonTLSSymbolUpdate(ArrayRef<OutputSection *> outputSections);
+bool hexagonNeedsTLSSymbol(ArrayRef<OutputSection *> outputSections);
+
class ThunkSection;
class Thunk;
struct InputSectionDescription;
#include "llvm/ADT/Twine.h"
using namespace llvm;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
// Returns a whole line containing the current token.
StringRef ScriptLexer::getLine() {
StringRef s = getCurrentMB().getBuffer();
}
std::string ScriptLexer::getCurrentLocation() {
- std::string filename = getCurrentMB().getBufferIdentifier();
+ std::string filename = std::string(getCurrentMB().getBufferIdentifier());
return (filename + ":" + Twine(getLineNumber())).str();
}
break;
}
- // Get a token before the opreator.
+ // Get a token before the operator.
if (e != 0)
ret.push_back(s.substr(0, e));
return mb;
llvm_unreachable("getCurrentMB: failed to find a token");
}
-
-} // namespace elf
-} // namespace lld
bool inExpr = false;
size_t pos = 0;
+protected:
+ MemoryBufferRef getCurrentMB();
+
private:
void maybeSplitExpr();
StringRef getLine();
size_t getLineNumber();
size_t getColumnNumber();
-
- MemoryBufferRef getCurrentMB();
};
} // namespace elf
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/ScopedPrinter.h"
#include <cassert>
#include <limits>
#include <vector>
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
class ScriptParser final : ScriptLexer {
public:
OutputSection *readOutputSectionDescription(StringRef outSec);
std::vector<BaseCommand *> readOverlay();
std::vector<StringRef> readOutputSectionPhdrs();
+ std::pair<uint64_t, uint64_t> readInputSectionFlags();
InputSectionDescription *readInputSectionDescription(StringRef tok);
StringMatcher readFilePatterns();
std::vector<SectionPattern> readInputSectionsList();
- InputSectionDescription *readInputSectionRules(StringRef filePattern);
+ InputSectionDescription *readInputSectionRules(StringRef filePattern,
+ uint64_t withFlags,
+ uint64_t withoutFlags);
unsigned readPhdrType();
SortSectionPolicy readSortKind();
SymbolAssignment *readProvideHidden(bool provide, bool hidden);
Expr readConstant();
Expr getPageSize();
- uint64_t readMemoryAssignment(StringRef, StringRef, StringRef);
+ Expr readMemoryAssignment(StringRef, StringRef, StringRef);
std::pair<uint32_t, uint32_t> readMemoryAttributes();
Expr combine(StringRef op, Expr l, Expr r);
}
void ScriptParser::readDynamicList() {
- config->hasDynamicList = true;
expect("{");
std::vector<SymbolVersion> locals;
std::vector<SymbolVersion> globals;
}
if (s.startswith("/")) {
+ // Case 1: s is an absolute path. Just open it.
driver->addFile(s, /*withLOption=*/false);
} else if (s.startswith("=")) {
+ // Case 2: relative to the sysroot.
if (config->sysroot.empty())
driver->addFile(s.substr(1), /*withLOption=*/false);
else
driver->addFile(saver.save(config->sysroot + "/" + s.substr(1)),
/*withLOption=*/false);
} else if (s.startswith("-l")) {
+ // Case 3: search in the list of library paths.
driver->addLibrary(s.substr(2));
- } else if (sys::fs::exists(s)) {
- driver->addFile(s, /*withLOption=*/false);
} else {
- if (Optional<std::string> path = findFromSearchPaths(s))
- driver->addFile(saver.save(*path), /*withLOption=*/true);
- else
- setError("unable to find " + s);
+ // Case 4: s is a relative path. Search in the directory of the script file.
+ std::string filename = std::string(getCurrentMB().getBufferIdentifier());
+ StringRef directory = sys::path::parent_path(filename);
+ if (!directory.empty()) {
+ SmallString<0> path(directory);
+ sys::path::append(path, s);
+ if (sys::fs::exists(path)) {
+ driver->addFile(path, /*withLOption=*/false);
+ return;
+ }
+ }
+ // Then search in the current working directory.
+ if (sys::fs::exists(s)) {
+ driver->addFile(s, /*withLOption=*/false);
+ } else {
+ // Finally, search in the list of library paths.
+ if (Optional<std::string> path = findFromSearchPaths(s))
+ driver->addFile(saver.save(*path), /*withLOption=*/true);
+ else
+ setError("unable to find " + s);
+ }
}
}
.Case("elf64-tradlittlemips", {ELF64LEKind, EM_MIPS})
.Case("elf32-littleriscv", {ELF32LEKind, EM_RISCV})
.Case("elf64-littleriscv", {ELF64LEKind, EM_RISCV})
+ .Case("elf64-sparc", {ELF64BEKind, EM_SPARCV9})
.Default({ELFNoneKind, EM_NONE});
}
void ScriptParser::readOutputFormat() {
expect("(");
- StringRef name = unquote(next());
- StringRef s = name;
+ config->bfdname = unquote(next());
+ StringRef s = config->bfdname;
if (s.consume_back("-freebsd"))
config->osabi = ELFOSABI_FREEBSD;
std::tie(config->ekind, config->emachine) = parseBfdName(s);
if (config->emachine == EM_NONE)
- setError("unknown output format name: " + name);
+ setError("unknown output format name: " + config->bfdname);
if (s == "elf32-ntradlittlemips" || s == "elf32-ntradbigmips")
config->mipsN32Abi = true;
}
void ScriptParser::readSections() {
- script->hasSectionsCommand = true;
-
- // -no-rosegment is used to avoid placing read only non-executable sections in
- // their own segment. We do the same if SECTIONS command is present in linker
- // script. See comment for computeFlags().
- config->singleRoRx = true;
-
expect("{");
std::vector<BaseCommand *> v;
while (!errorCount() && !consume("}")) {
else
v.push_back(readOutputSectionDescription(tok));
}
+ script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
+ v.end());
- if (!atEOF() && consume("INSERT")) {
- std::vector<BaseCommand *> *dest = nullptr;
- if (consume("AFTER"))
- dest = &script->insertAfterCommands[next()];
- else if (consume("BEFORE"))
- dest = &script->insertBeforeCommands[next()];
- else
- setError("expected AFTER/BEFORE, but got '" + next() + "'");
- if (dest)
- dest->insert(dest->end(), v.begin(), v.end());
+ if (atEOF() || !consume("INSERT")) {
+ script->hasSectionsCommand = true;
return;
}
- script->sectionCommands.insert(script->sectionCommands.end(), v.begin(),
- v.end());
+ bool isAfter = false;
+ if (consume("AFTER"))
+ isAfter = true;
+ else if (!consume("BEFORE"))
+ setError("expected AFTER/BEFORE, but got '" + next() + "'");
+ StringRef where = next();
+ for (BaseCommand *cmd : v)
+ if (auto *os = dyn_cast<OutputSection>(cmd))
+ script->insertCommands.push_back({os, isAfter, where});
}
void ScriptParser::readTarget() {
}
StringMatcher ScriptParser::readFilePatterns() {
- std::vector<StringRef> v;
+ StringMatcher Matcher;
+
while (!errorCount() && !consume(")"))
- v.push_back(next());
- return StringMatcher(v);
+ Matcher.addPattern(SingleStringMatcher(next()));
+ return Matcher;
}
SortSectionPolicy ScriptParser::readSortKind() {
excludeFilePat = readFilePatterns();
}
- std::vector<StringRef> v;
+ StringMatcher SectionMatcher;
while (!errorCount() && peek() != ")" && peek() != "EXCLUDE_FILE")
- v.push_back(unquote(next()));
+ SectionMatcher.addPattern(unquote(next()));
- if (!v.empty())
- ret.push_back({std::move(excludeFilePat), StringMatcher(v)});
+ if (!SectionMatcher.empty())
+ ret.push_back({std::move(excludeFilePat), std::move(SectionMatcher)});
else
setError("section pattern is expected");
}
//
// <section-list> is parsed by readInputSectionsList().
InputSectionDescription *
-ScriptParser::readInputSectionRules(StringRef filePattern) {
- auto *cmd = make<InputSectionDescription>(filePattern);
+ScriptParser::readInputSectionRules(StringRef filePattern, uint64_t withFlags,
+ uint64_t withoutFlags) {
+ auto *cmd =
+ make<InputSectionDescription>(filePattern, withFlags, withoutFlags);
expect("(");
while (!errorCount() && !consume(")")) {
ScriptParser::readInputSectionDescription(StringRef tok) {
// Input section wildcard can be surrounded by KEEP.
// https://sourceware.org/binutils/docs/ld/Input-Section-Keep.html#Input-Section-Keep
+ uint64_t withFlags = 0;
+ uint64_t withoutFlags = 0;
if (tok == "KEEP") {
expect("(");
- StringRef filePattern = next();
- InputSectionDescription *cmd = readInputSectionRules(filePattern);
+ if (consume("INPUT_SECTION_FLAGS"))
+ std::tie(withFlags, withoutFlags) = readInputSectionFlags();
+ InputSectionDescription *cmd =
+ readInputSectionRules(next(), withFlags, withoutFlags);
expect(")");
script->keptSections.push_back(cmd);
return cmd;
}
- return readInputSectionRules(tok);
+ if (tok == "INPUT_SECTION_FLAGS") {
+ std::tie(withFlags, withoutFlags) = readInputSectionFlags();
+ tok = next();
+ }
+ return readInputSectionRules(tok, withFlags, withoutFlags);
}
void ScriptParser::readSort() {
script->createOutputSection(next(), getCurrentLocation());
cmd->inOverlay = true;
expect("{");
- while (!errorCount() && !consume("}"))
- cmd->sectionCommands.push_back(readInputSectionRules(next()));
- cmd->phdrs = readOutputSectionPhdrs();
+ while (!errorCount() && !consume("}")) {
+ uint64_t withFlags = 0;
+ uint64_t withoutFlags = 0;
+ if (consume("INPUT_SECTION_FLAGS"))
+ std::tie(withFlags, withoutFlags) = readInputSectionFlags();
+ cmd->sectionCommands.push_back(
+ readInputSectionRules(next(), withFlags, withoutFlags));
+ }
return cmd;
}
// We handle the FILL command as an alias for =fillexp section attribute,
// which is different from what GNU linkers do.
// https://sourceware.org/binutils/docs/ld/Output-Section-Data.html
- expect("(");
+ if (peek() != "(")
+ setError("( expected, but got " + peek());
cmd->filler = readFill();
- expect(")");
} else if (tok == "SORT") {
readSort();
} else if (tok == "INCLUDE") {
// We have a file name and no input sections description. It is not a
// commonly used syntax, but still acceptable. In that case, all sections
// from the file will be included.
+ // FIXME: GNU ld permits INPUT_SECTION_FLAGS to be used here. We do not
+ // handle this case here as it will already have been matched by the
+ // case above.
auto *isd = make<InputSectionDescription>(tok);
- isd->sectionPatterns.push_back({{}, StringMatcher({"*"})});
+ isd->sectionPatterns.push_back({{}, StringMatcher("*")});
cmd->sectionCommands.push_back(isd);
}
}
if (consume(">"))
- cmd->memoryRegionName = next();
+ cmd->memoryRegionName = std::string(next());
if (consume("AT")) {
expect(">");
- cmd->lmaRegionName = next();
+ cmd->lmaRegionName = std::string(next());
}
if (cmd->lmaExpr && !cmd->lmaRegionName.empty())
// When reading a hexstring, ld.bfd handles it as a blob of arbitrary
// size, while ld.gold always handles it as a 32-bit big-endian number.
// We are compatible with ld.gold because it's easier to implement.
+// Also, we require that expressions with operators must be wrapped into
+// round brackets. We did it to resolve the ambiguity when parsing scripts like:
+// SECTIONS { .foo : { ... } =120+3 /DISCARD/ : { ... } }
std::array<uint8_t, 4> ScriptParser::readFill() {
- uint64_t value = readExpr()().val;
+ uint64_t value = readPrimary()().val;
if (value > UINT32_MAX)
setError("filler expression result does not fit 32-bit: 0x" +
Twine::utohexstr(value));
return make<ByteCommand>(e, size, commandString);
}
+static llvm::Optional<uint64_t> parseFlag(StringRef tok) {
+ if (llvm::Optional<uint64_t> asInt = parseInt(tok))
+ return asInt;
+#define CASE_ENT(enum) #enum, ELF::enum
+ return StringSwitch<llvm::Optional<uint64_t>>(tok)
+ .Case(CASE_ENT(SHF_WRITE))
+ .Case(CASE_ENT(SHF_ALLOC))
+ .Case(CASE_ENT(SHF_EXECINSTR))
+ .Case(CASE_ENT(SHF_MERGE))
+ .Case(CASE_ENT(SHF_STRINGS))
+ .Case(CASE_ENT(SHF_INFO_LINK))
+ .Case(CASE_ENT(SHF_LINK_ORDER))
+ .Case(CASE_ENT(SHF_OS_NONCONFORMING))
+ .Case(CASE_ENT(SHF_GROUP))
+ .Case(CASE_ENT(SHF_TLS))
+ .Case(CASE_ENT(SHF_COMPRESSED))
+ .Case(CASE_ENT(SHF_EXCLUDE))
+ .Case(CASE_ENT(SHF_ARM_PURECODE))
+ .Default(None);
+#undef CASE_ENT
+}
+
+// Reads the '(' <flags> ')' list of section flags in
+// INPUT_SECTION_FLAGS '(' <flags> ')' in the
+// following form:
+// <flags> ::= <flag>
+// | <flags> & flag
+// <flag> ::= Recognized Flag Name, or Integer value of flag.
+// If the first character of <flag> is a ! then this means without flag,
+// otherwise with flag.
+// Example: SHF_EXECINSTR & !SHF_WRITE means with flag SHF_EXECINSTR and
+// without flag SHF_WRITE.
+std::pair<uint64_t, uint64_t> ScriptParser::readInputSectionFlags() {
+ uint64_t withFlags = 0;
+ uint64_t withoutFlags = 0;
+ expect("(");
+ while (!errorCount()) {
+ StringRef tok = unquote(next());
+ bool without = tok.consume_front("!");
+ if (llvm::Optional<uint64_t> flag = parseFlag(tok)) {
+ if (without)
+ withoutFlags |= *flag;
+ else
+ withFlags |= *flag;
+ } else {
+ setError("unrecognised flag: " + tok);
+ }
+ if (consume(")"))
+ break;
+ if (!consume("&")) {
+ next();
+ setError("expected & or )");
+ }
+ }
+ return std::make_pair(withFlags, withoutFlags);
+}
+
StringRef ScriptParser::readParenLiteral() {
expect("(");
bool orig = inExpr;
setError("memory region not defined: " + name);
return [] { return 0; };
}
- return [=] { return script->memoryRegions[name]->length; };
+ return script->memoryRegions[name]->length;
}
if (tok == "LOADADDR") {
StringRef name = readParenLiteral();
setError("memory region not defined: " + name);
return [] { return 0; };
}
- return [=] { return script->memoryRegions[name]->origin; };
+ return script->memoryRegions[name]->origin;
}
if (tok == "SEGMENT_START") {
expect("(");
return [=] { return cmd->size; };
}
if (tok == "SIZEOF_HEADERS")
- return [=] { return getHeaderSize(); };
+ return [=] { return elf::getHeaderSize(); };
// Tok is the dot.
if (tok == ".")
// as a parent. This version hierarchy is, probably against your
// instinct, purely for hint; the runtime doesn't care about it
// at all. In LLD, we simply ignore it.
- if (peek() != ";")
- skip();
- expect(";");
+ if (next() != ";")
+ expect(";");
}
-static bool hasWildcard(StringRef s) {
+bool elf::hasWildcard(StringRef s) {
return s.find_first_of("?*[") != StringRef::npos;
}
return ret;
}
-uint64_t ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2,
- StringRef s3) {
+Expr ScriptParser::readMemoryAssignment(StringRef s1, StringRef s2,
+ StringRef s3) {
if (!consume(s1) && !consume(s2) && !consume(s3)) {
setError("expected one of: " + s1 + ", " + s2 + ", or " + s3);
- return 0;
+ return [] { return 0; };
}
expect("=");
- return readExpr()().getValue();
+ return readExpr();
}
// Parse the MEMORY command as specified in:
}
expect(":");
- uint64_t origin = readMemoryAssignment("ORIGIN", "org", "o");
+ Expr origin = readMemoryAssignment("ORIGIN", "org", "o");
expect(",");
- uint64_t length = readMemoryAssignment("LENGTH", "len", "l");
+ Expr length = readMemoryAssignment("LENGTH", "len", "l");
// Add the memory region to the region map.
MemoryRegion *mr = make<MemoryRegion>(tok, origin, length, flags, negFlags);
return {flags, negFlags};
}
-void readLinkerScript(MemoryBufferRef mb) {
+void elf::readLinkerScript(MemoryBufferRef mb) {
ScriptParser(mb).readLinkerScript();
}
-void readVersionScript(MemoryBufferRef mb) {
+void elf::readVersionScript(MemoryBufferRef mb) {
ScriptParser(mb).readVersionScript();
}
-void readDynamicList(MemoryBufferRef mb) { ScriptParser(mb).readDynamicList(); }
+void elf::readDynamicList(MemoryBufferRef mb) {
+ ScriptParser(mb).readDynamicList();
+}
-void readDefsym(StringRef name, MemoryBufferRef mb) {
+void elf::readDefsym(StringRef name, MemoryBufferRef mb) {
ScriptParser(mb).readDefsym(name);
}
-
-} // namespace elf
-} // namespace lld
// Parses the defsym expression.
void readDefsym(StringRef name, MemoryBufferRef mb);
+bool hasWildcard(StringRef s);
+
} // namespace elf
} // namespace lld
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
-SymbolTable *symtab;
+SymbolTable *elf::symtab;
void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
// Swap symbols as instructed by -wrap.
idx2 = idx1;
idx1 = idx3;
- // Now renaming is complete. No one refers Real symbol. We could leave
- // Real as-is, but if Real is written to the symbol table, that may
- // contain irrelevant values. So, we copy all values from Sym to Real.
- StringRef s = real->getName();
+ if (real->exportDynamic)
+ sym->exportDynamic = true;
+
+ // Now renaming is complete, and no one refers to real. We drop real from
+ // .symtab and .dynsym. If real is undefined, it is important that we don't
+ // leave it in .dynsym, because otherwise it might lead to an undefined symbol
+ // error in a subsequent link. If real is defined, we could emit real as an
+ // alias for sym, but that could degrade the user experience of some tools
+ // that can print out only one symbol for each location: sym is a preferred
+ // name than real, but they might print out real instead.
memcpy(real, sym, sizeof(SymbolUnion));
- real->setName(s);
+ real->isUsedInRegularObj = false;
}
// Find an existing symbol or create a new one.
}
Symbol *SymbolTable::addSymbol(const Symbol &newSym) {
- Symbol *sym = symtab->insert(newSym.getName());
+ Symbol *sym = insert(newSym.getName());
sym->resolve(newSym);
return sym;
}
return sym;
}
+// A version script/dynamic list is only meaningful for a Defined symbol.
+// A CommonSymbol will be converted to a Defined in replaceCommonSymbols().
+// A lazy symbol may be made Defined if an LTO libcall fetches it.
+static bool canBeVersioned(const Symbol &sym) {
+ return sym.isDefined() || sym.isCommon() || sym.isLazy();
+}
+
// Initialize demangledSyms with a map from demangled symbols to symbol
// objects. Used to handle "extern C++" directive in version scripts.
//
StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
if (!demangledSyms) {
demangledSyms.emplace();
- for (Symbol *sym : symVector) {
- if (!sym->isDefined() && !sym->isCommon())
- continue;
- (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
- }
+ for (Symbol *sym : symVector)
+ if (canBeVersioned(*sym))
+ (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
}
return *demangledSyms;
}
std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) {
if (ver.isExternCpp)
return getDemangledSyms().lookup(ver.name);
- if (Symbol *b = find(ver.name))
- if (b->isDefined() || b->isCommon())
- return {b};
+ if (Symbol *sym = find(ver.name))
+ if (canBeVersioned(*sym))
+ return {sym};
return {};
}
std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
std::vector<Symbol *> res;
- StringMatcher m(ver.name);
+ SingleStringMatcher m(ver.name);
if (ver.isExternCpp) {
for (auto &p : getDemangledSyms())
}
for (Symbol *sym : symVector)
- if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName()))
+ if (canBeVersioned(*sym) && m.match(sym->getName()))
res.push_back(sym);
return res;
}
// --dynamic-list.
handleDynamicList();
}
-
-} // namespace elf
-} // namespace lld
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include <cstring>
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
// Returns a symbol for an error message.
static std::string demangle(StringRef symName) {
if (elf::config->demangle)
return demangleItanium(symName);
- return symName;
+ return std::string(symName);
}
-std::string toString(const elf::Symbol &b) { return demangle(b.getName()); }
-std::string toELFString(const Archive::Symbol &b) {
+std::string lld::toString(const elf::Symbol &sym) {
+ StringRef name = sym.getName();
+ std::string ret = demangle(name);
+
+ // If sym has a non-default version, its name may have been truncated at '@'
+ // by Symbol::parseSymbolVersion(). Add the trailing part. This check is safe
+ // because every symbol name ends with '\0'.
+ if (name.data()[name.size()] == '@')
+ ret += name.data() + name.size();
+ return ret;
+}
+
+std::string lld::toELFString(const Archive::Symbol &b) {
return demangle(b.getName());
}
-namespace elf {
Defined *ElfSym::bss;
-Defined *ElfSym::data;
Defined *ElfSym::etext1;
Defined *ElfSym::etext2;
Defined *ElfSym::edata1;
Defined *ElfSym::relaIpltEnd;
Defined *ElfSym::riscvGlobalPointer;
Defined *ElfSym::tlsModuleBase;
+DenseMap<const Symbol *, const InputFile *> elf::backwardReferences;
static uint64_t getSymVA(const Symbol &sym, int64_t &addend) {
switch (sym.kind()) {
// MIPS relocatable files can mix regular and microMIPS code.
// Linker needs to distinguish such code. To do so microMIPS
// symbols has the `STO_MIPS_MICROMIPS` flag in the `st_other`
- // field. Unfortunately, the `MIPS::relocateOne()` method has
+ // field. Unfortunately, the `MIPS::relocate()` method has
// a symbol value only. To pass type of the symbol (regular/microMIPS)
// to that routine as well as other places where we write
// a symbol value as-is (.dynamic section, `Elf_Ehdr::e_entry`
if (config->relocatable)
return binding;
if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
- versionId == VER_NDX_LOCAL)
+ (versionId == VER_NDX_LOCAL && isDefined()))
return STB_LOCAL;
if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
return STB_GLOBAL;
}
// Print out a log message for --trace-symbol.
-void printTraceSymbol(const Symbol *sym) {
+void elf::printTraceSymbol(const Symbol *sym) {
std::string s;
if (sym->isUndefined())
s = ": reference to ";
message(toString(sym->file) + s + sym->getName());
}
-void maybeWarnUnorderableSymbol(const Symbol *sym) {
+void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
if (!config->warnSymbolOrdering)
return;
// 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.
-bool computeIsPreemptible(const Symbol &sym) {
+bool elf::computeIsPreemptible(const Symbol &sym) {
assert(!sym.isLocal());
// Only symbols with default visibility that appear in dynsym can be
if (!config->shared)
return false;
- // If the dynamic list is present, it specifies preemptable symbols in a DSO.
- if (config->hasDynamicList)
+ // If -Bsymbolic or --dynamic-list is specified, or -Bsymbolic-functions is
+ // specified and the symbol is STT_FUNC, the symbol is preemptible iff it is
+ // in the dynamic list.
+ if (config->symbolic || (config->bsymbolicFunctions && sym.isFunc()))
return sym.inDynamicList;
-
- // -Bsymbolic means that definitions are not preempted.
- if (config->bsymbolic || (config->bsymbolicFunctions && sym.isFunc()))
- return false;
return true;
}
+void elf::reportBackrefs() {
+ for (auto &it : backwardReferences) {
+ const Symbol &sym = *it.first;
+ warn("backward reference detected: " + sym.getName() + " in " +
+ toString(it.second) + " refers to " + toString(sym.file));
+ }
+}
+
static uint8_t getMinVisibility(uint8_t va, uint8_t vb) {
if (va == STV_DEFAULT)
return vb;
// group assignment rule simulates the traditional linker's semantics.
bool backref = config->warnBackrefs && other.file &&
file->groupId < other.file->groupId;
+ if (backref) {
+ // Some libraries have known problems and can cause noise. Filter them out
+ // with --warn-backrefs-exclude=.
+ StringRef name =
+ !file->archiveName.empty() ? file->archiveName : file->getName();
+ for (const llvm::GlobPattern &pat : config->warnBackrefsExclude)
+ if (pat.match(name)) {
+ backref = false;
+ break;
+ }
+ }
fetch();
// We don't report backward references to weak symbols as they can be
// overridden later.
+ //
+ // A traditional linker does not error for -ldef1 -lref -ldef2 (linking
+ // sandwich), where def2 may or may not be the same as def1. We don't want
+ // to warn for this case, so dismiss the warning if we see a subsequent lazy
+ // definition.
if (backref && !isWeak())
- warn("backward reference detected: " + other.getName() + " in " +
- toString(other.file) + " refers to " + toString(file));
+ backwardReferences.try_emplace(this, other.file);
return;
}
// reference is weak.
if (other.binding != STB_WEAK || !referenced)
binding = other.binding;
- referenced = true;
}
}
}
template <class LazyT> void Symbol::resolveLazy(const LazyT &other) {
- if (!isUndefined())
+ if (!isUndefined()) {
+ // See the comment in resolveUndefined().
+ if (isDefined())
+ backwardReferences.erase(this);
return;
+ }
// An undefined weak will not fetch archive members. See comment on Lazy in
// Symbols.h for the details.
uint8_t bind = binding;
replace(other);
binding = bind;
- referenced = true;
- }
+ } else if (traced)
+ printTraceSymbol(&other);
}
-
-} // namespace elf
-} // namespace lld
#include "InputSection.h"
#include "lld/Common/LLVM.h"
#include "lld/Common/Strings.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
namespace lld {
+// Returns a string representation for a symbol for diagnostics.
std::string toString(const elf::Symbol &);
// There are two different ways to convert an Archive::Symbol to a string:
uint8_t isPreemptible : 1;
// True if an undefined or shared symbol is used from a live section.
+ //
+ // NOTE: In Writer.cpp the field is used to mark local defined symbols
+ // which are referenced by relocations when -r or --emit-relocs is given.
uint8_t used : 1;
// True if a call to this symbol needs to be followed by a restore of the
// __bss_start
static Defined *bss;
- // __data_start
- static Defined *data;
-
// etext and _etext
static Defined *etext1;
static Defined *etext2;
void Symbol::replace(const Symbol &newSym) {
using llvm::ELF::STT_TLS;
- // Symbols representing thread-local variables must be referenced by
- // TLS-aware relocations, and non-TLS symbols must be reference by
- // non-TLS relocations, so there's a clear distinction between TLS
- // and non-TLS symbols. It is an error if the same symbol is defined
- // as a TLS symbol in one file and as a non-TLS symbol in other file.
- if (symbolKind != PlaceholderKind && !isLazy() && !newSym.isLazy() &&
- (type == STT_TLS) != (newSym.type == STT_TLS))
+ // st_value of STT_TLS represents the assigned offset, not the actual address
+ // which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can only be
+ // referenced by special TLS relocations. It is usually an error if a STT_TLS
+ // symbol is replaced by a non-STT_TLS symbol, vice versa. There are two
+ // exceptions: (a) a STT_NOTYPE lazy/undefined symbol can be replaced by a
+ // STT_TLS symbol, (b) a STT_TLS undefined symbol can be replaced by a
+ // STT_NOTYPE lazy symbol.
+ if (symbolKind != PlaceholderKind && !newSym.isLazy() &&
+ (type == STT_TLS) != (newSym.type == STT_TLS) &&
+ type != llvm::ELF::STT_NOTYPE)
error("TLS attribute mismatch: " + toString(*this) + "\n>>> defined in " +
toString(newSym.file) + "\n>>> defined in " + toString(file));
void maybeWarnUnorderableSymbol(const Symbol *sym);
bool computeIsPreemptible(const Symbol &sym);
+void reportBackrefs();
+
+// A mapping from a symbol to an InputFile referencing it backward. Used by
+// --warn-backrefs.
+extern llvm::DenseMap<const Symbol *, const InputFile *> backwardReferences;
} // namespace elf
} // namespace lld
// Try to merge two GOTs. In case of success the `Dst` contains
// result of merging and the function returns true. In case of
- // ovwerflow the `Dst` is unchanged and the function returns false.
+ // overflow the `Dst` is unchanged and the function returns false.
bool tryMergeGots(FileGot & dst, FileGot & src, bool isPrimary);
};
size_t getNumEntries() const { return entries.size(); }
size_t headerSize;
- size_t footerSize = 0;
std::vector<const Symbol *> entries;
};
void addEntry(Symbol &sym);
};
+class PPC32GlinkSection : public PltSection {
+public:
+ PPC32GlinkSection();
+ void writeTo(uint8_t *buf) override;
+ size_t getSize() const override;
+
+ std::vector<const Symbol *> canonical_plts;
+ static constexpr size_t footerSize = 64;
+};
+
// This is x86-only.
class IBTPltSection : public SyntheticSection {
public:
std::vector<InputSection *> exidxSections;
private:
- size_t size;
+ size_t size = 0;
// Instead of storing pointers to the .ARM.exidx InputSections from
// InputObjects, we store pointers to the executable sections that need
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-std::string toString(elf::RelType type) {
+const TargetInfo *elf::target;
+
+std::string lld::toString(RelType type) {
StringRef s = getELFRelocationTypeName(elf::config->emachine, type);
if (s == "Unknown")
return ("Unknown (" + Twine(type) + ")").str();
- return s;
+ return std::string(s);
}
-namespace elf {
-const TargetInfo *target;
-
-TargetInfo *getTarget() {
+TargetInfo *elf::getTarget() {
switch (config->emachine) {
case EM_386:
case EM_IAMCU:
return {};
}
-ErrorPlace getErrorPlace(const uint8_t *loc) {
+ErrorPlace elf::getErrorPlace(const uint8_t *loc) {
switch (config->ekind) {
case ELF32LEKind:
return getErrPlace<ELF32LE>(loc);
return expr;
}
-void TargetInfo::relaxGot(uint8_t *loc, RelType type, uint64_t val) const {
+void TargetInfo::relaxGot(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToLe(uint8_t *loc, RelType type,
+void TargetInfo::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsGdToIe(uint8_t *loc, RelType type,
+void TargetInfo::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsIeToLe(uint8_t *loc, RelType type,
+void TargetInfo::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
-void TargetInfo::relaxTlsLdToLe(uint8_t *loc, RelType type,
+void TargetInfo::relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
uint64_t val) const {
llvm_unreachable("Should not have claimed to be relaxable");
}
return *config->imageBase;
return config->isPic ? 0 : defaultImageBase;
}
-
-} // namespace elf
-} // namespace lld
virtual bool inBranchRange(RelType type, uint64_t src,
uint64_t dst) const;
- virtual void relocateOne(uint8_t *loc, RelType type, uint64_t val) const = 0;
+ virtual void relocate(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const = 0;
+ void relocateNoSym(uint8_t *loc, RelType type, uint64_t val) const {
+ relocate(loc, Relocation{R_NONE, type, 0, 0, nullptr}, val);
+ }
+
+ virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type,
+ JumpModType val) const {}
virtual ~TargetInfo();
+ // This deletes a jump insn at the end of the section if it is a fall thru to
+ // the next section. Further, if there is a conditional jump and a direct
+ // jump consecutively, it tries to flip the conditional jump to convert the
+ // direct jump into a fall thru and delete it. Returns true if a jump
+ // instruction can be deleted.
+ virtual bool deleteFallThruJmpInsn(InputSection &is, InputFile *file,
+ InputSection *nextIS) const {
+ return false;
+ }
+
unsigned defaultCommonPageSize = 4096;
unsigned defaultMaxPageSize = 4096;
// executable OutputSections.
std::array<uint8_t, 4> trapInstr;
+ // Stores the NOP instructions of different sizes for the target and is used
+ // to pad sections that are relaxed.
+ llvm::Optional<std::vector<std::vector<uint8_t>>> nopInstrs;
+
// If a target needs to rewrite calls to __morestack to instead call
// __morestack_non_split when a split-stack enabled caller calls a
// non-split-stack callee this will return true. Otherwise returns false.
virtual RelExpr adjustRelaxExpr(RelType type, const uint8_t *data,
RelExpr expr) const;
- virtual void relaxGot(uint8_t *loc, RelType type, uint64_t val) const;
- virtual void relaxTlsGdToIe(uint8_t *loc, RelType type, uint64_t val) const;
- virtual void relaxTlsGdToLe(uint8_t *loc, RelType type, uint64_t val) const;
- virtual void relaxTlsIeToLe(uint8_t *loc, RelType type, uint64_t val) const;
- virtual void relaxTlsLdToLe(uint8_t *loc, RelType type, uint64_t val) const;
+ virtual void relaxGot(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
+ virtual void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
+ virtual void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
+ virtual void relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
+ virtual void relaxTlsLdToLe(uint8_t *loc, const Relocation &rel,
+ uint64_t val) const;
protected:
// On FreeBSD x86_64 the first page cannot be mmaped.
void writePPC32GlinkSection(uint8_t *buf, size_t numEntries);
-bool tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel,
- uint8_t *bufLoc);
+bool tryRelaxPPC64TocIndirection(const Relocation &rel, uint8_t *bufLoc);
unsigned getPPCDFormOp(unsigned secondaryOp);
// In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first
// the .toc section.
bool isPPC64SmallCodeModelTocReloc(RelType type);
+void addPPC64SaveRestore();
uint64_t getPPC64TocBase();
uint64_t getAArch64Page(uint64_t expr);
template <class ELFT> bool isMipsPIC(const Defined *sym);
-static inline void reportRangeError(uint8_t *loc, RelType type, const Twine &v,
- int64_t min, uint64_t max) {
- ErrorPlace errPlace = getErrorPlace(loc);
- StringRef hint;
- if (errPlace.isec && errPlace.isec->name.startswith(".debug"))
- hint = "; consider recompiling with -fdebug-types-section to reduce size "
- "of debug sections";
-
- errorOrWarn(errPlace.loc + "relocation " + lld::toString(type) +
- " out of range: " + v.str() + " is not in [" + Twine(min).str() +
- ", " + Twine(max).str() + "]" + hint);
-}
+void reportRangeError(uint8_t *loc, const Relocation &rel, const Twine &v,
+ int64_t min, uint64_t max);
// Make sure that V can be represented as an N bit signed integer.
-inline void checkInt(uint8_t *loc, int64_t v, int n, RelType type) {
+inline void checkInt(uint8_t *loc, int64_t v, int n, const Relocation &rel) {
if (v != llvm::SignExtend64(v, n))
- reportRangeError(loc, type, Twine(v), llvm::minIntN(n), llvm::maxIntN(n));
+ reportRangeError(loc, rel, Twine(v), llvm::minIntN(n), llvm::maxIntN(n));
}
// Make sure that V can be represented as an N bit unsigned integer.
-inline void checkUInt(uint8_t *loc, uint64_t v, int n, RelType type) {
+inline void checkUInt(uint8_t *loc, uint64_t v, int n, const Relocation &rel) {
if ((v >> n) != 0)
- reportRangeError(loc, type, Twine(v), 0, llvm::maxUIntN(n));
+ reportRangeError(loc, rel, Twine(v), 0, llvm::maxUIntN(n));
}
// Make sure that V can be represented as an N bit signed or unsigned integer.
-inline void checkIntUInt(uint8_t *loc, uint64_t v, int n, RelType type) {
+inline void checkIntUInt(uint8_t *loc, uint64_t v, int n,
+ const Relocation &rel) {
// For the error message we should cast V to a signed integer so that error
// messages show a small negative value rather than an extremely large one
if (v != (uint64_t)llvm::SignExtend64(v, n) && (v >> n) != 0)
- reportRangeError(loc, type, Twine((int64_t)v), llvm::minIntN(n),
+ reportRangeError(loc, rel, Twine((int64_t)v), llvm::minIntN(n),
llvm::maxUIntN(n));
}
-inline void checkAlignment(uint8_t *loc, uint64_t v, int n, RelType type) {
+inline void checkAlignment(uint8_t *loc, uint64_t v, int n,
+ const Relocation &rel) {
if ((v & (n - 1)) != 0)
error(getErrorLocation(loc) + "improper alignment for relocation " +
- lld::toString(type) + ": 0x" + llvm::utohexstr(v) +
+ lld::toString(rel.type) + ": 0x" + llvm::utohexstr(v) +
" is not aligned to " + Twine(n) + " bytes");
}
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
-
-namespace lld {
-namespace elf {
+using namespace lld;
+using namespace lld::elf;
namespace {
void addSymbols(ThunkSection &isec) override;
};
+// PPC64 R2 Save Stub
+// When the caller requires a valid R2 TOC pointer but the callee does not
+// require a TOC pointer and the callee cannot guarantee that it doesn't
+// clobber R2 then we need to save R2. This stub:
+// 1) Saves the TOC pointer to the stack.
+// 2) Tail calls the callee.
+class PPC64R2SaveStub final : public Thunk {
+public:
+ PPC64R2SaveStub(Symbol &dest) : Thunk(dest, 0) {}
+ uint32_t size() override { return 8; }
+ void writeTo(uint8_t *buf) override;
+ void addSymbols(ThunkSection &isec) override;
+};
+
// A bl instruction uses a signed 24 bit offset, with an implicit 4 byte
// alignment. This gives a possible 26 bits of 'reach'. If the call offset is
// larger then that we need to emit a long-branch thunk. The target address
};
uint64_t s = getAArch64ThunkDestVA(destination, addend);
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 8, R_AARCH64_ABS64, s);
+ target->relocateNoSym(buf + 8, R_AARCH64_ABS64, s);
}
void AArch64ABSLongThunk::addSymbols(ThunkSection &isec) {
uint64_t s = getAArch64ThunkDestVA(destination, addend);
uint64_t p = getThunkTargetSym()->getVA();
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_AARCH64_ADR_PREL_PG_HI21,
- getAArch64Page(s) - getAArch64Page(p));
- target->relocateOne(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s);
+ target->relocateNoSym(buf, R_AARCH64_ADR_PREL_PG_HI21,
+ getAArch64Page(s) - getAArch64Page(p));
+ target->relocateNoSym(buf + 4, R_AARCH64_ADD_ABS_LO12_NC, s);
}
void AArch64ADRPThunk::addSymbols(ThunkSection &isec) {
0x00, 0x00, 0x00, 0xea, // b S
};
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_JUMP24, offset);
+ target->relocateNoSym(buf, R_ARM_JUMP24, offset);
}
bool ARMThunk::isCompatibleWith(const InputSection &isec,
0x00, 0xf0, 0x00, 0xb0, // b.w S
};
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_THM_JUMP24, offset);
+ target->relocateNoSym(buf, R_ARM_THM_JUMP24, offset);
}
bool ThumbThunk::isCompatibleWith(const InputSection &isec,
};
uint64_t s = getARMThunkDestVA(destination);
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_MOVW_ABS_NC, s);
- target->relocateOne(buf + 4, R_ARM_MOVT_ABS, s);
+ target->relocateNoSym(buf, R_ARM_MOVW_ABS_NC, s);
+ target->relocateNoSym(buf + 4, R_ARM_MOVT_ABS, s);
}
void ARMV7ABSLongThunk::addSymbols(ThunkSection &isec) {
};
uint64_t s = getARMThunkDestVA(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);
+ target->relocateNoSym(buf, R_ARM_THM_MOVW_ABS_NC, s);
+ target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_ABS, s);
}
void ThumbV7ABSLongThunk::addSymbols(ThunkSection &isec) {
uint64_t p = getThunkTargetSym()->getVA();
int64_t offset = s - p - 16;
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_MOVW_PREL_NC, offset);
- target->relocateOne(buf + 4, R_ARM_MOVT_PREL, offset);
+ target->relocateNoSym(buf, R_ARM_MOVW_PREL_NC, offset);
+ target->relocateNoSym(buf + 4, R_ARM_MOVT_PREL, offset);
}
void ARMV7PILongThunk::addSymbols(ThunkSection &isec) {
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
int64_t offset = s - p - 12;
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf, R_ARM_THM_MOVW_PREL_NC, offset);
- target->relocateOne(buf + 4, R_ARM_THM_MOVT_PREL, offset);
+ target->relocateNoSym(buf, R_ARM_THM_MOVW_PREL_NC, offset);
+ target->relocateNoSym(buf + 4, R_ARM_THM_MOVT_PREL, offset);
}
void ThumbV7PILongThunk::addSymbols(ThunkSection &isec) {
0x00, 0x00, 0x00, 0x00, // L1: .word S
};
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
+ target->relocateNoSym(buf + 4, R_ARM_ABS32, getARMThunkDestVA(destination));
}
void ARMV5ABSLongThunk::addSymbols(ThunkSection &isec) {
uint64_t s = getARMThunkDestVA(destination);
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12);
+ target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
}
void ARMV5PILongThunk::addSymbols(ThunkSection &isec) {
};
uint64_t s = getARMThunkDestVA(destination);
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 8, R_ARM_ABS32, s);
+ target->relocateNoSym(buf + 8, R_ARM_ABS32, s);
}
void ThumbV6MABSLongThunk::addSymbols(ThunkSection &isec) {
uint64_t s = getARMThunkDestVA(destination);
uint64_t p = getThunkTargetSym()->getVA() & ~0x1;
memcpy(buf, data, sizeof(data));
- target->relocateOne(buf + 12, R_ARM_REL32, s - p - 12);
+ target->relocateNoSym(buf + 12, R_ARM_REL32, s - p - 12);
}
void ThumbV6MPILongThunk::addSymbols(ThunkSection &isec) {
write32(buf + 4, 0x08000000 | (s >> 2)); // j func
write32(buf + 8, 0x27390000); // addiu $25, $25, %lo(func)
write32(buf + 12, 0x00000000); // nop
- target->relocateOne(buf, R_MIPS_HI16, s);
- target->relocateOne(buf + 8, R_MIPS_LO16, s);
+ target->relocateNoSym(buf, R_MIPS_HI16, s);
+ target->relocateNoSym(buf + 8, R_MIPS_LO16, s);
}
void MipsThunk::addSymbols(ThunkSection &isec) {
write16(buf + 4, 0xd400); // j func
write16(buf + 8, 0x3339); // addiu $25, $25, %lo(func)
write16(buf + 12, 0x0c00); // nop
- target->relocateOne(buf, R_MICROMIPS_HI16, s);
- target->relocateOne(buf + 4, R_MICROMIPS_26_S1, s);
- target->relocateOne(buf + 8, R_MICROMIPS_LO16, s);
+ target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
+ target->relocateNoSym(buf + 4, R_MICROMIPS_26_S1, s);
+ target->relocateNoSym(buf + 8, R_MICROMIPS_LO16, s);
}
void MicroMipsThunk::addSymbols(ThunkSection &isec) {
write16(buf, 0x1320); // lui $25, %hi(func)
write16(buf + 4, 0x3339); // addiu $25, $25, %lo(func)
write16(buf + 8, 0x9400); // bc func
- target->relocateOne(buf, R_MICROMIPS_HI16, s);
- target->relocateOne(buf + 4, R_MICROMIPS_LO16, s);
- target->relocateOne(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12);
+ target->relocateNoSym(buf, R_MICROMIPS_HI16, s);
+ target->relocateNoSym(buf + 4, R_MICROMIPS_LO16, s);
+ target->relocateNoSym(buf + 8, R_MICROMIPS_PC26_S1, s - p - 12);
}
void MicroMipsR6Thunk::addSymbols(ThunkSection &isec) {
return dyn_cast<InputSection>(dr.section);
}
-void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
- const InputFile *file, int64_t addend) {
+void elf::writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA,
+ const InputFile *file, int64_t addend) {
if (!config->isPic) {
write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha
write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11)
write32(buf + 4, 0x4e800420); // bctr
}
-void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) {
+void elf::writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) {
uint16_t offHa = (offset + 0x8000) >> 16;
uint16_t offLo = offset & 0xffff;
s->file = destination.file;
}
+void PPC64R2SaveStub::writeTo(uint8_t *buf) {
+ int64_t offset = destination.getVA() - (getThunkTargetSym()->getVA() + 4);
+ // The branch offset needs to fit in 26 bits.
+ if (!isInt<26>(offset))
+ fatal("R2 save stub branch offset is too large: " + Twine(offset));
+ write32(buf + 0, 0xf8410018); // std r2,24(r1)
+ write32(buf + 4, 0x48000000 | (offset & 0x03fffffc)); // b <offset>
+}
+
+void PPC64R2SaveStub::addSymbols(ThunkSection &isec) {
+ Defined *s = addSymbol(saver.save("__toc_save_" + destination.getName()),
+ STT_FUNC, 0, isec);
+ s->needsTocRestore = true;
+}
+
void PPC64LongBranchThunk::writeTo(uint8_t *buf) {
int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) -
getPPC64TocBase();
Thunk::~Thunk() = default;
static Thunk *addThunkAArch64(RelType type, Symbol &s, int64_t a) {
- if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26)
+ if (type != R_AARCH64_CALL26 && type != R_AARCH64_JUMP26 &&
+ type != R_AARCH64_PLT32)
fatal("unrecognized relocation type");
if (config->picThunk)
return make<AArch64ADRPThunk>(s, a);
}
static Thunk *addThunkPPC64(RelType type, Symbol &s, int64_t a) {
- assert(type == R_PPC64_REL24 && "unexpected relocation type for thunk");
+ assert((type == R_PPC64_REL14 || type == R_PPC64_REL24) &&
+ "unexpected relocation type for thunk");
if (s.isInPlt())
return make<PPC64PltCallStub>(s);
+ // This check looks at the st_other bits of the callee. If the value is 1
+ // then the callee clobbers the TOC and we need an R2 save stub.
+ if ((s.stOther >> 5) == 1)
+ return make<PPC64R2SaveStub>(s);
+
if (config->picThunk)
return make<PPC64PILongBranchThunk>(s, a);
return make<PPC64PDLongBranchThunk>(s, a);
}
-Thunk *addThunk(const InputSection &isec, Relocation &rel) {
+Thunk *elf::addThunk(const InputSection &isec, Relocation &rel) {
Symbol &s = *rel.sym;
int64_t a = rel.addend;
llvm_unreachable("add Thunk only supported for ARM, Mips and PowerPC");
}
-
-} // end namespace elf
-} // end namespace lld
#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/RandomNumberGenerator.h"
#include "llvm/Support/SHA1.h"
+#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/xxhash.h"
#include <climits>
+#define DEBUG_TYPE "lld"
+
using namespace llvm;
using namespace llvm::ELF;
using namespace llvm::object;
using namespace llvm::support;
using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::elf;
-namespace lld {
-namespace elf {
namespace {
// The writer writes a SymbolTable result to a file.
template <class ELFT> class Writer {
void sortSections();
void resolveShfLinkOrder();
void finalizeAddressDependentContent();
+ void optimizeBasicBlockJumps();
void sortInputSections();
void finalizeSections();
void checkExecuteOnly();
return name.startswith(prefix) || name == prefix.drop_back();
}
-StringRef getOutputSectionName(const InputSectionBase *s) {
+StringRef elf::getOutputSectionName(const InputSectionBase *s) {
if (config->relocatable)
return s->name;
}
}
- // This check is for -z keep-text-section-prefix. This option separates text
- // sections with prefix ".text.hot", ".text.unlikely", ".text.startup" or
- // ".text.exit".
- // When enabled, this allows identifying the hot code region (.text.hot) in
- // the final binary which can be selectively mapped to huge pages or mlocked,
- // for instance.
+ // A BssSection created for a common symbol is identified as "COMMON" in
+ // linker scripts. It should go to .bss section.
+ if (s->name == "COMMON")
+ return ".bss";
+
+ if (script->hasSectionsCommand)
+ return s->name;
+
+ // When no SECTIONS is specified, emulate GNU ld's internal linker scripts
+ // by grouping sections with certain prefixes.
+
+ // GNU ld places text sections with prefix ".text.hot.", ".text.unknown.",
+ // ".text.unlikely.", ".text.startup." or ".text.exit." before others.
+ // We provide an option -z keep-text-section-prefix to group such sections
+ // into separate output sections. This is more flexible. See also
+ // sortISDBySectionOrder().
+ // ".text.unknown" means the hotness of the section is unknown. When
+ // SampleFDO is used, if a function doesn't have sample, it could be very
+ // cold or it could be a new function never being sampled. Those functions
+ // will be kept in the ".text.unknown" section.
if (config->zKeepTextSectionPrefix)
- for (StringRef v :
- {".text.hot.", ".text.unlikely.", ".text.startup.", ".text.exit."})
+ for (StringRef v : {".text.hot.", ".text.unknown.", ".text.unlikely.",
+ ".text.startup.", ".text.exit."})
if (isSectionPrefix(v, s->name))
return v.drop_back();
for (StringRef v :
{".text.", ".rodata.", ".data.rel.ro.", ".data.", ".bss.rel.ro.",
".bss.", ".init_array.", ".fini_array.", ".ctors.", ".dtors.", ".tbss.",
- ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab.",
- ".openbsd.randomdata."})
+ ".gcc_except_table.", ".tdata.", ".ARM.exidx.", ".ARM.extab."})
if (isSectionPrefix(v, s->name))
return v.drop_back();
- // CommonSection is identified as "COMMON" in linker scripts.
- // By default, it should go to .bss section.
- if (s->name == "COMMON")
- return ".bss";
-
return s->name;
}
!config->dynamicLinker.empty() && script->needsInterpSection();
}
-template <class ELFT> void writeResult() { Writer<ELFT>().run(); }
+template <class ELFT> void elf::writeResult() {
+ llvm::TimeTraceScope timeScope("Write output file");
+ Writer<ELFT>().run();
+}
static void removeEmptyPTLoad(std::vector<PhdrEntry *> &phdrs) {
- llvm::erase_if(phdrs, [&](const PhdrEntry *p) {
- if (p->p_type != PT_LOAD)
- return false;
- if (!p->firstSec)
- return true;
- uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr;
- return size == 0;
- });
+ auto it = std::stable_partition(
+ phdrs.begin(), phdrs.end(), [&](const PhdrEntry *p) {
+ if (p->p_type != PT_LOAD)
+ return true;
+ if (!p->firstSec)
+ return false;
+ uint64_t size = p->lastSec->addr + p->lastSec->size - p->firstSec->addr;
+ return size != 0;
+ });
+
+ // Clear OutputSection::ptLoad for sections contained in removed
+ // segments.
+ DenseSet<PhdrEntry *> removed(it, phdrs.end());
+ for (OutputSection *sec : outputSections)
+ if (removed.count(sec->ptLoad))
+ sec->ptLoad = nullptr;
+ phdrs.erase(it, phdrs.end());
}
-void copySectionsIntoPartitions() {
+void elf::copySectionsIntoPartitions() {
std::vector<InputSectionBase *> newSections;
for (unsigned part = 2; part != partitions.size() + 1; ++part) {
for (InputSectionBase *s : inputSections) {
newSections.end());
}
-void combineEhSections() {
+void elf::combineEhSections() {
for (InputSectionBase *&s : inputSections) {
// Ignore dead sections and the partition end marker (.part.end),
// whose partition number is out of bounds.
// The linker is expected to define some symbols depending on
// the linking result. This function defines such symbols.
-void addReservedSymbols() {
+void elf::addReservedSymbols() {
if (config->emachine == EM_MIPS) {
// Define _gp for MIPS. st_value of _gp symbol will be updated by Writer
// so that it points to an absolute address which by default is relative
// glibc *crt1.o has a undefined reference to _SDA_BASE_. Since we don't
// support Small Data Area, define it arbitrarily as 0.
addOptionalRegular("_SDA_BASE_", nullptr, 0, STV_HIDDEN);
+ } else if (config->emachine == EM_PPC64) {
+ addPPC64SaveRestore();
}
// The Power Architecture 64-bit v2 ABI defines a TableOfContents (TOC) which
};
ElfSym::bss = add("__bss_start", 0);
- ElfSym::data = add("__data_start", 0);
ElfSym::end1 = add("end", -1);
ElfSym::end2 = add("_end", -1);
ElfSym::etext1 = add("etext", -1);
return nullptr;
}
-template <class ELFT> void createSyntheticSections() {
+template <class ELFT> void elf::createSyntheticSections() {
// Initialize all pointers with NULL. This is needed because
// you can call lld::elf::main more than once as a library.
memset(&Out::first, 0, sizeof(Out));
add(in.ibtPlt);
}
- in.plt = make<PltSection>();
+ in.plt = config->emachine == EM_PPC ? make<PPC32GlinkSection>()
+ : make<PltSection>();
add(in.plt);
in.iplt = make<IpltSection>();
add(in.iplt);
// The main function of the writer.
template <class ELFT> void Writer<ELFT>::run() {
- if (config->discard != DiscardPolicy::All)
- copyLocalSymbols();
+ copyLocalSymbols();
if (config->copyRelocs)
addSectionSymbols();
for (OutputSection *sec : outputSections)
sec->addr = 0;
+ // Handle --print-map(-M)/--Map, --cref and --print-archive-stats=. Dump them
+ // before checkSections() because the files may be useful in case
+ // checkSections() or openFile() fails, for example, due to an erroneous file
+ // size.
+ writeMapFile();
+ writeCrossReferenceTable();
+ writeArchiveStats();
+
if (config->checkSections)
checkSections();
if (errorCount())
return;
- // Handle -Map and -cref options.
- writeMapFile();
- writeCrossReferenceTable();
- if (errorCount())
- return;
-
if (auto e = buffer->commit())
error("failed to write to the output file: " + toString(std::move(e)));
}
+template <class ELFT, class RelTy>
+static void markUsedLocalSymbolsImpl(ObjFile<ELFT> *file,
+ llvm::ArrayRef<RelTy> rels) {
+ for (const RelTy &rel : rels) {
+ Symbol &sym = file->getRelocTargetSym(rel);
+ if (sym.isLocal())
+ sym.used = true;
+ }
+}
+
+// The function ensures that the "used" field of local symbols reflects the fact
+// that the symbol is used in a relocation from a live section.
+template <class ELFT> static void markUsedLocalSymbols() {
+ // With --gc-sections, the field is already filled.
+ // See MarkLive<ELFT>::resolveReloc().
+ if (config->gcSections)
+ return;
+ // Without --gc-sections, the field is initialized with "true".
+ // Drop the flag first and then rise for symbols referenced in relocations.
+ for (InputFile *file : objectFiles) {
+ ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
+ for (Symbol *b : f->getLocalSymbols())
+ b->used = false;
+ for (InputSectionBase *s : f->getSections()) {
+ InputSection *isec = dyn_cast_or_null<InputSection>(s);
+ if (!isec)
+ continue;
+ if (isec->type == SHT_REL)
+ markUsedLocalSymbolsImpl(f, isec->getDataAs<typename ELFT::Rel>());
+ else if (isec->type == SHT_RELA)
+ markUsedLocalSymbolsImpl(f, isec->getDataAs<typename ELFT::Rela>());
+ }
+ }
+}
+
static bool shouldKeepInSymtab(const Defined &sym) {
if (sym.isSection())
return false;
- if (config->discard == DiscardPolicy::None)
+ // If --emit-reloc or -r is given, preserve symbols referenced by relocations
+ // from live sections.
+ if (config->copyRelocs && sym.used)
return true;
- // If -emit-reloc is given, all symbols including local ones need to be
- // copied because they may be referenced by relocations.
- if (config->emitRelocs)
+ // Exclude local symbols pointing to .ARM.exidx sections.
+ // They are probably mapping symbols "$d", which are optional for these
+ // sections. After merging the .ARM.exidx sections, some of these symbols
+ // may become dangling. The easiest way to avoid the issue is not to add
+ // them to the symbol table from the beginning.
+ if (config->emachine == EM_ARM && sym.section &&
+ sym.section->type == SHT_ARM_EXIDX)
+ return false;
+
+ if (config->discard == DiscardPolicy::None)
return true;
+ if (config->discard == DiscardPolicy::All)
+ return false;
// In ELF assembly .L symbols are normally discarded by the assembler.
// If the assembler fails to do so, the linker discards them if
template <class ELFT> void Writer<ELFT>::copyLocalSymbols() {
if (!in.symTab)
return;
+ if (config->copyRelocs && config->discard != DiscardPolicy::None)
+ markUsedLocalSymbols<ELFT>();
for (InputFile *file : objectFiles) {
ObjFile<ELFT> *f = cast<ObjFile<ELFT>>(file);
for (Symbol *b : f->getLocalSymbols()) {
- if (!b->isLocal())
- fatal(toString(f) +
- ": broken object: getLocalSymbols returns a non-local symbol");
+ assert(b->isLocal() && "should have been caught in initializeSymbols()");
auto *dr = dyn_cast<Defined>(b);
// No reason to keep local undefined symbol in symtab.
// However, if "-z now" is given, the lazy symbol resolution is
// disabled, which enables us to put it into RELRO.
if (sec == in.gotPlt->getParent())
-#ifndef __OpenBSD__
return config->zNow;
-#else
- return true; /* kbind(2) means we can always put these in RELRO */
-#endif
// .dynamic section contains data for the dynamic linker, and
// there's no need to write to it at runtime, so it's better to put
StringRef s = sec->name;
return s == ".data.rel.ro" || s == ".bss.rel.ro" || s == ".ctors" ||
s == ".dtors" || s == ".jcr" || s == ".eh_frame" ||
- s == ".openbsd.randomdata";
+ s == ".fini_array" || s == ".init_array" ||
+ s == ".openbsd.randomdata" || s == ".preinit_array";
}
// We compute a rank for each section. The rank indicates where the
if (ElfSym::bss)
ElfSym::bss->section = findSection(".bss");
- if (ElfSym::data)
- ElfSym::data->section = findSection(".data");
-
// Setup MIPS _gp_disp/__gnu_local_gp symbols which should
// be equal to the _gp symbol's value.
if (ElfSym::mipsGp) {
return i;
}
+// Adds random priorities to sections not already in the map.
+static void maybeShuffle(DenseMap<const InputSectionBase *, int> &order) {
+ if (!config->shuffleSectionSeed)
+ return;
+
+ std::vector<int> priorities(inputSections.size() - order.size());
+ // Existing priorities are < 0, so use priorities >= 0 for the missing
+ // sections.
+ int curPrio = 0;
+ for (int &prio : priorities)
+ prio = curPrio++;
+ uint32_t seed = *config->shuffleSectionSeed;
+ std::mt19937 g(seed ? seed : std::random_device()());
+ llvm::shuffle(priorities.begin(), priorities.end(), g);
+ int prioIndex = 0;
+ for (InputSectionBase *sec : inputSections) {
+ if (order.try_emplace(sec, priorities[prioIndex]).second)
+ ++prioIndex;
+ }
+}
+
// Builds section order for handling --symbol-ordering-file.
static DenseMap<const InputSectionBase *, int> buildSectionOrder() {
DenseMap<const InputSectionBase *, int> sectionOrder;
const DenseMap<const InputSectionBase *, int> &order) {
StringRef name = sec->name;
+ // Never sort these.
+ if (name == ".init" || name == ".fini")
+ return;
+
+ // Sort input sections by priority using the list provided by
+ // --symbol-ordering-file or --shuffle-sections=. This is a least significant
+ // digit radix sort. The sections may be sorted stably again by a more
+ // significant key.
+ if (!order.empty())
+ for (BaseCommand *b : sec->sectionCommands)
+ if (auto *isd = dyn_cast<InputSectionDescription>(b))
+ sortISDBySectionOrder(isd, order);
+
// Sort input sections by section name suffixes for
// __attribute__((init_priority(N))).
if (name == ".init_array" || name == ".fini_array") {
return;
}
- // Never sort these.
- if (name == ".init" || name == ".fini")
- return;
-
// .toc is allocated just after .got and is accessed using GOT-relative
// relocations. Object files compiled with small code model have an
// addressable range of [.got, .got + 0xFFFC] for GOT-relative relocations.
});
return;
}
-
- // Sort input sections by priority using the list provided
- // by --symbol-ordering-file.
- if (!order.empty())
- for (BaseCommand *b : sec->sectionCommands)
- if (auto *isd = dyn_cast<InputSectionDescription>(b))
- sortISDBySectionOrder(isd, order);
}
// If no layout was provided by linker script, we want to apply default
template <class ELFT> void Writer<ELFT>::sortInputSections() {
// Build the order once since it is expensive.
DenseMap<const InputSectionBase *, int> order = buildSectionOrder();
+ maybeShuffle(order);
for (BaseCommand *base : script->sectionCommands)
if (auto *sec = dyn_cast<OutputSection>(base))
sortSection(sec, order);
llvm::find_if(script->sectionCommands, isSection),
llvm::find_if(llvm::reverse(script->sectionCommands), isSection).base(),
compareSections);
+
+ // Process INSERT commands. From this point onwards the order of
+ // script->sectionCommands is fixed.
+ script->processInsertCommands();
return;
}
+ script->processInsertCommands();
+
// Orphan sections are sections present in the input files which are
// not explicitly placed into the output file by the linker script.
//
OutputSection *bOut = lb->getParent();
if (aOut != bOut)
- return aOut->sectionIndex < bOut->sectionIndex;
+ return aOut->addr < bOut->addr;
return la->outSecOff < lb->outSecOff;
}
}
}
+static void finalizeSynthetic(SyntheticSection *sec) {
+ if (sec && sec->isNeeded() && sec->getParent())
+ sec->finalizeContents();
+}
+
// We need to generate and finalize the content that depends on the address of
// InputSections. As the generation of the content may also alter InputSection
// addresses we must converge to a fixed point. We do that here. See the comment
AArch64Err843419Patcher a64p;
ARMErr657417Patcher a32p;
script->assignAddresses();
+ // .ARM.exidx and SHF_LINK_ORDER do not require precise addresses, but they
+ // do require the relative addresses of OutputSections because linker scripts
+ // can assign Virtual Addresses to OutputSections that are not monotonically
+ // increasing.
+ for (Partition &part : partitions)
+ finalizeSynthetic(part.armExidx);
+ resolveShfLinkOrder();
+
+ // Converts call x@GDPLT to call __tls_get_addr
+ if (config->emachine == EM_HEXAGON)
+ hexagonTLSSymbolUpdate(outputSections);
int assignPasses = 0;
for (;;) {
}
}
}
+
+ // If addrExpr is set, the address may not be a multiple of the alignment.
+ // Warn because this is error-prone.
+ for (BaseCommand *cmd : script->sectionCommands)
+ if (auto *os = dyn_cast<OutputSection>(cmd))
+ if (os->addr % os->alignment != 0)
+ warn("address (0x" + Twine::utohexstr(os->addr) + ") of section " +
+ os->name + " is not a multiple of alignment (" +
+ Twine(os->alignment) + ")");
+}
+
+// If Input Sections have been shrinked (basic block sections) then
+// update symbol values and sizes associated with these sections. With basic
+// block sections, input sections can shrink when the jump instructions at
+// the end of the section are relaxed.
+static void fixSymbolsAfterShrinking() {
+ for (InputFile *File : objectFiles) {
+ parallelForEach(File->getSymbols(), [&](Symbol *Sym) {
+ auto *def = dyn_cast<Defined>(Sym);
+ if (!def)
+ return;
+
+ const SectionBase *sec = def->section;
+ if (!sec)
+ return;
+
+ const InputSectionBase *inputSec = dyn_cast<InputSectionBase>(sec->repl);
+ if (!inputSec || !inputSec->bytesDropped)
+ return;
+
+ const size_t OldSize = inputSec->data().size();
+ const size_t NewSize = OldSize - inputSec->bytesDropped;
+
+ if (def->value > NewSize && def->value <= OldSize) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "Moving symbol " << Sym->getName() << " from "
+ << def->value << " to "
+ << def->value - inputSec->bytesDropped << " bytes\n");
+ def->value -= inputSec->bytesDropped;
+ return;
+ }
+
+ if (def->value + def->size > NewSize && def->value <= OldSize &&
+ def->value + def->size <= OldSize) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "Shrinking symbol " << Sym->getName() << " from "
+ << def->size << " to " << def->size - inputSec->bytesDropped
+ << " bytes\n");
+ def->size -= inputSec->bytesDropped;
+ }
+ });
+ }
}
-static void finalizeSynthetic(SyntheticSection *sec) {
- if (sec && sec->isNeeded() && sec->getParent())
- sec->finalizeContents();
+// If basic block sections exist, there are opportunities to delete fall thru
+// jumps and shrink jump instructions after basic block reordering. This
+// relaxation pass does that. It is only enabled when --optimize-bb-jumps
+// option is used.
+template <class ELFT> void Writer<ELFT>::optimizeBasicBlockJumps() {
+ assert(config->optimizeBBJumps);
+
+ script->assignAddresses();
+ // For every output section that has executable input sections, this
+ // does the following:
+ // 1. Deletes all direct jump instructions in input sections that
+ // jump to the following section as it is not required.
+ // 2. If there are two consecutive jump instructions, it checks
+ // if they can be flipped and one can be deleted.
+ for (OutputSection *os : outputSections) {
+ if (!(os->flags & SHF_EXECINSTR))
+ continue;
+ std::vector<InputSection *> sections = getInputSections(os);
+ std::vector<unsigned> result(sections.size());
+ // Delete all fall through jump instructions. Also, check if two
+ // consecutive jump instructions can be flipped so that a fall
+ // through jmp instruction can be deleted.
+ parallelForEachN(0, sections.size(), [&](size_t i) {
+ InputSection *next = i + 1 < sections.size() ? sections[i + 1] : nullptr;
+ InputSection &is = *sections[i];
+ result[i] =
+ target->deleteFallThruJmpInsn(is, is.getFile<ELFT>(), next) ? 1 : 0;
+ });
+ size_t numDeleted = std::count(result.begin(), result.end(), 1);
+ if (numDeleted > 0) {
+ script->assignAddresses();
+ LLVM_DEBUG(llvm::dbgs()
+ << "Removing " << numDeleted << " fall through jumps\n");
+ }
+ }
+
+ fixSymbolsAfterShrinking();
+
+ for (OutputSection *os : outputSections) {
+ std::vector<InputSection *> sections = getInputSections(os);
+ for (InputSection *is : sections)
+ is->trim();
+ }
}
// In order to allow users to manipulate linker-synthesized sections,
if (!os || ss->isNeeded())
continue;
- // If we reach here, then SS is an unused synthetic section and we want to
- // remove it from corresponding input section description of output section.
+ // If we reach here, then ss is an unused synthetic section and we want to
+ // remove it from the corresponding input section description, and
+ // orphanSections.
for (BaseCommand *b : os->sectionCommands)
if (auto *isd = dyn_cast<InputSectionDescription>(b))
llvm::erase_if(isd->sections,
[=](InputSection *isec) { return isec == ss; });
+ llvm::erase_if(script->orphanSections,
+ [=](const InputSectionBase *isec) { return isec == ss; });
}
}
// we can correctly decide if a dynamic relocation is needed. This is called
// after processSymbolAssignments() because it needs to know whether a
// linker-script-defined symbol is absolute.
+ ppc64noTocRelax.clear();
if (!config->relocatable) {
forEachRelSec(scanRelocations<ELFT>);
reportUndefinedSymbols<ELFT>();
if (sym->isUndefined() && !sym->isWeak())
if (auto *f = dyn_cast_or_null<SharedFile>(sym->file))
if (f->allNeededIsKnown)
- error(toString(f) + ": undefined reference to " + toString(*sym));
+ errorOrWarn(toString(f) + ": undefined reference to " +
+ toString(*sym) + " [--no-allow-shlib-undefined]");
}
// Now that we have defined all possible global symbols including linker-
in.mipsGot->build();
removeUnusedSyntheticSections();
+ script->diagnoseOrphanHandling();
sortSections();
sec->addrExpr = [=] { return i->second; };
}
+ // With the outputSections available check for GDPLT relocations
+ // and add __tls_get_addr symbol if needed.
+ if (config->emachine == EM_HEXAGON && hexagonNeedsTLSSymbol(outputSections)) {
+ Symbol *sym = symtab->addSymbol(Undefined{
+ nullptr, "__tls_get_addr", STB_GLOBAL, STV_DEFAULT, STT_NOTYPE});
+ sym->isPreemptible = true;
+ partitions[0].dynSymTab->addSymbol(sym);
+ }
+
// This is a bit of a hack. A value of 0 means undef, so we set it
// to 1 to make __ehdr_start defined. The section number is not
// particularly relevant.
// Dynamic section must be the last one in this list and dynamic
// symbol table section (dynSymTab) must be the first one.
for (Partition &part : partitions) {
- finalizeSynthetic(part.armExidx);
finalizeSynthetic(part.dynSymTab);
finalizeSynthetic(part.gnuHashTab);
finalizeSynthetic(part.hashTab);
if (!script->hasSectionsCommand && !config->relocatable)
fixSectionAlignments();
- // SHFLinkOrder processing must be processed after relative section placements are
- // known but before addresses are allocated.
- resolveShfLinkOrder();
- if (errorCount())
- return;
-
// This is used to:
// 1) Create "thunks":
// Jump instructions in many ISAs have small displacements, and therefore
// sometimes using forward symbol declarations. We want to set the correct
// values. They also might change after adding the thunks.
finalizeAddressDependentContent();
+ if (errorCount())
+ return;
// finalizeAddressDependentContent may have added local symbols to the static symbol table.
finalizeSynthetic(in.symTab);
finalizeSynthetic(in.ppc64LongBranchTarget);
+ // Relaxation to delete inter-basic block jumps created by basic block
+ // sections. Run after in.symTab is finalized as optimizeBasicBlockJumps
+ // can relax jump instructions based on symbol offset.
+ if (config->optimizeBBJumps)
+ optimizeBasicBlockJumps();
+
// Fill other section headers. The dynamic table is finalized
// at the end because some tags like RELSZ depend on result
// of finalizing other sections.
StringRef s = sec->name;
if (!isValidCIdentifier(s))
return;
- addOptionalRegular(saver.save("__start_" + s), sec, 0, STV_PROTECTED);
- addOptionalRegular(saver.save("__stop_" + s), sec, -1, STV_PROTECTED);
+ addOptionalRegular(saver.save("__start_" + s), sec, 0,
+ config->zStartStopVisibility);
+ addOptionalRegular(saver.save("__stop_" + s), sec, -1,
+ config->zStartStopVisibility);
}
static bool needsPtLoad(OutputSection *sec) {
// time, we don't want to create a separate load segment for the headers,
// even if the first output section has an AT or AT> attribute.
uint64_t newFlags = computeFlags(sec->getPhdrFlags());
- if (!load ||
- ((sec->lmaExpr ||
- (sec->lmaRegion && (sec->lmaRegion != load->firstSec->lmaRegion))) &&
- load->lastSec != Out::programHeaders) ||
- sec->memRegion != load->firstSec->memRegion || flags != newFlags ||
- sec == relroEnd) {
+ bool sameLMARegion =
+ load && !sec->lmaExpr && sec->lmaRegion == load->firstSec->lmaRegion;
+ if (!(load && newFlags == flags && sec != relroEnd &&
+ sec->memRegion == load->firstSec->memRegion &&
+ (sameLMARegion || load->lastSec == Out::programHeaders))) {
load = addHdr(PT_LOAD, newFlags);
flags = newFlags;
}
const PhdrEntry *prev;
auto pageAlign = [&](const PhdrEntry *p) {
OutputSection *cmd = p->firstSec;
- if (cmd && !cmd->addrExpr) {
+ if (!cmd)
+ return;
+ cmd->alignExpr = [align = cmd->alignment]() { return align; };
+ if (!cmd->addrExpr) {
// Prefer advancing to align(dot, maxPageSize) + dot%maxPageSize to avoid
// padding in the file contents.
//
}
};
-#ifdef __OpenBSD__
- // On i386, produce binaries that are compatible with our W^X implementation
- if (config->emachine == EM_386) {
- auto NXAlign = [](OutputSection *Cmd) {
- if (Cmd && !Cmd->addrExpr)
- Cmd->addrExpr = [=] {
- return alignTo(script->getDot(), 0x20000000);
- };
- };
-
- for (Partition &part : partitions) {
- PhdrEntry *firstRW = nullptr;
- for (PhdrEntry *P : part.phdrs) {
- if (P->p_type == PT_LOAD && (P->p_flags & PF_W)) {
- firstRW = P;
- break;
- }
- }
-
- if (firstRW)
- NXAlign(firstRW->firstSec);
- }
- }
-#endif
-
for (Partition &part : partitions) {
prev = nullptr;
for (const PhdrEntry *p : part.phdrs)
part.buildId->writeBuildId(buildId);
}
-template void createSyntheticSections<ELF32LE>();
-template void createSyntheticSections<ELF32BE>();
-template void createSyntheticSections<ELF64LE>();
-template void createSyntheticSections<ELF64BE>();
-
-template void writeResult<ELF32LE>();
-template void writeResult<ELF32BE>();
-template void writeResult<ELF64LE>();
-template void writeResult<ELF64BE>();
+template void elf::createSyntheticSections<ELF32LE>();
+template void elf::createSyntheticSections<ELF32BE>();
+template void elf::createSyntheticSections<ELF64LE>();
+template void elf::createSyntheticSections<ELF64BE>();
-} // namespace elf
-} // namespace lld
+template void elf::writeResult<ELF32LE>();
+template void elf::writeResult<ELF32BE>();
+template void elf::writeResult<ELF64LE>();
+template void elf::writeResult<ELF64BE>();
// placed in it.
struct PhdrEntry {
PhdrEntry(unsigned type, unsigned flags)
- : p_align(type == llvm::ELF::PT_LOAD ? config->textAlignPageSize : 0),
+ : p_align(type == llvm::ELF::PT_LOAD ? config->maxPageSize : 0),
p_type(type), p_flags(flags) {}
void add(OutputSection *sec);
--- /dev/null
+//===- X86_64.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "Target.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm::MachO;
+using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::macho;
+
+namespace {
+
+struct X86_64 : TargetInfo {
+ X86_64();
+
+ uint64_t getImplicitAddend(MemoryBufferRef, const section_64 &,
+ const relocation_info &) const override;
+ void relocateOne(uint8_t *loc, const Reloc &, uint64_t val) const override;
+
+ void writeStub(uint8_t *buf, const DylibSymbol &) const override;
+ void writeStubHelperHeader(uint8_t *buf) const override;
+ void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+ uint64_t entryAddr) const override;
+
+ void prepareSymbolRelocation(lld::macho::Symbol &, const InputSection *,
+ const Reloc &) override;
+ uint64_t getSymbolVA(const lld::macho::Symbol &, uint8_t type) const override;
+};
+
+} // namespace
+
+static std::string getErrorLocation(MemoryBufferRef mb, const section_64 &sec,
+ const relocation_info &rel) {
+ return ("invalid relocation at offset " + std::to_string(rel.r_address) +
+ " of " + sec.segname + "," + sec.sectname + " in " +
+ mb.getBufferIdentifier())
+ .str();
+}
+
+static void validateLength(MemoryBufferRef mb, const section_64 &sec,
+ const relocation_info &rel,
+ const std::vector<uint8_t> &validLengths) {
+ if (std::find(validLengths.begin(), validLengths.end(), rel.r_length) !=
+ validLengths.end())
+ return;
+
+ std::string msg = getErrorLocation(mb, sec, rel) + ": relocations of type " +
+ std::to_string(rel.r_type) + " must have r_length of ";
+ bool first = true;
+ for (uint8_t length : validLengths) {
+ if (!first)
+ msg += " or ";
+ first = false;
+ msg += std::to_string(length);
+ }
+ fatal(msg);
+}
+
+uint64_t X86_64::getImplicitAddend(MemoryBufferRef mb, const section_64 &sec,
+ const relocation_info &rel) const {
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+ const uint8_t *loc = buf + sec.offset + rel.r_address;
+ switch (rel.r_type) {
+ case X86_64_RELOC_BRANCH:
+ // XXX: ld64 also supports r_length = 0 here but I'm not sure when such a
+ // relocation will actually be generated.
+ validateLength(mb, sec, rel, {2});
+ break;
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ case X86_64_RELOC_GOT_LOAD:
+ case X86_64_RELOC_GOT:
+ if (!rel.r_pcrel)
+ fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " +
+ std::to_string(rel.r_type) + " must be pcrel");
+ validateLength(mb, sec, rel, {2});
+ break;
+ case X86_64_RELOC_UNSIGNED:
+ if (rel.r_pcrel)
+ fatal(getErrorLocation(mb, sec, rel) + ": relocations of type " +
+ std::to_string(rel.r_type) + " must not be pcrel");
+ validateLength(mb, sec, rel, {2, 3});
+ break;
+ default:
+ error("TODO: Unhandled relocation type " + std::to_string(rel.r_type));
+ return 0;
+ }
+
+ switch (rel.r_length) {
+ case 0:
+ return *loc;
+ case 1:
+ return read16le(loc);
+ case 2:
+ return read32le(loc);
+ case 3:
+ return read64le(loc);
+ default:
+ llvm_unreachable("invalid r_length");
+ }
+}
+
+void X86_64::relocateOne(uint8_t *loc, const Reloc &r, uint64_t val) const {
+ switch (r.type) {
+ case X86_64_RELOC_BRANCH:
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ case X86_64_RELOC_GOT_LOAD:
+ case X86_64_RELOC_GOT:
+ // These types are only used for pc-relative relocations, so offset by 4
+ // since the RIP has advanced by 4 at this point. This is only valid when
+ // r_length = 2, which is enforced by validateLength().
+ val -= 4;
+ break;
+ case X86_64_RELOC_UNSIGNED:
+ break;
+ default:
+ llvm_unreachable(
+ "getImplicitAddend should have flagged all unhandled relocation types");
+ }
+
+ switch (r.length) {
+ case 0:
+ *loc = val;
+ break;
+ case 1:
+ write16le(loc, val);
+ break;
+ case 2:
+ write32le(loc, val);
+ break;
+ case 3:
+ write64le(loc, val);
+ break;
+ default:
+ llvm_unreachable("invalid r_length");
+ }
+}
+
+// The following methods emit a number of assembly sequences with RIP-relative
+// addressing. Note that RIP-relative addressing on X86-64 has the RIP pointing
+// to the next instruction, not the current instruction, so we always have to
+// account for the current instruction's size when calculating offsets.
+// writeRipRelative helps with that.
+//
+// bufAddr: The virtual address corresponding to buf[0].
+// bufOff: The offset within buf of the next instruction.
+// destAddr: The destination address that the current instruction references.
+static void writeRipRelative(uint8_t *buf, uint64_t bufAddr, uint64_t bufOff,
+ uint64_t destAddr) {
+ uint64_t rip = bufAddr + bufOff;
+ // For the instructions we care about, the RIP-relative address is always
+ // stored in the last 4 bytes of the instruction.
+ write32le(buf + bufOff - 4, destAddr - rip);
+}
+
+static constexpr uint8_t stub[] = {
+ 0xff, 0x25, 0, 0, 0, 0, // jmpq *__la_symbol_ptr(%rip)
+};
+
+void X86_64::writeStub(uint8_t *buf, const DylibSymbol &sym) const {
+ memcpy(buf, stub, 2); // just copy the two nonzero bytes
+ uint64_t stubAddr = in.stubs->addr + sym.stubsIndex * sizeof(stub);
+ writeRipRelative(buf, stubAddr, sizeof(stub),
+ in.lazyPointers->addr + sym.stubsIndex * WordSize);
+}
+
+static constexpr uint8_t stubHelperHeader[] = {
+ 0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // 0x0: leaq ImageLoaderCache(%rip), %r11
+ 0x41, 0x53, // 0x7: pushq %r11
+ 0xff, 0x25, 0, 0, 0, 0, // 0x9: jmpq *dyld_stub_binder@GOT(%rip)
+ 0x90, // 0xf: nop
+};
+
+static constexpr uint8_t stubHelperEntry[] = {
+ 0x68, 0, 0, 0, 0, // 0x0: pushq <bind offset>
+ 0xe9, 0, 0, 0, 0, // 0x5: jmp <__stub_helper>
+};
+
+void X86_64::writeStubHelperHeader(uint8_t *buf) const {
+ memcpy(buf, stubHelperHeader, sizeof(stubHelperHeader));
+ writeRipRelative(buf, in.stubHelper->addr, 7, in.imageLoaderCache->getVA());
+ writeRipRelative(buf, in.stubHelper->addr, 0xf,
+ in.got->addr +
+ in.stubHelper->stubBinder->gotIndex * WordSize);
+}
+
+void X86_64::writeStubHelperEntry(uint8_t *buf, const DylibSymbol &sym,
+ uint64_t entryAddr) const {
+ memcpy(buf, stubHelperEntry, sizeof(stubHelperEntry));
+ write32le(buf + 1, sym.lazyBindOffset);
+ writeRipRelative(buf, entryAddr, sizeof(stubHelperEntry),
+ in.stubHelper->addr);
+}
+
+void X86_64::prepareSymbolRelocation(lld::macho::Symbol &sym,
+ const InputSection *isec, const Reloc &r) {
+ switch (r.type) {
+ case X86_64_RELOC_GOT_LOAD:
+ // TODO: implement mov -> lea relaxation for non-dynamic symbols
+ case X86_64_RELOC_GOT:
+ in.got->addEntry(sym);
+ break;
+ case X86_64_RELOC_BRANCH: {
+ if (auto *dysym = dyn_cast<DylibSymbol>(&sym))
+ in.stubs->addEntry(*dysym);
+ break;
+ }
+ case X86_64_RELOC_UNSIGNED: {
+ if (auto *dysym = dyn_cast<DylibSymbol>(&sym)) {
+ if (r.length != 3) {
+ error("X86_64_RELOC_UNSIGNED referencing the dynamic symbol " +
+ dysym->getName() + " must have r_length = 3");
+ return;
+ }
+ in.binding->addEntry(dysym, isec, r.offset, r.addend);
+ }
+ break;
+ }
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ break;
+ case X86_64_RELOC_SUBTRACTOR:
+ case X86_64_RELOC_TLV:
+ fatal("TODO: handle relocation type " + std::to_string(r.type));
+ break;
+ default:
+ llvm_unreachable("unexpected relocation type");
+ }
+}
+
+uint64_t X86_64::getSymbolVA(const lld::macho::Symbol &sym,
+ uint8_t type) const {
+ switch (type) {
+ case X86_64_RELOC_GOT_LOAD:
+ case X86_64_RELOC_GOT:
+ return in.got->addr + sym.gotIndex * WordSize;
+ case X86_64_RELOC_BRANCH:
+ if (auto *dysym = dyn_cast<DylibSymbol>(&sym))
+ return in.stubs->addr + dysym->stubsIndex * sizeof(stub);
+ return sym.getVA();
+ case X86_64_RELOC_UNSIGNED:
+ case X86_64_RELOC_SIGNED:
+ case X86_64_RELOC_SIGNED_1:
+ case X86_64_RELOC_SIGNED_2:
+ case X86_64_RELOC_SIGNED_4:
+ return sym.getVA();
+ case X86_64_RELOC_SUBTRACTOR:
+ case X86_64_RELOC_TLV:
+ fatal("TODO: handle relocation type " + std::to_string(type));
+ default:
+ llvm_unreachable("Unexpected relocation type");
+ }
+}
+
+X86_64::X86_64() {
+ cpuType = CPU_TYPE_X86_64;
+ cpuSubtype = CPU_SUBTYPE_X86_64_ALL;
+
+ stubSize = sizeof(stub);
+ stubHelperHeaderSize = sizeof(stubHelperHeader);
+ stubHelperEntrySize = sizeof(stubHelperEntry);
+}
+
+TargetInfo *macho::createX86_64TargetInfo() {
+ static X86_64 t;
+ return &t;
+}
--- /dev/null
+set(LLVM_TARGET_DEFINITIONS Options.td)
+tablegen(LLVM Options.inc -gen-opt-parser-defs)
+add_public_tablegen_target(MachOOptionsTableGen)
+
+add_lld_library(lldMachO2
+ Arch/X86_64.cpp
+ Driver.cpp
+ ExportTrie.cpp
+ InputFiles.cpp
+ InputSection.cpp
+ MergedOutputSection.cpp
+ OutputSection.cpp
+ OutputSegment.cpp
+ SymbolTable.cpp
+ Symbols.cpp
+ SyntheticSections.cpp
+ Target.cpp
+ Writer.cpp
+
+ LINK_COMPONENTS
+ ${LLVM_TARGETS_TO_BUILD}
+ BinaryFormat
+ Core
+ Object
+ Option
+ Support
+ TextAPI
+
+ LINK_LIBS
+ lldCommon
+ ${LLVM_PTHREAD_LIB}
+
+ DEPENDS
+ MachOOptionsTableGen
+ ${tablegen_deps}
+ )
--- /dev/null
+//===- Config.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_CONFIG_H
+#define LLD_MACHO_CONFIG_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/TextAPI/MachO/Architecture.h"
+
+#include <vector>
+
+namespace lld {
+namespace macho {
+
+class Symbol;
+struct SymbolPriorityEntry;
+
+struct Configuration {
+ Symbol *entry;
+ bool hasReexports = false;
+ llvm::StringRef installName;
+ llvm::StringRef outputFile;
+ llvm::MachO::Architecture arch;
+ llvm::MachO::HeaderFileType outputType;
+ std::vector<llvm::StringRef> librarySearchPaths;
+ // TODO: use the framework search paths
+ std::vector<llvm::StringRef> frameworkSearchPaths;
+ llvm::DenseMap<llvm::StringRef, SymbolPriorityEntry> priorities;
+};
+
+// The symbol with the highest priority should be ordered first in the output
+// section (modulo input section contiguity constraints). Using priority
+// (highest first) instead of order (lowest first) has the convenient property
+// that the default-constructed zero priority -- for symbols/sections without a
+// user-defined order -- naturally ends up putting them at the end of the
+// output.
+struct SymbolPriorityEntry {
+ // The priority given to a matching symbol, regardless of which object file
+ // it originated from.
+ size_t anyObjectFile = 0;
+ // The priority given to a matching symbol from a particular object file.
+ llvm::DenseMap<llvm::StringRef, size_t> objectFiles;
+};
+
+extern Configuration *config;
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- Driver.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Driver.h"
+#include "Config.h"
+#include "InputFiles.h"
+#include "OutputSection.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Target.h"
+#include "Writer.h"
+
+#include "lld/Common/Args.h"
+#include "lld/Common/Driver.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/LLVM.h"
+#include "lld/Common/Memory.h"
+#include "lld/Common/Version.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace llvm::sys;
+using namespace llvm::opt;
+using namespace lld;
+using namespace lld::macho;
+
+Configuration *lld::macho::config;
+
+// Create prefix string literals used in Options.td
+#define PREFIX(NAME, VALUE) const char *NAME[] = VALUE;
+#include "Options.inc"
+#undef PREFIX
+
+// Create table mapping all options defined in Options.td
+static const opt::OptTable::Info optInfo[] = {
+#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \
+ {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \
+ X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12},
+#include "Options.inc"
+#undef OPTION
+};
+
+MachOOptTable::MachOOptTable() : OptTable(optInfo) {}
+
+opt::InputArgList MachOOptTable::parse(ArrayRef<const char *> argv) {
+ // Make InputArgList from string vectors.
+ unsigned missingIndex;
+ unsigned missingCount;
+ SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size());
+
+ opt::InputArgList args = ParseArgs(vec, missingIndex, missingCount);
+
+ if (missingCount)
+ error(Twine(args.getArgString(missingIndex)) + ": missing argument");
+
+ for (opt::Arg *arg : args.filtered(OPT_UNKNOWN))
+ error("unknown argument: " + arg->getSpelling());
+ return args;
+}
+
+void MachOOptTable::printHelp(const char *argv0, bool showHidden) const {
+ PrintHelp(lld::outs(), (std::string(argv0) + " [options] file...").c_str(),
+ "LLVM Linker", showHidden);
+ lld::outs() << "\n";
+}
+
+static Optional<std::string> findLibrary(StringRef name) {
+ std::string stub = (llvm::Twine("lib") + name + ".tbd").str();
+ std::string shared = (llvm::Twine("lib") + name + ".dylib").str();
+ std::string archive = (llvm::Twine("lib") + name + ".a").str();
+ llvm::SmallString<260> location;
+
+ for (StringRef dir : config->librarySearchPaths) {
+ for (StringRef library : {stub, shared, archive}) {
+ location = dir;
+ llvm::sys::path::append(location, library);
+ if (fs::exists(location))
+ return location.str().str();
+ }
+ }
+ return {};
+}
+
+static TargetInfo *createTargetInfo(opt::InputArgList &args) {
+ StringRef arch = args.getLastArgValue(OPT_arch, "x86_64");
+ config->arch = llvm::MachO::getArchitectureFromName(
+ args.getLastArgValue(OPT_arch, arch));
+ switch (config->arch) {
+ case llvm::MachO::AK_x86_64:
+ case llvm::MachO::AK_x86_64h:
+ return createX86_64TargetInfo();
+ default:
+ fatal("missing or unsupported -arch " + arch);
+ }
+}
+
+static bool isDirectory(StringRef option, StringRef path) {
+ if (!fs::exists(path)) {
+ warn("directory not found for option -" + option + path);
+ return false;
+ } else if (!fs::is_directory(path)) {
+ warn("option -" + option + path + " references a non-directory path");
+ return false;
+ }
+ return true;
+}
+
+static void getSearchPaths(std::vector<StringRef> &paths, unsigned optionCode,
+ opt::InputArgList &args,
+ const SmallVector<StringRef, 2> &systemPaths) {
+ StringRef optionLetter{(optionCode == OPT_F ? "F" : "L")};
+ for (auto const &path : args::getStrings(args, optionCode)) {
+ if (isDirectory(optionLetter, path))
+ paths.push_back(path);
+ }
+ if (!args.hasArg(OPT_Z) && Triple(sys::getProcessTriple()).isOSDarwin()) {
+ for (auto const &path : systemPaths) {
+ if (isDirectory(optionLetter, path))
+ paths.push_back(path);
+ }
+ }
+}
+
+static void getLibrarySearchPaths(std::vector<StringRef> &paths,
+ opt::InputArgList &args) {
+ getSearchPaths(paths, OPT_L, args, {"/usr/lib", "/usr/local/lib"});
+}
+
+static void getFrameworkSearchPaths(std::vector<StringRef> &paths,
+ opt::InputArgList &args) {
+ getSearchPaths(paths, OPT_F, args,
+ {"/Library/Frameworks", "/System/Library/Frameworks"});
+}
+
+static void addFile(StringRef path) {
+ Optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer)
+ return;
+ MemoryBufferRef mbref = *buffer;
+
+ switch (identify_magic(mbref.getBuffer())) {
+ case file_magic::archive: {
+ std::unique_ptr<object::Archive> file = CHECK(
+ object::Archive::create(mbref), path + ": failed to parse archive");
+
+ if (!file->isEmpty() && !file->hasSymbolTable())
+ error(path + ": archive has no index; run ranlib to add one");
+
+ inputFiles.push_back(make<ArchiveFile>(std::move(file)));
+ break;
+ }
+ case file_magic::macho_object:
+ inputFiles.push_back(make<ObjFile>(mbref));
+ break;
+ case file_magic::macho_dynamically_linked_shared_lib:
+ inputFiles.push_back(make<DylibFile>(mbref));
+ break;
+ case file_magic::tapi_file: {
+ llvm::Expected<std::unique_ptr<llvm::MachO::InterfaceFile>> result =
+ TextAPIReader::get(mbref);
+ if (!result)
+ return;
+
+ inputFiles.push_back(make<DylibFile>(std::move(*result)));
+ break;
+ }
+ default:
+ error(path + ": unhandled file type");
+ }
+}
+
+static std::array<StringRef, 6> archNames{"arm", "arm64", "i386",
+ "x86_64", "ppc", "ppc64"};
+static bool isArchString(StringRef s) {
+ static DenseSet<StringRef> archNamesSet(archNames.begin(), archNames.end());
+ return archNamesSet.find(s) != archNamesSet.end();
+}
+
+// An order file has one entry per line, in the following format:
+//
+// <arch>:<object file>:<symbol name>
+//
+// <arch> and <object file> are optional. If not specified, then that entry
+// matches any symbol of that name.
+//
+// If a symbol is matched by multiple entries, then it takes the lowest-ordered
+// entry (the one nearest to the front of the list.)
+//
+// The file can also have line comments that start with '#'.
+void parseOrderFile(StringRef path) {
+ Optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer) {
+ error("Could not read order file at " + path);
+ return;
+ }
+
+ MemoryBufferRef mbref = *buffer;
+ size_t priority = std::numeric_limits<size_t>::max();
+ for (StringRef rest : args::getLines(mbref)) {
+ StringRef arch, objectFile, symbol;
+
+ std::array<StringRef, 3> fields;
+ uint8_t fieldCount = 0;
+ while (rest != "" && fieldCount < 3) {
+ std::pair<StringRef, StringRef> p = getToken(rest, ": \t\n\v\f\r");
+ StringRef tok = p.first;
+ rest = p.second;
+
+ // Check if we have a comment
+ if (tok == "" || tok[0] == '#')
+ break;
+
+ fields[fieldCount++] = tok;
+ }
+
+ switch (fieldCount) {
+ case 3:
+ arch = fields[0];
+ objectFile = fields[1];
+ symbol = fields[2];
+ break;
+ case 2:
+ (isArchString(fields[0]) ? arch : objectFile) = fields[0];
+ symbol = fields[1];
+ break;
+ case 1:
+ symbol = fields[0];
+ break;
+ case 0:
+ break;
+ default:
+ llvm_unreachable("too many fields in order file");
+ }
+
+ if (!arch.empty()) {
+ if (!isArchString(arch)) {
+ error("invalid arch \"" + arch + "\" in order file: expected one of " +
+ llvm::join(archNames, ", "));
+ continue;
+ }
+
+ // TODO: Update when we extend support for other archs
+ if (arch != "x86_64")
+ continue;
+ }
+
+ if (!objectFile.empty() && !objectFile.endswith(".o")) {
+ error("invalid object file name \"" + objectFile +
+ "\" in order file: should end with .o");
+ continue;
+ }
+
+ if (!symbol.empty()) {
+ SymbolPriorityEntry &entry = config->priorities[symbol];
+ if (!objectFile.empty())
+ entry.objectFiles.insert(std::make_pair(objectFile, priority));
+ else
+ entry.anyObjectFile = std::max(entry.anyObjectFile, priority);
+ }
+
+ --priority;
+ }
+}
+
+// We expect sub-library names of the form "libfoo", which will match a dylib
+// with a path of .*/libfoo.dylib.
+static bool markSubLibrary(StringRef searchName) {
+ for (InputFile *file : inputFiles) {
+ if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
+ StringRef filename = path::filename(dylibFile->getName());
+ if (filename.consume_front(searchName) && filename == ".dylib") {
+ dylibFile->reexport = true;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void handlePlatformVersion(const opt::Arg *arg) {
+ // TODO: implementation coming very soon ...
+}
+
+static void warnIfDeprecatedOption(const opt::Option &opt) {
+ if (!opt.getGroup().isValid())
+ return;
+ if (opt.getGroup().getID() == OPT_grp_deprecated) {
+ warn("Option `" + opt.getPrefixedName() + "' is deprecated in ld64:");
+ warn(opt.getHelpText());
+ }
+}
+
+static void warnIfUnimplementedOption(const opt::Option &opt) {
+ if (!opt.getGroup().isValid())
+ return;
+ switch (opt.getGroup().getID()) {
+ case OPT_grp_deprecated:
+ // warn about deprecated options elsewhere
+ break;
+ case OPT_grp_undocumented:
+ warn("Option `" + opt.getPrefixedName() +
+ "' is undocumented. Should lld implement it?");
+ break;
+ case OPT_grp_obsolete:
+ warn("Option `" + opt.getPrefixedName() +
+ "' is obsolete. Please modernize your usage.");
+ break;
+ case OPT_grp_ignored:
+ warn("Option `" + opt.getPrefixedName() + "' is ignored.");
+ break;
+ default:
+ warn("Option `" + opt.getPrefixedName() +
+ "' is not yet implemented. Stay tuned...");
+ break;
+ }
+}
+
+bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly,
+ raw_ostream &stdoutOS, raw_ostream &stderrOS) {
+ lld::stdoutOS = &stdoutOS;
+ lld::stderrOS = &stderrOS;
+
+ stderrOS.enable_colors(stderrOS.has_colors());
+ // TODO: Set up error handler properly, e.g. the errorLimitExceededMsg
+
+ MachOOptTable parser;
+ opt::InputArgList args = parser.parse(argsArr.slice(1));
+
+ if (args.hasArg(OPT_help_hidden)) {
+ parser.printHelp(argsArr[0], /*showHidden=*/true);
+ return true;
+ } else if (args.hasArg(OPT_help)) {
+ parser.printHelp(argsArr[0], /*showHidden=*/false);
+ return true;
+ }
+
+ config = make<Configuration>();
+ symtab = make<SymbolTable>();
+ target = createTargetInfo(args);
+
+ config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main"));
+ config->outputFile = args.getLastArgValue(OPT_o, "a.out");
+ config->installName =
+ args.getLastArgValue(OPT_install_name, config->outputFile);
+ getLibrarySearchPaths(config->librarySearchPaths, args);
+ getFrameworkSearchPaths(config->frameworkSearchPaths, args);
+ config->outputType = args.hasArg(OPT_dylib) ? MH_DYLIB : MH_EXECUTE;
+
+ if (args.hasArg(OPT_v)) {
+ message(getLLDVersion());
+ message(StringRef("Library search paths:") +
+ (config->librarySearchPaths.size()
+ ? "\n\t" + llvm::join(config->librarySearchPaths, "\n\t")
+ : ""));
+ message(StringRef("Framework search paths:") +
+ (config->frameworkSearchPaths.size()
+ ? "\n\t" + llvm::join(config->frameworkSearchPaths, "\n\t")
+ : ""));
+ freeArena();
+ return !errorCount();
+ }
+
+ for (const auto &arg : args) {
+ const auto &opt = arg->getOption();
+ warnIfDeprecatedOption(opt);
+ switch (arg->getOption().getID()) {
+ case OPT_INPUT:
+ addFile(arg->getValue());
+ break;
+ case OPT_l: {
+ StringRef name = arg->getValue();
+ if (Optional<std::string> path = findLibrary(name)) {
+ addFile(*path);
+ break;
+ }
+ error("library not found for -l" + name);
+ break;
+ }
+ case OPT_platform_version:
+ handlePlatformVersion(arg);
+ break;
+ case OPT_o:
+ case OPT_dylib:
+ case OPT_e:
+ case OPT_L:
+ case OPT_Z:
+ case OPT_arch:
+ // handled elsewhere
+ break;
+ default:
+ warnIfUnimplementedOption(opt);
+ break;
+ }
+ }
+
+ // Now that all dylibs have been loaded, search for those that should be
+ // re-exported.
+ for (opt::Arg *arg : args.filtered(OPT_sub_library)) {
+ config->hasReexports = true;
+ StringRef searchName = arg->getValue();
+ if (!markSubLibrary(searchName))
+ error("-sub_library " + searchName + " does not match a supplied dylib");
+ }
+
+ StringRef orderFile = args.getLastArgValue(OPT_order_file);
+ if (!orderFile.empty())
+ parseOrderFile(orderFile);
+
+ if (config->outputType == MH_EXECUTE && !isa<Defined>(config->entry)) {
+ error("undefined symbol: " + config->entry->getName());
+ return false;
+ }
+
+ createSyntheticSections();
+
+ // Initialize InputSections.
+ for (InputFile *file : inputFiles) {
+ for (SubsectionMap &map : file->subsections) {
+ for (auto &p : map) {
+ InputSection *isec = p.second;
+ inputSections.push_back(isec);
+ }
+ }
+ }
+
+ // Write to an output file.
+ writeResult();
+
+ if (canExitEarly)
+ exitLld(errorCount() ? 1 : 0);
+
+ freeArena();
+ return !errorCount();
+}
--- /dev/null
+//===- Driver.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_DRIVER_H
+#define LLD_MACHO_DRIVER_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/Option/OptTable.h"
+
+namespace lld {
+namespace macho {
+
+class MachOOptTable : public llvm::opt::OptTable {
+public:
+ MachOOptTable();
+ llvm::opt::InputArgList parse(ArrayRef<const char *> argv);
+ void printHelp(const char *argv0, bool showHidden) const;
+};
+
+// Create enum with OPT_xxx values for each option in Options.td
+enum {
+ OPT_INVALID = 0,
+#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID,
+#include "Options.inc"
+#undef OPTION
+};
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- ExportTrie.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a partial implementation of the Mach-O export trie format. It's
+// essentially a symbol table encoded as a compressed prefix trie, meaning that
+// the common prefixes of each symbol name are shared for a more compact
+// representation. The prefixes are stored on the edges of the trie, and one
+// edge can represent multiple characters. For example, given two exported
+// symbols _bar and _baz, we will have a trie like this (terminal nodes are
+// marked with an asterisk):
+//
+// +-+-+
+// | | // root node
+// +-+-+
+// |
+// | _ba
+// |
+// +-+-+
+// | |
+// +-+-+
+// r / \ z
+// / \
+// +-+-+ +-+-+
+// | * | | * |
+// +-+-+ +-+-+
+//
+// More documentation of the format can be found in
+// llvm/tools/obj2yaml/macho2yaml.cpp.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExportTrie.h"
+#include "Symbols.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/LEB128.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+namespace {
+
+struct Edge {
+ Edge(StringRef s, TrieNode *node) : substring(s), child(node) {}
+
+ StringRef substring;
+ struct TrieNode *child;
+};
+
+struct ExportInfo {
+ uint64_t address;
+ // TODO: Add proper support for re-exports & stub-and-resolver flags.
+};
+
+} // namespace
+
+struct macho::TrieNode {
+ std::vector<Edge> edges;
+ Optional<ExportInfo> info;
+ // Estimated offset from the start of the serialized trie to the current node.
+ // This will converge to the true offset when updateOffset() is run to a
+ // fixpoint.
+ size_t offset = 0;
+
+ // Returns whether the new estimated offset differs from the old one.
+ bool updateOffset(size_t &nextOffset);
+ void writeTo(uint8_t *buf) const;
+};
+
+bool TrieNode::updateOffset(size_t &nextOffset) {
+ // Size of the whole node (including the terminalSize and the outgoing edges.)
+ // In contrast, terminalSize only records the size of the other data in the
+ // node.
+ size_t nodeSize;
+ if (info) {
+ uint64_t flags = 0;
+ uint32_t terminalSize =
+ getULEB128Size(flags) + getULEB128Size(info->address);
+ // Overall node size so far is the uleb128 size of the length of the symbol
+ // info + the symbol info itself.
+ nodeSize = terminalSize + getULEB128Size(terminalSize);
+ } else {
+ nodeSize = 1; // Size of terminalSize (which has a value of 0)
+ }
+ // Compute size of all child edges.
+ ++nodeSize; // Byte for number of children.
+ for (Edge &edge : edges) {
+ nodeSize += edge.substring.size() + 1 // String length.
+ + getULEB128Size(edge.child->offset); // Offset len.
+ }
+ // On input, 'nextOffset' is the new preferred location for this node.
+ bool result = (offset != nextOffset);
+ // Store new location in node object for use by parents.
+ offset = nextOffset;
+ nextOffset += nodeSize;
+ return result;
+}
+
+void TrieNode::writeTo(uint8_t *buf) const {
+ buf += offset;
+ if (info) {
+ // TrieNodes with Symbol info: size, flags address
+ uint64_t flags = 0; // TODO: emit proper flags
+ uint32_t terminalSize =
+ getULEB128Size(flags) + getULEB128Size(info->address);
+ buf += encodeULEB128(terminalSize, buf);
+ buf += encodeULEB128(flags, buf);
+ buf += encodeULEB128(info->address, buf);
+ } else {
+ // TrieNode with no Symbol info.
+ *buf++ = 0; // terminalSize
+ }
+ // Add number of children. TODO: Handle case where we have more than 256.
+ assert(edges.size() < 256);
+ *buf++ = edges.size();
+ // Append each child edge substring and node offset.
+ for (const Edge &edge : edges) {
+ memcpy(buf, edge.substring.data(), edge.substring.size());
+ buf += edge.substring.size();
+ *buf++ = '\0';
+ buf += encodeULEB128(edge.child->offset, buf);
+ }
+}
+
+TrieNode *TrieBuilder::makeNode() {
+ auto *node = make<TrieNode>();
+ nodes.emplace_back(node);
+ return node;
+}
+
+static int charAt(const Symbol *sym, size_t pos) {
+ StringRef str = sym->getName();
+ if (pos >= str.size())
+ return -1;
+ return str[pos];
+}
+
+// Build the trie by performing a three-way radix quicksort: We start by sorting
+// the strings by their first characters, then sort the strings with the same
+// first characters by their second characters, and so on recursively. Each
+// time the prefixes diverge, we add a node to the trie.
+//
+// node: The most recently created node along this path in the trie (i.e.
+// the furthest from the root.)
+// lastPos: The prefix length of the most recently created node, i.e. the number
+// of characters along its path from the root.
+// pos: The string index we are currently sorting on. Note that each symbol
+// S contained in vec has the same prefix S[0...pos).
+void TrieBuilder::sortAndBuild(MutableArrayRef<const Symbol *> vec,
+ TrieNode *node, size_t lastPos, size_t pos) {
+tailcall:
+ if (vec.empty())
+ return;
+
+ // Partition items so that items in [0, i) are less than the pivot,
+ // [i, j) are the same as the pivot, and [j, vec.size()) are greater than
+ // the pivot.
+ const Symbol *pivotSymbol = vec[vec.size() / 2];
+ int pivot = charAt(pivotSymbol, pos);
+ size_t i = 0;
+ size_t j = vec.size();
+ for (size_t k = 0; k < j;) {
+ int c = charAt(vec[k], pos);
+ if (c < pivot)
+ std::swap(vec[i++], vec[k++]);
+ else if (c > pivot)
+ std::swap(vec[--j], vec[k]);
+ else
+ k++;
+ }
+
+ bool isTerminal = pivot == -1;
+ bool prefixesDiverge = i != 0 || j != vec.size();
+ if (lastPos != pos && (isTerminal || prefixesDiverge)) {
+ TrieNode *newNode = makeNode();
+ node->edges.emplace_back(pivotSymbol->getName().slice(lastPos, pos),
+ newNode);
+ node = newNode;
+ lastPos = pos;
+ }
+
+ sortAndBuild(vec.slice(0, i), node, lastPos, pos);
+ sortAndBuild(vec.slice(j), node, lastPos, pos);
+
+ if (isTerminal) {
+ assert(j - i == 1); // no duplicate symbols
+ node->info = {pivotSymbol->getVA()};
+ } else {
+ // This is the tail-call-optimized version of the following:
+ // sortAndBuild(vec.slice(i, j - i), node, lastPos, pos + 1);
+ vec = vec.slice(i, j - i);
+ ++pos;
+ goto tailcall;
+ }
+}
+
+size_t TrieBuilder::build() {
+ if (exported.empty())
+ return 0;
+
+ TrieNode *root = makeNode();
+ sortAndBuild(exported, root, 0, 0);
+
+ // Assign each node in the vector an offset in the trie stream, iterating
+ // until all uleb128 sizes have stabilized.
+ size_t offset;
+ bool more;
+ do {
+ offset = 0;
+ more = false;
+ for (TrieNode *node : nodes)
+ more |= node->updateOffset(offset);
+ } while (more);
+
+ return offset;
+}
+
+void TrieBuilder::writeTo(uint8_t *buf) const {
+ for (TrieNode *node : nodes)
+ node->writeTo(buf);
+}
+
+namespace {
+
+// Parse a serialized trie and invoke a callback for each entry.
+class TrieParser {
+public:
+ TrieParser(const uint8_t *buf, size_t size, const TrieEntryCallback &callback)
+ : start(buf), end(start + size), callback(callback) {}
+
+ void parse(const uint8_t *buf, const Twine &cumulativeString);
+
+ void parse() { parse(start, ""); }
+
+ const uint8_t *start;
+ const uint8_t *end;
+ const TrieEntryCallback &callback;
+};
+
+} // namespace
+
+void TrieParser::parse(const uint8_t *buf, const Twine &cumulativeString) {
+ if (buf >= end)
+ fatal("Node offset points outside export section");
+
+ unsigned ulebSize;
+ uint64_t terminalSize = decodeULEB128(buf, &ulebSize);
+ buf += ulebSize;
+ uint64_t flags = 0;
+ size_t offset;
+ if (terminalSize != 0) {
+ flags = decodeULEB128(buf, &ulebSize);
+ callback(cumulativeString, flags);
+ }
+ buf += terminalSize;
+ uint8_t numEdges = *buf++;
+ for (uint8_t i = 0; i < numEdges; ++i) {
+ const char *cbuf = reinterpret_cast<const char *>(buf);
+ StringRef substring = StringRef(cbuf, strnlen(cbuf, end - buf));
+ buf += substring.size() + 1;
+ offset = decodeULEB128(buf, &ulebSize);
+ buf += ulebSize;
+ parse(start + offset, cumulativeString + substring);
+ }
+}
+
+void macho::parseTrie(const uint8_t *buf, size_t size,
+ const TrieEntryCallback &callback) {
+ if (size == 0)
+ return;
+
+ TrieParser(buf, size, callback).parse();
+}
--- /dev/null
+//===- ExportTrie.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_EXPORT_TRIE_H
+#define LLD_MACHO_EXPORT_TRIE_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+
+#include <vector>
+
+namespace lld {
+namespace macho {
+
+struct TrieNode;
+class Symbol;
+
+class TrieBuilder {
+public:
+ void addSymbol(const Symbol &sym) { exported.push_back(&sym); }
+ // Returns the size in bytes of the serialized trie.
+ size_t build();
+ void writeTo(uint8_t *buf) const;
+
+private:
+ TrieNode *makeNode();
+ void sortAndBuild(llvm::MutableArrayRef<const Symbol *> vec, TrieNode *node,
+ size_t lastPos, size_t pos);
+
+ std::vector<const Symbol *> exported;
+ std::vector<TrieNode *> nodes;
+};
+
+using TrieEntryCallback =
+ llvm::function_ref<void(const llvm::Twine & /*name*/, uint64_t /*flags*/)>;
+
+void parseTrie(const uint8_t *buf, size_t size, const TrieEntryCallback &);
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- InputFiles.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains functions to parse Mach-O object files. In this comment,
+// we describe the Mach-O file structure and how we parse it.
+//
+// Mach-O is not very different from ELF or COFF. The notion of symbols,
+// sections and relocations exists in Mach-O as it does in ELF and COFF.
+//
+// Perhaps the notion that is new to those who know ELF/COFF is "subsections".
+// In ELF/COFF, sections are an atomic unit of data copied from input files to
+// output files. When we merge or garbage-collect sections, we treat each
+// section as an atomic unit. In Mach-O, that's not the case. Sections can
+// consist of multiple subsections, and subsections are a unit of merging and
+// garbage-collecting. Therefore, Mach-O's subsections are more similar to
+// ELF/COFF's sections than Mach-O's sections are.
+//
+// A section can have multiple symbols. A symbol that does not have the
+// N_ALT_ENTRY attribute indicates a beginning of a subsection. Therefore, by
+// definition, a symbol is always present at the beginning of each subsection. A
+// symbol with N_ALT_ENTRY attribute does not start a new subsection and can
+// point to a middle of a subsection.
+//
+// The notion of subsections also affects how relocations are represented in
+// Mach-O. All references within a section need to be explicitly represented as
+// relocations if they refer to different subsections, because we obviously need
+// to fix up addresses if subsections are laid out in an output file differently
+// than they were in object files. To represent that, Mach-O relocations can
+// refer to an unnamed location via its address. Scattered relocations (those
+// with the R_SCATTERED bit set) always refer to unnamed locations.
+// Non-scattered relocations refer to an unnamed location if r_extern is not set
+// and r_symbolnum is zero.
+//
+// Without the above differences, I think you can use your knowledge about ELF
+// and COFF for Mach-O.
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputFiles.h"
+#include "Config.h"
+#include "ExportTrie.h"
+#include "InputSection.h"
+#include "MachOStructs.h"
+#include "OutputSection.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Target.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace llvm::support::endian;
+using namespace llvm::sys;
+using namespace lld;
+using namespace lld::macho;
+
+std::vector<InputFile *> macho::inputFiles;
+
+// Open a given file path and return it as a memory-mapped file.
+Optional<MemoryBufferRef> macho::readFile(StringRef path) {
+ // Open a file.
+ auto mbOrErr = MemoryBuffer::getFile(path);
+ if (auto ec = mbOrErr.getError()) {
+ 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
+
+ // If this is a regular non-fat file, return it.
+ const char *buf = mbref.getBufferStart();
+ auto *hdr = reinterpret_cast<const MachO::fat_header *>(buf);
+ if (read32be(&hdr->magic) != MachO::FAT_MAGIC)
+ return mbref;
+
+ // Object files and archive files may be fat files, which contains
+ // multiple real files for different CPU ISAs. Here, we search for a
+ // file that matches with the current link target and returns it as
+ // a MemoryBufferRef.
+ auto *arch = reinterpret_cast<const MachO::fat_arch *>(buf + sizeof(*hdr));
+
+ for (uint32_t i = 0, n = read32be(&hdr->nfat_arch); i < n; ++i) {
+ if (reinterpret_cast<const char *>(arch + i + 1) >
+ buf + mbref.getBufferSize()) {
+ error(path + ": fat_arch struct extends beyond end of file");
+ return None;
+ }
+
+ if (read32be(&arch[i].cputype) != target->cpuType ||
+ read32be(&arch[i].cpusubtype) != target->cpuSubtype)
+ continue;
+
+ uint32_t offset = read32be(&arch[i].offset);
+ uint32_t size = read32be(&arch[i].size);
+ if (offset + size > mbref.getBufferSize())
+ error(path + ": slice extends beyond end of file");
+ return MemoryBufferRef(StringRef(buf + offset, size), path.copy(bAlloc));
+ }
+
+ error("unable to find matching architecture in " + path);
+ return None;
+}
+
+static const load_command *findCommand(const mach_header_64 *hdr,
+ uint32_t type) {
+ const uint8_t *p =
+ reinterpret_cast<const uint8_t *>(hdr) + sizeof(mach_header_64);
+
+ for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) {
+ auto *cmd = reinterpret_cast<const load_command *>(p);
+ if (cmd->cmd == type)
+ return cmd;
+ p += cmd->cmdsize;
+ }
+ return nullptr;
+}
+
+void InputFile::parseSections(ArrayRef<section_64> sections) {
+ subsections.reserve(sections.size());
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+
+ for (const section_64 &sec : sections) {
+ InputSection *isec = make<InputSection>();
+ isec->file = this;
+ isec->name = StringRef(sec.sectname, strnlen(sec.sectname, 16));
+ isec->segname = StringRef(sec.segname, strnlen(sec.segname, 16));
+ isec->data = {isZeroFill(sec.flags) ? nullptr : buf + sec.offset,
+ static_cast<size_t>(sec.size)};
+ if (sec.align >= 32)
+ error("alignment " + std::to_string(sec.align) + " of section " +
+ isec->name + " is too large");
+ else
+ isec->align = 1 << sec.align;
+ isec->flags = sec.flags;
+ subsections.push_back({{0, isec}});
+ }
+}
+
+// Find the subsection corresponding to the greatest section offset that is <=
+// that of the given offset.
+//
+// offset: an offset relative to the start of the original InputSection (before
+// any subsection splitting has occurred). It will be updated to represent the
+// same location as an offset relative to the start of the containing
+// subsection.
+static InputSection *findContainingSubsection(SubsectionMap &map,
+ uint32_t *offset) {
+ auto it = std::prev(map.upper_bound(*offset));
+ *offset -= it->first;
+ return it->second;
+}
+
+void InputFile::parseRelocations(const section_64 &sec,
+ SubsectionMap &subsecMap) {
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+ ArrayRef<any_relocation_info> relInfos(
+ reinterpret_cast<const any_relocation_info *>(buf + sec.reloff),
+ sec.nreloc);
+
+ for (const any_relocation_info &anyRel : relInfos) {
+ if (anyRel.r_word0 & R_SCATTERED)
+ fatal("TODO: Scattered relocations not supported");
+
+ auto rel = reinterpret_cast<const relocation_info &>(anyRel);
+
+ Reloc r;
+ r.type = rel.r_type;
+ r.pcrel = rel.r_pcrel;
+ r.length = rel.r_length;
+ uint64_t rawAddend = target->getImplicitAddend(mb, sec, rel);
+
+ if (rel.r_extern) {
+ r.target = symbols[rel.r_symbolnum];
+ r.addend = rawAddend;
+ } else {
+ if (rel.r_symbolnum == 0 || rel.r_symbolnum > subsections.size())
+ fatal("invalid section index in relocation for offset " +
+ std::to_string(r.offset) + " in section " + sec.sectname +
+ " of " + getName());
+
+ SubsectionMap &targetSubsecMap = subsections[rel.r_symbolnum - 1];
+ const section_64 &targetSec = sectionHeaders[rel.r_symbolnum - 1];
+ uint32_t targetOffset;
+ if (rel.r_pcrel) {
+ // The implicit addend for pcrel section relocations is the pcrel offset
+ // in terms of the addresses in the input file. Here we adjust it so
+ // that it describes the offset from the start of the target section.
+ // TODO: The offset of 4 is probably not right for ARM64, nor for
+ // relocations with r_length != 2.
+ targetOffset =
+ sec.addr + rel.r_address + 4 + rawAddend - targetSec.addr;
+ } else {
+ // The addend for a non-pcrel relocation is its absolute address.
+ targetOffset = rawAddend - targetSec.addr;
+ }
+ r.target = findContainingSubsection(targetSubsecMap, &targetOffset);
+ r.addend = targetOffset;
+ }
+
+ r.offset = rel.r_address;
+ InputSection *subsec = findContainingSubsection(subsecMap, &r.offset);
+ subsec->relocs.push_back(r);
+ }
+}
+
+void InputFile::parseSymbols(ArrayRef<structs::nlist_64> nList,
+ const char *strtab, bool subsectionsViaSymbols) {
+ // resize(), not reserve(), because we are going to create N_ALT_ENTRY symbols
+ // out-of-sequence.
+ symbols.resize(nList.size());
+ std::vector<size_t> altEntrySymIdxs;
+
+ auto createDefined = [&](const structs::nlist_64 &sym, InputSection *isec,
+ uint32_t value) -> Symbol * {
+ StringRef name = strtab + sym.n_strx;
+ if (sym.n_type & N_EXT)
+ // Global defined symbol
+ return symtab->addDefined(name, isec, value);
+ else
+ // Local defined symbol
+ return make<Defined>(name, isec, value);
+ };
+
+ for (size_t i = 0, n = nList.size(); i < n; ++i) {
+ const structs::nlist_64 &sym = nList[i];
+
+ // Undefined symbol
+ if (!sym.n_sect) {
+ StringRef name = strtab + sym.n_strx;
+ symbols[i] = symtab->addUndefined(name);
+ continue;
+ }
+
+ const section_64 &sec = sectionHeaders[sym.n_sect - 1];
+ SubsectionMap &subsecMap = subsections[sym.n_sect - 1];
+ uint64_t offset = sym.n_value - sec.addr;
+
+ // If the input file does not use subsections-via-symbols, all symbols can
+ // use the same subsection. Otherwise, we must split the sections along
+ // symbol boundaries.
+ if (!subsectionsViaSymbols) {
+ symbols[i] = createDefined(sym, subsecMap[0], offset);
+ continue;
+ }
+
+ // nList entries aren't necessarily arranged in address order. Therefore,
+ // we can't create alt-entry symbols at this point because a later symbol
+ // may split its section, which may affect which subsection the alt-entry
+ // symbol is assigned to. So we need to handle them in a second pass below.
+ if (sym.n_desc & N_ALT_ENTRY) {
+ altEntrySymIdxs.push_back(i);
+ continue;
+ }
+
+ // Find the subsection corresponding to the greatest section offset that is
+ // <= that of the current symbol. The subsection that we find either needs
+ // to be used directly or split in two.
+ uint32_t firstSize = offset;
+ InputSection *firstIsec = findContainingSubsection(subsecMap, &firstSize);
+
+ if (firstSize == 0) {
+ // Alias of an existing symbol, or the first symbol in the section. These
+ // are handled by reusing the existing section.
+ symbols[i] = createDefined(sym, firstIsec, 0);
+ continue;
+ }
+
+ // We saw a symbol definition at a new offset. Split the section into two
+ // subsections. The new symbol uses the second subsection.
+ auto *secondIsec = make<InputSection>(*firstIsec);
+ secondIsec->data = firstIsec->data.slice(firstSize);
+ firstIsec->data = firstIsec->data.slice(0, firstSize);
+ // TODO: ld64 appears to preserve the original alignment as well as each
+ // subsection's offset from the last aligned address. We should consider
+ // emulating that behavior.
+ secondIsec->align = MinAlign(firstIsec->align, offset);
+
+ subsecMap[offset] = secondIsec;
+ // By construction, the symbol will be at offset zero in the new section.
+ symbols[i] = createDefined(sym, secondIsec, 0);
+ }
+
+ for (size_t idx : altEntrySymIdxs) {
+ const structs::nlist_64 &sym = nList[idx];
+ SubsectionMap &subsecMap = subsections[sym.n_sect - 1];
+ uint32_t off = sym.n_value - sectionHeaders[sym.n_sect - 1].addr;
+ InputSection *subsec = findContainingSubsection(subsecMap, &off);
+ symbols[idx] = createDefined(sym, subsec, off);
+ }
+}
+
+ObjFile::ObjFile(MemoryBufferRef mb) : InputFile(ObjKind, mb) {
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+ auto *hdr = reinterpret_cast<const mach_header_64 *>(mb.getBufferStart());
+
+ if (const load_command *cmd = findCommand(hdr, LC_SEGMENT_64)) {
+ auto *c = reinterpret_cast<const segment_command_64 *>(cmd);
+ sectionHeaders = ArrayRef<section_64>{
+ reinterpret_cast<const section_64 *>(c + 1), c->nsects};
+ parseSections(sectionHeaders);
+ }
+
+ // TODO: Error on missing LC_SYMTAB?
+ if (const load_command *cmd = findCommand(hdr, LC_SYMTAB)) {
+ auto *c = reinterpret_cast<const symtab_command *>(cmd);
+ ArrayRef<structs::nlist_64> nList(
+ reinterpret_cast<const structs::nlist_64 *>(buf + c->symoff), c->nsyms);
+ const char *strtab = reinterpret_cast<const char *>(buf) + c->stroff;
+ bool subsectionsViaSymbols = hdr->flags & MH_SUBSECTIONS_VIA_SYMBOLS;
+ parseSymbols(nList, strtab, subsectionsViaSymbols);
+ }
+
+ // The relocations may refer to the symbols, so we parse them after we have
+ // parsed all the symbols.
+ for (size_t i = 0, n = subsections.size(); i < n; ++i)
+ parseRelocations(sectionHeaders[i], subsections[i]);
+}
+
+DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella)
+ : InputFile(DylibKind, mb) {
+ if (umbrella == nullptr)
+ umbrella = this;
+
+ auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());
+ auto *hdr = reinterpret_cast<const mach_header_64 *>(mb.getBufferStart());
+
+ // Initialize dylibName.
+ if (const load_command *cmd = findCommand(hdr, LC_ID_DYLIB)) {
+ auto *c = reinterpret_cast<const dylib_command *>(cmd);
+ dylibName = reinterpret_cast<const char *>(cmd) + read32le(&c->dylib.name);
+ } else {
+ error("dylib " + getName() + " missing LC_ID_DYLIB load command");
+ return;
+ }
+
+ // Initialize symbols.
+ if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) {
+ auto *c = reinterpret_cast<const dyld_info_command *>(cmd);
+ parseTrie(buf + c->export_off, c->export_size,
+ [&](const Twine &name, uint64_t flags) {
+ symbols.push_back(symtab->addDylib(saver.save(name), umbrella));
+ });
+ } else {
+ error("LC_DYLD_INFO_ONLY not found in " + getName());
+ return;
+ }
+
+ if (hdr->flags & MH_NO_REEXPORTED_DYLIBS)
+ return;
+
+ const uint8_t *p =
+ reinterpret_cast<const uint8_t *>(hdr) + sizeof(mach_header_64);
+ for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) {
+ auto *cmd = reinterpret_cast<const load_command *>(p);
+ p += cmd->cmdsize;
+ if (cmd->cmd != LC_REEXPORT_DYLIB)
+ continue;
+
+ auto *c = reinterpret_cast<const dylib_command *>(cmd);
+ StringRef reexportPath =
+ reinterpret_cast<const char *>(c) + read32le(&c->dylib.name);
+ // TODO: Expand @loader_path, @executable_path etc in reexportPath
+ Optional<MemoryBufferRef> buffer = readFile(reexportPath);
+ if (!buffer) {
+ error("unable to read re-exported dylib at " + reexportPath);
+ return;
+ }
+ reexported.push_back(make<DylibFile>(*buffer, umbrella));
+ }
+}
+
+DylibFile::DylibFile(std::shared_ptr<llvm::MachO::InterfaceFile> interface,
+ DylibFile *umbrella)
+ : InputFile(DylibKind, MemoryBufferRef()) {
+ if (umbrella == nullptr)
+ umbrella = this;
+
+ dylibName = saver.save(interface->getInstallName());
+ // TODO(compnerd) filter out symbols based on the target platform
+ for (const auto symbol : interface->symbols())
+ if (symbol->getArchitectures().has(config->arch))
+ symbols.push_back(
+ symtab->addDylib(saver.save(symbol->getName()), umbrella));
+ // TODO(compnerd) properly represent the hierarchy of the documents as it is
+ // in theory possible to have re-exported dylibs from re-exported dylibs which
+ // should be parent'ed to the child.
+ for (auto document : interface->documents())
+ reexported.push_back(make<DylibFile>(document, umbrella));
+}
+
+ArchiveFile::ArchiveFile(std::unique_ptr<llvm::object::Archive> &&f)
+ : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) {
+ for (const object::Archive::Symbol &sym : file->symbols())
+ symtab->addLazy(sym.getName(), this, sym);
+}
+
+void ArchiveFile::fetch(const object::Archive::Symbol &sym) {
+ object::Archive::Child c =
+ CHECK(sym.getMember(), toString(this) +
+ ": could not get the member for symbol " +
+ sym.getName());
+
+ if (!seen.insert(c.getChildOffset()).second)
+ return;
+
+ MemoryBufferRef mb =
+ CHECK(c.getMemoryBufferRef(),
+ toString(this) +
+ ": could not get the buffer for the member defining symbol " +
+ sym.getName());
+ auto file = make<ObjFile>(mb);
+ symbols.insert(symbols.end(), file->symbols.begin(), file->symbols.end());
+ subsections.insert(subsections.end(), file->subsections.begin(),
+ file->subsections.end());
+}
+
+// Returns "<internal>" or "baz.o".
+std::string lld::toString(const InputFile *file) {
+ return file ? std::string(file->getName()) : "<internal>";
+}
--- /dev/null
+//===- InputFiles.h ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_INPUT_FILES_H
+#define LLD_MACHO_INPUT_FILES_H
+
+#include "MachOStructs.h"
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/TextAPI/MachO/InterfaceFile.h"
+#include "llvm/TextAPI/MachO/TextAPIReader.h"
+
+#include <map>
+#include <vector>
+
+namespace lld {
+namespace macho {
+
+class InputSection;
+class Symbol;
+struct Reloc;
+
+// If .subsections_via_symbols is set, each InputSection will be split along
+// symbol boundaries. The keys of a SubsectionMap represent the offsets of
+// each subsection from the start of the original pre-split InputSection.
+using SubsectionMap = std::map<uint32_t, InputSection *>;
+
+class InputFile {
+public:
+ enum Kind {
+ ObjKind,
+ DylibKind,
+ ArchiveKind,
+ };
+
+ virtual ~InputFile() = default;
+ Kind kind() const { return fileKind; }
+ StringRef getName() const { return mb.getBufferIdentifier(); }
+
+ MemoryBufferRef mb;
+ std::vector<Symbol *> symbols;
+ ArrayRef<llvm::MachO::section_64> sectionHeaders;
+ std::vector<SubsectionMap> subsections;
+
+protected:
+ InputFile(Kind kind, MemoryBufferRef mb) : mb(mb), fileKind(kind) {}
+
+ void parseSections(ArrayRef<llvm::MachO::section_64>);
+
+ void parseSymbols(ArrayRef<lld::structs::nlist_64> nList, const char *strtab,
+ bool subsectionsViaSymbols);
+
+ void parseRelocations(const llvm::MachO::section_64 &, SubsectionMap &);
+
+private:
+ const Kind fileKind;
+};
+
+// .o file
+class ObjFile : public InputFile {
+public:
+ explicit ObjFile(MemoryBufferRef mb);
+ static bool classof(const InputFile *f) { return f->kind() == ObjKind; }
+};
+
+// .dylib file
+class DylibFile : public InputFile {
+public:
+ explicit DylibFile(std::shared_ptr<llvm::MachO::InterfaceFile> interface,
+ DylibFile *umbrella = nullptr);
+
+ // Mach-O dylibs can re-export other dylibs as sub-libraries, meaning that the
+ // symbols in those sub-libraries will be available under the umbrella
+ // library's namespace. Those sub-libraries can also have their own
+ // re-exports. When loading a re-exported dylib, `umbrella` should be set to
+ // the root dylib to ensure symbols in the child library are correctly bound
+ // to the root. On the other hand, if a dylib is being directly loaded
+ // (through an -lfoo flag), then `umbrella` should be a nullptr.
+ explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella = nullptr);
+
+ static bool classof(const InputFile *f) { return f->kind() == DylibKind; }
+
+ StringRef dylibName;
+ uint64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel
+ bool reexport = false;
+ std::vector<DylibFile *> reexported;
+};
+
+// .a file
+class ArchiveFile : public InputFile {
+public:
+ explicit ArchiveFile(std::unique_ptr<llvm::object::Archive> &&file);
+ static bool classof(const InputFile *f) { return f->kind() == ArchiveKind; }
+ void fetch(const llvm::object::Archive::Symbol &sym);
+
+private:
+ std::unique_ptr<llvm::object::Archive> file;
+ // Keep track of children fetched from the archive by tracking
+ // which address offsets have been fetched already.
+ llvm::DenseSet<uint64_t> seen;
+};
+
+extern std::vector<InputFile *> inputFiles;
+
+llvm::Optional<MemoryBufferRef> readFile(StringRef path);
+
+} // namespace macho
+
+std::string toString(const macho::InputFile *file);
+} // namespace lld
+
+#endif
--- /dev/null
+//===- InputSection.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "InputSection.h"
+#include "OutputSegment.h"
+#include "Symbols.h"
+#include "Target.h"
+#include "lld/Common/Memory.h"
+#include "llvm/Support/Endian.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace llvm::support;
+using namespace lld;
+using namespace lld::macho;
+
+std::vector<InputSection *> macho::inputSections;
+
+uint64_t InputSection::getFileOffset() const {
+ return parent->fileOff + outSecFileOff;
+}
+
+uint64_t InputSection::getVA() const { return parent->addr + outSecOff; }
+
+void InputSection::writeTo(uint8_t *buf) {
+ if (getFileSize() == 0)
+ return;
+
+ memcpy(buf, data.data(), data.size());
+
+ for (Reloc &r : relocs) {
+ uint64_t va = 0;
+ if (auto *s = r.target.dyn_cast<Symbol *>())
+ va = target->getSymbolVA(*s, r.type);
+ else if (auto *isec = r.target.dyn_cast<InputSection *>())
+ va = isec->getVA();
+
+ uint64_t val = va + r.addend;
+ if (r.pcrel)
+ val -= getVA() + r.offset;
+ target->relocateOne(buf + r.offset, r, val);
+ }
+}
--- /dev/null
+//===- InputSection.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_INPUT_SECTION_H
+#define LLD_MACHO_INPUT_SECTION_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/BinaryFormat/MachO.h"
+
+namespace lld {
+namespace macho {
+
+class InputFile;
+class InputSection;
+class OutputSection;
+class Symbol;
+
+struct Reloc {
+ uint8_t type;
+ bool pcrel;
+ uint8_t length;
+ // The offset from the start of the subsection that this relocation belongs
+ // to.
+ uint32_t offset;
+ // Adding this offset to the address of the target symbol or subsection gives
+ // the destination that this relocation refers to.
+ uint64_t addend;
+ llvm::PointerUnion<Symbol *, InputSection *> target;
+};
+
+inline bool isZeroFill(uint8_t flags) {
+ return (flags & llvm::MachO::SECTION_TYPE) == llvm::MachO::S_ZEROFILL;
+}
+
+class InputSection {
+public:
+ virtual ~InputSection() = default;
+ virtual uint64_t getSize() const { return data.size(); }
+ virtual uint64_t getFileSize() const {
+ return isZeroFill(flags) ? 0 : getSize();
+ }
+ uint64_t getFileOffset() const;
+ uint64_t getVA() const;
+
+ virtual void writeTo(uint8_t *buf);
+
+ InputFile *file = nullptr;
+ StringRef name;
+ StringRef segname;
+
+ OutputSection *parent = nullptr;
+ uint64_t outSecOff = 0;
+ uint64_t outSecFileOff = 0;
+
+ uint32_t align = 1;
+ uint32_t flags = 0;
+
+ ArrayRef<uint8_t> data;
+ std::vector<Reloc> relocs;
+};
+
+extern std::vector<InputSection *> inputSections;
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- MachOStructs.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines structures used in the MachO object file format. Note that
+// unlike llvm/BinaryFormat/MachO.h, the structs here are defined in terms of
+// endian- and alignment-compatibility wrappers.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_MACHO_STRUCTS_H
+#define LLD_MACHO_MACHO_STRUCTS_H
+
+#include "llvm/Support/Endian.h"
+
+namespace lld {
+
+namespace structs {
+
+struct nlist_64 {
+ llvm::support::ulittle32_t n_strx;
+ uint8_t n_type;
+ uint8_t n_sect;
+ llvm::support::ulittle16_t n_desc;
+ llvm::support::ulittle64_t n_value;
+};
+
+} // namespace structs
+
+} // namespace lld
+
+#endif
--- /dev/null
+//===- OutputSection.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MergedOutputSection.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+void MergedOutputSection::mergeInput(InputSection *input) {
+ if (inputs.empty()) {
+ align = input->align;
+ flags = input->flags;
+ } else {
+ mergeFlags(input->flags);
+ align = std::max(align, input->align);
+ }
+
+ inputs.push_back(input);
+ input->parent = this;
+}
+
+void MergedOutputSection::finalize() {
+ uint64_t isecAddr = addr;
+ uint64_t isecFileOff = fileOff;
+ for (InputSection *isec : inputs) {
+ isecAddr = alignTo(isecAddr, isec->align);
+ isecFileOff = alignTo(isecFileOff, isec->align);
+ isec->outSecOff = isecAddr - addr;
+ isec->outSecFileOff = isecFileOff - fileOff;
+ isecAddr += isec->getSize();
+ isecFileOff += isec->getFileSize();
+ }
+ size = isecAddr - addr;
+ fileSize = isecFileOff - fileOff;
+}
+
+void MergedOutputSection::writeTo(uint8_t *buf) const {
+ for (InputSection *isec : inputs) {
+ isec->writeTo(buf + isec->outSecFileOff);
+ }
+}
+
+// TODO: this is most likely wrong; reconsider how section flags
+// are actually merged. The logic presented here was written without
+// any form of informed research.
+void MergedOutputSection::mergeFlags(uint32_t inputFlags) {
+ uint8_t sectionFlag = MachO::SECTION_TYPE & inputFlags;
+ if (sectionFlag != (MachO::SECTION_TYPE & flags))
+ error("Cannot add merge section; inconsistent type flags " +
+ Twine(sectionFlag));
+
+ uint32_t inconsistentFlags =
+ MachO::S_ATTR_DEBUG | MachO::S_ATTR_STRIP_STATIC_SYMS |
+ MachO::S_ATTR_NO_DEAD_STRIP | MachO::S_ATTR_LIVE_SUPPORT;
+ if ((inputFlags ^ flags) & inconsistentFlags)
+ error("Cannot add merge section; cannot merge inconsistent flags");
+
+ // Negate pure instruction presence if any section isn't pure.
+ uint32_t pureMask = ~MachO::S_ATTR_PURE_INSTRUCTIONS | (inputFlags & flags);
+
+ // Merge the rest
+ flags |= inputFlags;
+ flags &= pureMask;
+}
--- /dev/null
+//===- OutputSection.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_MERGED_OUTPUT_SECTION_H
+#define LLD_MACHO_MERGED_OUTPUT_SECTION_H
+
+#include "InputSection.h"
+#include "OutputSection.h"
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/MapVector.h"
+
+namespace lld {
+namespace macho {
+
+// Linking multiple files will inevitably mean resolving sections in different
+// files that are labeled with the same segment and section name. This class
+// contains all such sections and writes the data from each section sequentially
+// in the final binary.
+class MergedOutputSection : public OutputSection {
+public:
+ MergedOutputSection(StringRef name) : OutputSection(MergedKind, name) {}
+
+ const InputSection *firstSection() const { return inputs.front(); }
+ const InputSection *lastSection() const { return inputs.back(); }
+
+ // These accessors will only be valid after finalizing the section
+ uint64_t getSize() const override { return size; }
+ uint64_t getFileSize() const override { return fileSize; }
+
+ void mergeInput(InputSection *input);
+ void finalize() override;
+
+ void writeTo(uint8_t *buf) const override;
+
+ std::vector<InputSection *> inputs;
+
+ static bool classof(const OutputSection *sec) {
+ return sec->kind() == MergedKind;
+ }
+
+private:
+ void mergeFlags(uint32_t inputFlags);
+
+ size_t size = 0;
+ uint64_t fileSize = 0;
+};
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+include "llvm/Option/OptParser.td"
+
+def help : Flag<["-", "--"], "help">;
+def help_hidden : Flag<["--"], "help-hidden">,
+ HelpText<"Display help for hidden options">;
+
+// This is a complete Options.td compiled from Apple's ld(1) manpage
+// dated 2018-03-07 and cross checked with ld64 source code in repo
+// https://github.com/apple-opensource/ld64 at git tag "512.4" dated
+// 2018-03-18.
+
+// Flags<[HelpHidden]> marks options that are not yet ported to lld,
+// and serve as a scoreboard for annotating our progress toward
+// implementing ld64 options in lld. As you add implementions to
+// Driver.cpp, please remove the hidden flag here.
+
+def grp_kind : OptionGroup<"kind">, HelpText<"OUTPUT KIND">;
+
+def execute : Flag<["-"], "execute">,
+ HelpText<"Produce a main executable (default)">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def dylib : Flag<["-"], "dylib">,
+ HelpText<"Produce a shared library">,
+ Group<grp_kind>;
+def bundle : Flag<["-"], "bundle">,
+ HelpText<"Produce a bundle">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def r : Flag<["-"], "r">,
+ HelpText<"Merge multiple object files into one, retaining relocations">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def dylinker : Flag<["-"], "dylinker">,
+ HelpText<"Produce a dylinker only used when building dyld">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def dynamic : Flag<["-"], "dynamic">,
+ HelpText<"Link dynamically (default)">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def static : Flag<["-"], "static">,
+ HelpText<"Link statically">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def preload : Flag<["-"], "preload">,
+ HelpText<"Produce an unsegmented binary for embedded systems">,
+ Flags<[HelpHidden]>,
+ Group<grp_kind>;
+def arch : Separate<["-"], "arch">,
+ MetaVarName<"<arch_name>">,
+ HelpText<"The architecture (e.g. ppc, ppc64, i386, x86_64)">,
+ Group<grp_kind>;
+def o : Separate<["-"], "o">,
+ MetaVarName<"<path>">,
+ HelpText<"The name of the output file (default: `a.out')">,
+ Group<grp_kind>;
+
+def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARIES">;
+
+def l : Joined<["-"], "l">,
+ MetaVarName<"<name>">,
+ HelpText<"Search for lib<name>.dylib or lib<name>.a on the library search path">,
+ Group<grp_libs>;
+def weak_l : Joined<["-"], "weak-l">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -l<name>, but mark library and its references as weak imports">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def weak_library : Separate<["-"], "weak_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Like bare <path>, but mark library and its references as weak imports">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def reexport_l : Joined<["-"], "reexport-l">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -l<name>, but export all symbols of <name> from newly created library">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def reexport_library : Separate<["-"], "reexport_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Like bare <path>, but export all symbols of <path> from newly created library">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def upward_l : Joined<["-"], "upward-l">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -l<name>, but specify dylib as an upward dependency">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def upward_library : Separate<["-"], "upward_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Like bare <path>, but specify dylib as an upward dependency">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def L : JoinedOrSeparate<["-"], "L">,
+ MetaVarName<"<dir>">,
+ HelpText<"Add dir to the library search path">,
+ Group<grp_libs>;
+def Z : Flag<["-"], "Z">,
+ HelpText<"Remove standard directories from the library and framework search paths">,
+ Group<grp_libs>;
+def syslibroot : Separate<["-"], "syslibroot">,
+ MetaVarName<"<rootdir>">,
+ HelpText<"Prepend <rootdir> to all library and framework search paths">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def search_paths_first : Flag<["-"], "search_paths_first">,
+ HelpText<"Search for lib<name>.dylib and lib<name>.a at each step in traversing search path (default for Xcode 4 and later)">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def search_dylibs_first : Flag<["-"], "search_dylibs_first">,
+ HelpText<"Search for lib<name>.dylib on first pass, then for lib<name>.a on second pass through search path (default for Xcode 3 and earlier)">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def framework : Separate<["-"], "framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Search for <name>.framework/<name> on the framework search path">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def weak_framework : Separate<["-"], "weak_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -framework <name>, but mark framework and its references as weak imports">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def reexport_framework : Separate<["-"], "reexport_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -framework <name>, but export all symbols of <name> from the newly created library">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def upward_framework : Separate<["-"], "upward_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"Like -framework <name>, but specify the framework as an upward dependency">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def F : JoinedOrSeparate<["-"], "F">,
+ MetaVarName<"<dir>">,
+ HelpText<"Add dir to the framework search path">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def all_load : Flag<["-"], "all_load">,
+ HelpText<"Load all members of all static archive libraries">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def ObjC : Flag<["-"], "ObjC">,
+ HelpText<"Load all members of static archives that are an Objective-C class or category.">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+def force_load : Separate<["-"], "force_load">,
+ MetaVarName<"<path>">,
+ HelpText<"Load all members static archive library at <path>">,
+ Flags<[HelpHidden]>,
+ Group<grp_libs>;
+
+def grp_content : OptionGroup<"content">, HelpText<"ADDITIONAL CONTENT">;
+
+def sectcreate : MultiArg<["-"], "sectcreate", 3>,
+ MetaVarName<"<segment> <section> <file>">,
+ HelpText<"Create <section> in <segment> from the contents of <file>">,
+ Flags<[HelpHidden]>,
+ Group<grp_content>;
+def segcreate : MultiArg<["-"], "segcreate", 3>,
+ MetaVarName<"<segment> <section> <file>">,
+ Alias<sectcreate>,
+ HelpText<"Alias for -sectcreate">,
+ Flags<[HelpHidden]>,
+ Group<grp_content>;
+def filelist : Separate<["-"], "filelist">,
+ MetaVarName<"<file>">,
+ HelpText<"Read names of files to link from <file>">,
+ Flags<[HelpHidden]>,
+ Group<grp_content>;
+def dtrace : Separate<["-"], "dtrace">,
+ MetaVarName<"<script>">,
+ HelpText<"Enable DTrace static probes according to declarations in <script>">,
+ Flags<[HelpHidden]>,
+ Group<grp_content>;
+
+def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">;
+
+def dead_strip : Flag<["-"], "dead_strip">,
+ HelpText<"Remove unreachable functions and data">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def order_file : Separate<["-"], "order_file">,
+ MetaVarName<"<file>">,
+ HelpText<"Layout functions and data according to specification in <file>">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def sectorder : MultiArg<["-"], "sectorder", 3>,
+ MetaVarName<"<segname> <sectname> <orderfile>">,
+ HelpText<"Replaced by more general -order_file option">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_order_inits : Flag<["-"], "no_order_inits">,
+ HelpText<"Disable default reordering of initializer and terminator functions">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_order_data : Flag<["-"], "no_order_data">,
+ HelpText<"Disable default reordering of global data accessed at launch time">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def platform_version : MultiArg<["-"], "platform_version", 3>,
+ MetaVarName<"<platform> <min_version> <sdk_version>">,
+ HelpText<"Platform (e.g., macos, ios, tvos, watchos, bridgeos, mac-catalyst, ios-sim, tvos-sim, watchos-sim, driverkit) and version numbers">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def macos_version_min : Separate<["-"], "macos_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Oldest macOS version for which linked output is useable">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def macosx_version_min : Separate<["-"], "macosx_version_min">,
+ MetaVarName<"<version>">,
+ Alias<macos_version_min>,
+ HelpText<"Alias for -macos_version_min">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def ios_version_min : Separate<["-"], "ios_version_min">,
+ MetaVarName<"<version>">,
+ HelpText<"Oldest iOS version for which linked output is useable">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">,
+ MetaVarName<"<version>">,
+ Alias<ios_version_min>,
+ HelpText<"Alias for -ios_version_min">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def image_base : Separate<["-"], "image_base">,
+ MetaVarName<"<address>">,
+ HelpText<"Preferred hex load address for a dylib or bundle.">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def seg1addr : Separate<["-"], "seg1addr">,
+ MetaVarName<"<address>">,
+ Alias<image_base>,
+ HelpText<"Alias for -image_base">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_implicit_dylibs : Flag<["-"], "no_implicit_dylibs">,
+ HelpText<"Do not optimize public dylib transitive symbol references">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def exported_symbols_order : Separate<["-"], "exported_symbols_order">,
+ MetaVarName<"<file>">,
+ HelpText<"Specify frequently-used symbols in <file> to optimize symbol exports">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_zero_fill_sections : Flag<["-"], "no_zero_fill_sections">,
+ HelpText<"Explicitly store zeroed data in the final image">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def merge_zero_fill_sections : Flag<["-"], "merge_zero_fill_sections">,
+ HelpText<"Merge all zeroed data into the __zerofill section">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+def no_branch_islands : Flag<["-"], "no_branch_islands">,
+ HelpText<"Disable infra for branches beyond the maximum branch distance.">,
+ Flags<[HelpHidden]>,
+ Group<grp_opts>;
+
+def grp_dylib : OptionGroup<"dylib">, HelpText<"DYNAMIC LIBRARIES (DYLIB)">;
+
+def install_name : Separate<["-"], "install_name">,
+ MetaVarName<"<name>">,
+ HelpText<"Set an internal install path in a dylib">,
+ Group<grp_dylib>;
+def dylib_install_name : Separate<["-"], "dylib_install_name">,
+ MetaVarName<"<name>">,
+ Alias<install_name>,
+ HelpText<"Alias for -install_name">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def dylinker_install_name : Separate<["-"], "dylinker_install_name">,
+ MetaVarName<"<name>">,
+ Alias<install_name>,
+ HelpText<"Alias for -install_name">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">,
+ HelpText<"Clients can discard this dylib if it is unreferenced">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def compatibility_version : Separate<["-"], "compatibility_version">,
+ MetaVarName<"<version>">,
+ HelpText<"Compatibility <version> of this library">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">,
+ MetaVarName<"<version>">,
+ Alias<compatibility_version>,
+ HelpText<"Alias for -compatibility_version">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def current_version : Separate<["-"], "current_version">,
+ MetaVarName<"<version>">,
+ HelpText<"Current <version> of this library">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+def dylib_current_version : Separate<["-"], "dylib_current_version">,
+ MetaVarName<"<version>">,
+ Alias<current_version>,
+ HelpText<"Alias for -current_version">,
+ Flags<[HelpHidden]>,
+ Group<grp_dylib>;
+
+def grp_main : OptionGroup<"main">, HelpText<"MAIN EXECUTABLE">;
+
+def pie : Flag<["-"], "pie">,
+ HelpText<"Build a position independent executable (default for macOS 10.7 and later)">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def no_pie : Flag<["-"], "no_pie">,
+ HelpText<"Do not build a position independent executable (default for macOS 10.6 and earlier)">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def pagezero_size : Separate<["-"], "pagezero_size">,
+ MetaVarName<"<size>">,
+ HelpText<"Size of unreadable segment at address zero is hex <size> (default is 4KB on 32-bit and 4GB on 64-bit)">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def stack_size : Separate<["-"], "stack_size">,
+ MetaVarName<"<size>">,
+ HelpText<"Maximum hex stack size for the main thread in a program. (default is 8MB)">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def allow_stack_execute : Flag<["-"], "allow_stack_execute">,
+ HelpText<"Mark stack segment as executable">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+def export_dynamic : Flag<["-"], "export_dynamic">,
+ HelpText<"Preserve all global symbols during LTO">,
+ Flags<[HelpHidden]>,
+ Group<grp_main>;
+
+def grp_bundle : OptionGroup<"bundle">, HelpText<"CREATING A BUNDLE">;
+
+def bundle_loader : Separate<["-"], "bundle_loader">,
+ MetaVarName<"<executable>">,
+ HelpText<"Resolve undefined symbols from <executable>">,
+ Flags<[HelpHidden]>,
+ Group<grp_bundle>;
+
+def grp_object : OptionGroup<"object">, HelpText<"CREATING AN OBJECT FILE">;
+
+def keep_private_externs : Flag<["-"], "keep_private_externs">,
+ HelpText<"Do not convert private external symbols to static symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_object>;
+def d : Flag<["-"], "d">,
+ HelpText<"Force tentative into real definitions for common symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_object>;
+
+def grp_resolve : OptionGroup<"resolve">, HelpText<"SYMBOL RESOLUTION">;
+
+def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Symbols specified in <file> remain global, while others become private externs">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def exported_symbol : Separate<["-"], "exported_symbol">,
+ MetaVarName<"<symbol>">,
+ HelpText<"<symbol> remains global, while others become private externs">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Global symbols specified in <file> become private externs">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def unexported_symbol : Separate<["-"], "unexported_symbol">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Global <symbol> becomes private extern">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def reexported_symbols_list : Separate<["-"], "reexported_symbols_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Symbols from dependent dylibs specified in <file> are reexported by this dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def alias : MultiArg<["-"], "alias", 2>,
+ MetaVarName<"<symbol_name> <alternate_name>">,
+ HelpText<"Create a symbol alias with default global visibility">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def alias_list : Separate<["-"], "alias_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Create symbol aliases specified in <file>">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def flat_namespace : Flag<["-"], "flat_namespace">,
+ HelpText<"Resolve symbols from all dylibs, both direct & transitive. Do not record source libraries: dyld must re-search at runtime and use the first definition found">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def u : Separate<["-"], "u">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Require that <symbol> be defined for the link to succeed">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def U : Separate<["-"], "U">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Allow <symbol> to have no definition">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def undefined : Separate<["-"], "undefined">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Handle undefined symbols according to <treatment>: error, warning, suppress, or dynamic_lookup (default is error)">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def rpath : Separate<["-"], "rpath">,
+ MetaVarName<"<path>">,
+ HelpText<"Add <path> to dyld search list for dylibs with load path prefix `@rpath/'">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def commons : Separate<["-"], "commons">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Resolve tentative definitions in dylibs according to <treatment>: ignore_dylibs, use_dylibs, error (default is ignore_dylibs)">,
+ Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+
+def grp_introspect : OptionGroup<"introspect">, HelpText<"INTROSPECTING THE LINKER">;
+
+def why_load : Flag<["-"], "why_load">,
+ HelpText<"Log the symbol that compels loading of each object file from a static library">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def whyload : Flag<["-"], "whyload">,
+ Alias<why_load>,
+ HelpText<"Alias for -why_load">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def why_live : Separate<["-"], "why_live">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Log a chain of references to <symbol>, for use with -dead_strip">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def print_statistics : Flag<["-"], "print_statistics">,
+ HelpText<"Log the linker's memory and CPU usage">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def t : Flag<["-"], "t">,
+ HelpText<"Log every file the linker loads: object, archive, and dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def whatsloaded : Flag<["-"], "whatsloaded">,
+ HelpText<"Logs only the object files the linker loads">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def order_file_statistics : Flag<["-"], "order_file_statistics">,
+ HelpText<"Logs information about -order_file">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+def map : Separate<["-"], "map">,
+ MetaVarName<"<path>">,
+ HelpText<"Writes all symbols and their addresses to <path>">,
+ Flags<[HelpHidden]>,
+ Group<grp_introspect>;
+
+def grp_symtab : OptionGroup<"symtab">, HelpText<"SYMBOL TABLE OPTIMIZATIONS">;
+
+def S : Flag<["-"], "S">,
+ HelpText<"Strip debug information (STABS or DWARF) from the output">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+def x : Flag<["-"], "x">,
+ HelpText<"Exclude non-global symbols from the output symbol table">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+def non_global_symbols_strip_list : Separate<["-"], "non_global_symbols_strip_list">,
+ MetaVarName<"<path>">,
+ HelpText<"Specify in <path> the non-global symbols that should be removed from the output symbol table">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+def non_global_symbols_no_strip_list : Separate<["-"], "non_global_symbols_no_strip_list">,
+ MetaVarName<"<path>">,
+ HelpText<"Specify in <path> the non-global symbols that should remain in the output symbol table">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+def oso_prefix : Separate<["-"], "oso_prefix">,
+ MetaVarName<"<path>">,
+ HelpText<"Remove the prefix <path> from OSO symbols in the debug map">,
+ Flags<[HelpHidden]>,
+ Group<grp_symtab>;
+
+def grp_bitcode : OptionGroup<"bitcode">, HelpText<"BITCODE BUILD FLOW">;
+
+def bitcode_bundle : Flag<["-"], "bitcode_bundle">,
+ HelpText<"Generate an embedded bitcode bundle in the __LLVM,__bundle section of the output">,
+ Flags<[HelpHidden]>,
+ Group<grp_bitcode>;
+def bitcode_hide_symbols : Flag<["-"], "bitcode_hide_symbols">,
+ HelpText<"With -bitcode_bundle, hide all non-exported symbols from output bitcode bundle.">,
+ Flags<[HelpHidden]>,
+ Group<grp_bitcode>;
+def bitcode_symbol_map : Separate<["-"], "bitcode_symbol_map">,
+ MetaVarName<"<path>">,
+ HelpText<"Write the bitcode symbol reverse mapping to file <path>, or if a directory, to <path>/UUID.bcsymbolmap">,
+ Flags<[HelpHidden]>,
+ Group<grp_bitcode>;
+
+def grp_rare : OptionGroup<"rare">, HelpText<"RARELY USED">;
+
+def v : Flag<["-"], "v">,
+ HelpText<"Print the linker version">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def version_details : Flag<["-"], "version_details">,
+ HelpText<"Print the linker version in JSON form">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_weak_imports : Flag<["-"], "no_weak_imports">,
+ HelpText<"Fail if any symbols are weak imports, allowed to be NULL at runtime">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_deduplicate : Flag<["-"], "no_deduplicate">,
+ HelpText<"Omit the deduplication pass">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def verbose_deduplicate : Flag<["-"], "verbose_deduplicate">,
+ HelpText<"Print function names eliminated by deduplication and the total size of code savings">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_inits : Flag<["-"], "no_inits">,
+ HelpText<"Fail if the output contains static initializers">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_warn_inits : Flag<["-"], "no_warn_inits">,
+ HelpText<"Suppress warnings for static initializers in the output">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def debug_variant : Flag<["-"], "debug_variant">,
+ HelpText<"Suppress warnings germane to binaries shipping to customers">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def unaligned_pointers : Separate<["-"], "unaligned_pointers">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Handle unaligned pointers in __DATA segments according to <treatment>: warning, error, or suppress (default for arm64e is error, otherwise suppress)">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def dirty_data_list : Separate<["-"], "dirty_data_list">,
+ MetaVarName<"<path>">,
+ HelpText<"Specify data symbols in <path> destined for the __DATA_DIRTY segment">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def max_default_common_align : Separate<["-"], "max_default_common_align">,
+ MetaVarName<"<boundary>">,
+ HelpText<"Reduce maximum alignment for common symbols to a hex power-of-2 <boundary>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def move_to_rw_segment : MultiArg<["-"], "move_to_rw_segment", 2>,
+ MetaVarName<"<segment> <path>">,
+ HelpText<"Move data symbols listed in <path> to another <segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def move_to_ro_segment : MultiArg<["-"], "move_to_ro_segment", 2>,
+ MetaVarName<"<segment> <path>">,
+ HelpText<"Move code symbols listed in <path> to another <segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def rename_section : MultiArg<["-"], "rename_section", 4>,
+ MetaVarName<"<from_segment> <from_section> <to_segment> <to_section>">,
+ HelpText<"Rename <from_segment>/<from_section> as <to_segment>/<to_section>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def rename_segment : MultiArg<["-"], "rename_segment", 2>,
+ MetaVarName<"<from_segment> <to_segment>">,
+ HelpText<"Rename <from_segment> as <to_segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def trace_symbol_layout : Flag<["-"], "trace_symbol_layout">,
+ HelpText<"Show where and why symbols move, as specified by -move_to_ro_segment, -move_to_rw_segment, -rename_section, and -rename_segment">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def section_order : MultiArg<["-"], "section_order", 2>,
+ MetaVarName<"<segment> <sections>">,
+ HelpText<"With -preload, specify layout sequence of colon-separated <sections> in <segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segment_order : Separate<["-"], "segment_order">,
+ MetaVarName<"<colon_separated_segment_list>">,
+ HelpText<"With -preload, specify layout sequence of colon-separated <segments>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def allow_heap_execute : Flag<["-"], "allow_heap_execute">,
+ HelpText<"On i386, allow any page to execute code">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def application_extension : Flag<["-"], "application_extension">,
+ HelpText<"Designate the linker output as safe for use in an application extension">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_application_extension : Flag<["-"], "no_application_extension">,
+ HelpText<"Designate the linker output as unsafe for use in an application extension">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def fatal_warnings : Flag<["-"], "fatal_warnings">,
+ HelpText<"Escalate warnings as errors">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_eh_labels : Flag<["-"], "no_eh_labels">,
+ HelpText<"In -r mode, suppress .eh labels in the __eh_frame section">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def warn_compact_unwind : Flag<["-"], "warn_compact_unwind">,
+ HelpText<"Warn for each FDE that cannot compact into the __unwind_info section and must remain in the __eh_frame section">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def warn_weak_exports : Flag<["-"], "warn_weak_exports">,
+ HelpText<"Warn if the linked image contains weak external symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_weak_exports : Flag<["-"], "no_weak_exports">,
+ HelpText<"Fail if the linked image contains weak external symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def objc_gc_compaction : Flag<["-"], "objc_gc_compaction">,
+ HelpText<"Mark the Objective-C image as compatible with compacting garbage collection">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def objc_gc : Flag<["-"], "objc_gc">,
+ HelpText<"Verify that all code was compiled with -fobjc-gc or -fobjc-gc-only">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def objc_gc_only : Flag<["-"], "objc_gc_only">,
+ HelpText<"Verify that all code was compiled with -fobjc-gc-only">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def dead_strip_dylibs : Flag<["-"], "dead_strip_dylibs">,
+ HelpText<"Remove dylibs that are unreachable by the entry point or exported symbols">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def allow_sub_type_mismatches : Flag<["-"], "allow_sub_type_mismatches">,
+ HelpText<"Permit mixing objects compiled for different ARM CPU subtypes">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_uuid : Flag<["-"], "no_uuid">,
+ HelpText<"Do not generate the LC_UUID load command">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def root_safe : Flag<["-"], "root_safe">,
+ HelpText<"Set the MH_ROOT_SAFE bit in the mach-o header">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def setuid_safe : Flag<["-"], "setuid_safe">,
+ HelpText<"Set the MH_SETUID_SAFE bit in the mach-o header">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def interposable : Flag<["-"], "interposable">,
+ HelpText<"Indirects access to all to exported symbols in a dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def multi_module : Flag<["-"], "multi_module">,
+ Alias<interposable>,
+ HelpText<"Alias for -interposable">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def init : Separate<["-"], "init">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Run <symbol> as the first initializer in a dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def sub_library : Separate<["-"], "sub_library">,
+ MetaVarName<"<name>">,
+ HelpText<"Re-export the dylib as <name>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def sub_umbrella : Separate<["-"], "sub_umbrella">,
+ MetaVarName<"<name>">,
+ HelpText<"Re-export the framework as <name>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def allowable_client : Separate<["-"], "allowable_client">,
+ MetaVarName<"<name>">,
+ HelpText<"Specify <name> of a dylib or framework that is allowed to link to this dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def client_name : Separate<["-"], "client_name">,
+ MetaVarName<"<name>">,
+ HelpText<"Specifies a <name> this client should match with the -allowable_client <name> in a dependent dylib">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def umbrella : Separate<["-"], "umbrella">,
+ MetaVarName<"<<name>>">,
+ HelpText<"Re-export this dylib through the umbrella framework <name>a">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def headerpad : Separate<["-"], "headerpad">,
+ MetaVarName<"<size>">,
+ HelpText<"Allocate hex <size> extra space for future expansion of the load commands via install_name_tool">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def headerpad_max_install_names : Flag<["-"], "headerpad_max_install_names">,
+ HelpText<"Allocate extra space so all load-command paths can expand to MAXPATHLEN via install_name_tool">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def bind_at_load : Flag<["-"], "bind_at_load">,
+ HelpText<"Tell dyld to bind all symbols at load time, rather than lazily">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def force_flat_namespace : Flag<["-"], "force_flat_namespace">,
+ HelpText<"Tell dyld to use a flat namespace on this executable and all its dependent dylibs & bundles">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segalign : Separate<["-"], "segalign">,
+ MetaVarName<"<boundary>">,
+ HelpText<"Align all segments to hex power-of-2 <boundary>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def sectalign : MultiArg<["-"], "sectalign", 3>,
+ MetaVarName<"<segment> <section> <boundary>">,
+ HelpText<"Align <section> within <segment> to hex power-of-2 <boundary>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def stack_addr : Separate<["-"], "stack_addr">,
+ MetaVarName<"<address>">,
+ HelpText<"Initialize stack pointer to hex <address> rounded to a page boundary">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segprot : MultiArg<["-"], "segprot", 3>,
+ MetaVarName<"<segment> <max> <init>">,
+ HelpText<"Specifies the <max> and <init> virtual memory protection of <segment> as r/w/x/-seg_addr_table path Specify hex base addresses and dylib install names on successive lines in <path>. This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segs_read_write_addr : Separate<["-"], "segs_read_write_addr">,
+ MetaVarName<"<address>">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segs_read_only_addr : Separate<["-"], "segs_read_only_addr">,
+ MetaVarName<"<address>">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def segaddr : MultiArg<["-"], "segaddr", 2>,
+ MetaVarName<"<segment> <address>">,
+ HelpText<"Specify the starting hex <address> at a 4KiB page boundary for <segment>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def seg_page_size : MultiArg<["-"], "seg_page_size", 2>,
+ MetaVarName<"<segment> <size>">,
+ HelpText<"Specifies the page <size> for <segment>. Segment size will be a multiple of its page size">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def dylib_file : Separate<["-"], "dylib_file">,
+ MetaVarName<"<install_path:current_path>">,
+ HelpText<"Specify <current_path> as different from where a dylib normally resides at <install_path>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def prebind : Flag<["-"], "prebind">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def weak_reference_mismatches : Separate<["-"], "weak_reference_mismatches">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Resolve symbol imports of conflicting weakness according to <treatment> as weak, non-weak, or error (default is non-weak)">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def read_only_relocs : Separate<["-"], "read_only_relocs">,
+ MetaVarName<"<treatment>">,
+ HelpText<"Handle relocations that modify read-only pages according to <treatment> of warning, error, or suppress (i.e., allow)">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def force_cpusubtype_ALL : Flag<["-"], "force_cpusubtype_ALL">,
+ HelpText<"Mark binary as runnable on any PowerPC, ignoring any PowerPC cpu requirements encoded in the object files">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_arch_warnings : Flag<["-"], "no_arch_warnings">,
+ HelpText<"Suppresses warnings about inputs whose architecture does not match the -arch option">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def arch_errors_fatal : Flag<["-"], "arch_errors_fatal">,
+ HelpText<"Escalate to errors any warnings about inputs whose architecture does not match the -arch option">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def e : Separate<["-"], "e">,
+ MetaVarName<"<symbol>">,
+ HelpText<"Make <symbol> the entry point of an executable (default is \"start\" from crt1.o)">,
+ Group<grp_rare>;
+def w : Flag<["-"], "w">,
+ HelpText<"Suppress all warnings">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def final_output : Separate<["-"], "final_output">,
+ MetaVarName<"<name>">,
+ HelpText<"Specify the dylib install name if -install_name is not used--used by compiler driver for multiple -arch arguments">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def arch_multiple : Flag<["-"], "arch_multiple">,
+ HelpText<"Augment error and warning messages with the architecture name">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def twolevel_namespace_hints : Flag<["-"], "twolevel_namespace_hints">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def dot : Separate<["-"], "dot">,
+ MetaVarName<"<path>">,
+ HelpText<"Write a graph of symbol dependencies to <path> as a .dot file viewable with GraphViz">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def keep_relocs : Flag<["-"], "keep_relocs">,
+ HelpText<"Retain section-based relocation records in the output, which are ignored at runtime by dyld">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def warn_stabs : Flag<["-"], "warn_stabs">,
+ HelpText<"Warn when bad stab symbols inside a BINCL/EINCL prevent optimization">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def warn_commons : Flag<["-"], "warn_commons">,
+ HelpText<"Warn when a tentative definition in an object file matches an external symbol in a dylib, which often means \"extern\" is missing from a variable declaration in a header file">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def read_only_stubs : Flag<["-"], "read_only_stubs">,
+ HelpText<"On i386, make the __IMPORT segment of a final linked image read-only">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def slow_stubs : Flag<["-"], "slow_stubs">,
+ HelpText<"This option is obsolete">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def interposable_list : Separate<["-"], "interposable_list">,
+ MetaVarName<"<path>">,
+ HelpText<"Access global symbols listed in <path> indirectly">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_function_starts : Flag<["-"], "no_function_starts">,
+ HelpText<"Do not creates a compressed table of function start addresses">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
+ HelpText<"Do not merge Objective-C categories into their classes">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def object_path_lto : Separate<["-"], "object_path_lto">,
+ MetaVarName<"<path>">,
+ HelpText<"Retain any temporary mach-o file in <path> that would otherwise be deleted during LTO">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def lto_library : Separate<["-"], "lto_library">,
+ MetaVarName<"<path>">,
+ HelpText<"Override the default ../lib/libLTO.dylib as <path>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def cache_path_lto : Separate<["-"], "cache_path_lto">,
+ MetaVarName<"<path>">,
+ HelpText<"Use <path> as a directory for the incremental LTO cache">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def prune_interval_lto : Separate<["-"], "prune_interval_lto">,
+ MetaVarName<"<seconds>">,
+ HelpText<"Prune the incremental LTO cache after <seconds> (-1 disables pruning)">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def prune_after_lto : Separate<["-"], "prune_after_lto">,
+ MetaVarName<"<seconds>">,
+ HelpText<"Remove LTO cache entries after <seconds>">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def max_relative_cache_size_lto : Separate<["-"], "max_relative_cache_size_lto">,
+ MetaVarName<"<percent>">,
+ HelpText<"Limit the incremental LTO cache growth to <percent> of free disk, space">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def page_align_data_atoms : Flag<["-"], "page_align_data_atoms">,
+ HelpText<"Distribute global variables on separate pages so page used/dirty status can guide creation of an order file to cluster commonly used/dirty globals">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+def not_for_dyld_shared_cache : Flag<["-"], "not_for_dyld_shared_cache">,
+ HelpText<"Prevent system dylibs from being placed into the dylib shared cache">,
+ Flags<[HelpHidden]>,
+ Group<grp_rare>;
+
+def grp_deprecated : OptionGroup<"deprecated">, HelpText<"DEPRECATED">;
+
+def lazy_framework : Separate<["-"], "lazy_framework">,
+ MetaVarName<"<name>">,
+ HelpText<"This option is deprecated and is now an alias for -framework.">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def lazy_library : Separate<["-"], "lazy_library">,
+ MetaVarName<"<path>">,
+ HelpText<"This option is deprecated and is now an alias for regular linking">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def lazy_l : Joined<["-"], "lazy-l">,
+ MetaVarName<"<name>">,
+ HelpText<"This option is deprecated and is now an alias for -l<path>.">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def single_module : Flag<["-"], "single_module">,
+ HelpText<"Unnecessary option: this is already the default">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def no_dead_strip_inits_and_terms : Flag<["-"], "no_dead_strip_inits_and_terms">,
+ HelpText<"Unnecessary option: initialization and termination are roots of the dead strip graph, so never dead stripped">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+def noall_load : Flag<["-"], "noall_load">,
+ HelpText<"Unnecessary option: this is already the default">,
+ Flags<[HelpHidden]>,
+ Group<grp_deprecated>;
+
+def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE">;
+
+def y : Joined<["-"], "y">,
+ MetaVarName<"<symbol>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def sectobjectsymbols : MultiArg<["-"], "sectobjectsymbols", 2>,
+ MetaVarName<"<segname> <sectname>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def nofixprebinding : Flag<["-"], "nofixprebinding">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def noprebind_all_twolevel_modules : Flag<["-"], "noprebind_all_twolevel_modules">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def prebind_all_twolevel_modules : Flag<["-"], "prebind_all_twolevel_modules">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def prebind_allow_overlap : Flag<["-"], "prebind_allow_overlap">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def noprebind : Flag<["-"], "noprebind">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def sect_diff_relocs : Separate<["-"], "sect_diff_relocs">,
+ MetaVarName<"<treatment>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def A : Separate<["-"], "A">,
+ MetaVarName<"<basefile>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def b : Flag<["-"], "b">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def Sn : Flag<["-"], "Sn">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def Si : Flag<["-"], "Si">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def Sp : Flag<["-"], "Sp">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def X : Flag<["-"], "X">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def s : Flag<["-"], "s">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def m : Flag<["-"], "m">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def Y : Separate<["-"], "Y">,
+ MetaVarName<"<number>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def nomultidefs : Flag<["-"], "nomultidefs">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def multiply_defined_unused : Separate<["-"], "multiply_defined_unused">,
+ MetaVarName<"<treatment>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def multiply_defined : Separate<["-"], "multiply_defined">,
+ MetaVarName<"<treatment>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def private_bundle : Flag<["-"], "private_bundle">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def seg_addr_table_filename : Separate<["-"], "seg_addr_table_filename">,
+ MetaVarName<"<path>">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def sectorder_detail : Flag<["-"], "sectorder_detail">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def no_compact_linkedit : Flag<["-"], "no_compact_linkedit">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def dependent_dr_info : Flag<["-"], "dependent_dr_info">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def no_dependent_dr_info : Flag<["-"], "no_dependent_dr_info">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def seglinkedit : Flag<["-"], "seglinkedit">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def noseglinkedit : Flag<["-"], "noseglinkedit">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def fvmlib : Flag<["-"], "fvmlib">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+def run_init_lazily : Flag<["-"], "run_init_lazily">,
+ HelpText<"This option is obsolete in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_obsolete>;
+
+def grp_undocumented : OptionGroup<"undocumented">, HelpText<"UNDOCUMENTED">;
+
+def add_ast_path : Flag<["-"], "add_ast_path">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def add_linker_option : Flag<["-"], "add_linker_option">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def add_source_version : Flag<["-"], "add_source_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_source_version : Flag<["-"], "no_source_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def add_split_seg_info : Flag<["-"], "add_split_seg_info">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def allow_dead_duplicates : Flag<["-"], "allow_dead_duplicates">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def allow_simulator_linking_to_macosx_dylibs : Flag<["-"], "allow_simulator_linking_to_macosx_dylibs">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def bitcode_process_mode : Flag<["-"], "bitcode_process_mode">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def bitcode_verify : Flag<["-"], "bitcode_verify">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def classic_linker : Flag<["-"], "classic_linker">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def data_const : Flag<["-"], "data_const">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_data_const : Flag<["-"], "no_data_const">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def data_in_code_info : Flag<["-"], "data_in_code_info">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_data_in_code_info : Flag<["-"], "no_data_in_code_info">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def debug_snapshot : Flag<["-"], "debug_snapshot">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def demangle : Flag<["-"], "demangle">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def dependency_info : Flag<["-"], "dependency_info">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def dyld_env : Flag<["-"], "dyld_env">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def encryptable : Flag<["-"], "encryptable">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def executable_path : Flag<["-"], "executable_path">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def fixup_chains : Flag<["-"], "fixup_chains">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def fixup_chains_section : Flag<["-"], "fixup_chains_section">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def flto_codegen_only : Flag<["-"], "flto-codegen-only">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_load_swift_libs : Flag<["-"], "force_load_swift_libs">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbol_not_weak : Flag<["-"], "force_symbol_not_weak">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbols_coalesce_list : Flag<["-"], "force_symbols_coalesce_list">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbols_not_weak_list : Flag<["-"], "force_symbols_not_weak_list">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbols_weak_list : Flag<["-"], "force_symbols_weak_list">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def force_symbol_weak : Flag<["-"], "force_symbol_weak">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def function_starts : Flag<["-"], "function_starts">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def i : Flag<["-"], "i">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def ignore_auto_link : Flag<["-"], "ignore_auto_link">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def ignore_optimization_hints : Flag<["-"], "ignore_optimization_hints">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def init_offsets : Flag<["-"], "init_offsets">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def keep_dwarf_unwind : Flag<["-"], "keep_dwarf_unwind">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_keep_dwarf_unwind : Flag<["-"], "no_keep_dwarf_unwind">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def kext : Flag<["-"], "kext">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def kext_objects_dir : Flag<["-"], "kext_objects_dir">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_kext_objects : Flag<["-"], "no_kext_objects">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def kexts_use_stubs : Flag<["-"], "kexts_use_stubs">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def maccatalyst_version_min : Flag<["-"], "maccatalyst_version_min">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def iosmac_version_min : Flag<["-"], "iosmac_version_min">,
+ Alias<maccatalyst_version_min>,
+ HelpText<"Alias for -maccatalyst_version_min">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def uikitformac_version_min : Flag<["-"], "uikitformac_version_min">,
+ Alias<maccatalyst_version_min>,
+ HelpText<"Alias for -maccatalyst_version_min">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def mcpu : Flag<["-"], "mcpu">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def mllvm : Flag<["-"], "mllvm">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_compact_unwind : Flag<["-"], "no_compact_unwind">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_dtrace_dof : Flag<["-"], "no_dtrace_dof">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_encryption : Flag<["-"], "no_encryption">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def no_new_main : Flag<["-"], "no_new_main">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def objc_abi_version : Flag<["-"], "objc_abi_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def pause : Flag<["-"], "pause">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def random_uuid : Flag<["-"], "random_uuid">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def save_temps : Flag<["-"], "save-temps">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def sdk_version : Flag<["-"], "sdk_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def simulator_support : Flag<["-"], "simulator_support">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def snapshot_dir : Flag<["-"], "snapshot_dir">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def source_version : Flag<["-"], "source_version">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def text_exec : Flag<["-"], "text_exec">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def threaded_starts_section : Flag<["-"], "threaded_starts_section">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def twolevel_namespace : Flag<["-"], "twolevel_namespace">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def verbose_optimization_hints : Flag<["-"], "verbose_optimization_hints">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+def version_load_command : Flag<["-"], "version_load_command">,
+ HelpText<"This option is undocumented in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_undocumented>;
+
+def grp_ignored : OptionGroup<"ignored">, HelpText<"IGNORED">;
+
+def M : Flag<["-"], "M">,
+ HelpText<"This option is ignored in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_ignored>;
+def new_linker : Flag<["-"], "new_linker">,
+ HelpText<"This option is ignored in ld64">,
+ Flags<[HelpHidden]>,
+ Group<grp_ignored>;
--- /dev/null
+//===- OutputSection.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "OutputSection.h"
+#include "OutputSegment.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+
+uint64_t OutputSection::getSegmentOffset() const {
+ return addr - parent->firstSection()->addr;
+}
--- /dev/null
+//===- OutputSection.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_OUTPUT_SECTION_H
+#define LLD_MACHO_OUTPUT_SECTION_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace lld {
+namespace macho {
+
+class InputSection;
+class OutputSegment;
+
+// Output sections represent the finalized sections present within the final
+// linked executable. They can represent special sections (like the symbol
+// table), or represent coalesced sections from the various inputs given to the
+// linker with the same segment / section name.
+class OutputSection {
+public:
+ enum Kind {
+ MergedKind,
+ SyntheticKind,
+ };
+
+ OutputSection(Kind kind, StringRef name) : name(name), sectionKind(kind) {}
+ virtual ~OutputSection() = default;
+ Kind kind() const { return sectionKind; }
+
+ // These accessors will only be valid after finalizing the section.
+ uint64_t getSegmentOffset() const;
+
+ // How much space the section occupies in the address space.
+ virtual uint64_t getSize() const = 0;
+ // How much space the section occupies in the file. Most sections are copied
+ // as-is so their file size is the same as their address space size.
+ virtual uint64_t getFileSize() const { return getSize(); }
+
+ // Hidden sections omit header content, but body content may still be present.
+ virtual bool isHidden() const { return false; }
+ // Unneeded sections are omitted entirely (header and body).
+ virtual bool isNeeded() const { return true; }
+
+ // Specifically finalizes addresses and section size, not content.
+ virtual void finalize() {
+ // TODO investigate refactoring synthetic section finalization logic into
+ // overrides of this function.
+ }
+
+ virtual void writeTo(uint8_t *buf) const = 0;
+
+ StringRef name;
+ OutputSegment *parent = nullptr;
+
+ uint32_t index = 0;
+ uint64_t addr = 0;
+ uint64_t fileOff = 0;
+ uint32_t align = 1;
+ uint32_t flags = 0;
+
+private:
+ Kind sectionKind;
+};
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- OutputSegment.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "OutputSegment.h"
+#include "InputSection.h"
+#include "MergedOutputSection.h"
+#include "SyntheticSections.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+static uint32_t initProt(StringRef name) {
+ if (name == segment_names::text)
+ return VM_PROT_READ | VM_PROT_EXECUTE;
+ if (name == segment_names::pageZero)
+ return 0;
+ if (name == segment_names::linkEdit)
+ return VM_PROT_READ;
+ return VM_PROT_READ | VM_PROT_WRITE;
+}
+
+static uint32_t maxProt(StringRef name) {
+ if (name == segment_names::pageZero)
+ return 0;
+ return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+}
+
+size_t OutputSegment::numNonHiddenSections() const {
+ size_t count = 0;
+ for (const OutputSection *osec : sections) {
+ count += (!osec->isHidden() ? 1 : 0);
+ }
+ return count;
+}
+
+void OutputSegment::addOutputSection(OutputSection *osec) {
+ osec->parent = this;
+ sections.push_back(osec);
+}
+
+static llvm::DenseMap<StringRef, OutputSegment *> nameToOutputSegment;
+std::vector<OutputSegment *> macho::outputSegments;
+
+OutputSegment *macho::getOrCreateOutputSegment(StringRef name) {
+ OutputSegment *&segRef = nameToOutputSegment[name];
+ if (segRef != nullptr)
+ return segRef;
+
+ segRef = make<OutputSegment>();
+ segRef->name = name;
+ segRef->maxProt = maxProt(name);
+ segRef->initProt = initProt(name);
+
+ outputSegments.push_back(segRef);
+ return segRef;
+}
--- /dev/null
+//===- OutputSegment.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_OUTPUT_SEGMENT_H
+#define LLD_MACHO_OUTPUT_SEGMENT_H
+
+#include "OutputSection.h"
+#include "lld/Common/LLVM.h"
+
+namespace lld {
+namespace macho {
+
+namespace segment_names {
+
+constexpr const char pageZero[] = "__PAGEZERO";
+constexpr const char text[] = "__TEXT";
+constexpr const char data[] = "__DATA";
+constexpr const char linkEdit[] = "__LINKEDIT";
+constexpr const char dataConst[] = "__DATA_CONST";
+
+} // namespace segment_names
+
+class OutputSection;
+class InputSection;
+
+class OutputSegment {
+public:
+ const OutputSection *firstSection() const { return sections.front(); }
+ const OutputSection *lastSection() const { return sections.back(); }
+
+ void addOutputSection(OutputSection *os);
+ void sortOutputSections(
+ llvm::function_ref<bool(OutputSection *, OutputSection *)> comparator) {
+ llvm::stable_sort(sections, comparator);
+ }
+
+ const std::vector<OutputSection *> &getSections() const { return sections; }
+ size_t numNonHiddenSections() const;
+
+ uint64_t fileOff = 0;
+ StringRef name;
+ uint32_t maxProt = 0;
+ uint32_t initProt = 0;
+ uint8_t index;
+
+private:
+ std::vector<OutputSection *> sections;
+};
+
+extern std::vector<OutputSegment *> outputSegments;
+
+OutputSegment *getOrCreateOutputSegment(StringRef name);
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- SymbolTable.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolTable.h"
+#include "InputFiles.h"
+#include "Symbols.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+
+Symbol *SymbolTable::find(StringRef name) {
+ auto it = symMap.find(llvm::CachedHashStringRef(name));
+ if (it == symMap.end())
+ return nullptr;
+ return symVector[it->second];
+}
+
+std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
+ auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()});
+
+ // Name already present in the symbol table.
+ if (!p.second)
+ return {symVector[p.first->second], false};
+
+ // Name is a new symbol.
+ Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ symVector.push_back(sym);
+ return {sym, true};
+}
+
+Symbol *SymbolTable::addDefined(StringRef name, InputSection *isec,
+ uint32_t value) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+
+ if (!wasInserted && isa<Defined>(s))
+ error("duplicate symbol: " + name);
+
+ replaceSymbol<Defined>(s, name, isec, value);
+ return s;
+}
+
+Symbol *SymbolTable::addUndefined(StringRef name) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+
+ if (wasInserted)
+ replaceSymbol<Undefined>(s, name);
+ else if (LazySymbol *lazy = dyn_cast<LazySymbol>(s))
+ lazy->fetchArchiveMember();
+ return s;
+}
+
+Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+
+ if (wasInserted || isa<Undefined>(s))
+ replaceSymbol<DylibSymbol>(s, file, name);
+ return s;
+}
+
+Symbol *SymbolTable::addLazy(StringRef name, ArchiveFile *file,
+ const llvm::object::Archive::Symbol &sym) {
+ Symbol *s;
+ bool wasInserted;
+ std::tie(s, wasInserted) = insert(name);
+
+ if (wasInserted)
+ replaceSymbol<LazySymbol>(s, file, sym);
+ else if (isa<Undefined>(s))
+ file->fetch(sym);
+ return s;
+}
+
+SymbolTable *macho::symtab;
--- /dev/null
+//===- SymbolTable.h --------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_SYMBOL_TABLE_H
+#define LLD_MACHO_SYMBOL_TABLE_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/CachedHashString.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Object/Archive.h"
+
+namespace lld {
+namespace macho {
+
+class ArchiveFile;
+class DylibFile;
+class InputSection;
+class Symbol;
+
+class SymbolTable {
+public:
+ Symbol *addDefined(StringRef name, InputSection *isec, uint32_t value);
+
+ Symbol *addUndefined(StringRef name);
+
+ Symbol *addDylib(StringRef name, DylibFile *file);
+
+ Symbol *addLazy(StringRef name, ArchiveFile *file,
+ const llvm::object::Archive::Symbol &sym);
+
+ ArrayRef<Symbol *> getSymbols() const { return symVector; }
+ Symbol *find(StringRef name);
+
+private:
+ std::pair<Symbol *, bool> insert(StringRef name);
+ llvm::DenseMap<llvm::CachedHashStringRef, int> symMap;
+ std::vector<Symbol *> symVector;
+};
+
+extern SymbolTable *symtab;
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- Symbols.cpp --------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Symbols.h"
+#include "InputFiles.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+
+void LazySymbol::fetchArchiveMember() { file->fetch(sym); }
+
+// Returns a symbol for an error message.
+std::string lld::toString(const Symbol &sym) {
+ if (Optional<std::string> s = demangleItanium(sym.getName()))
+ return *s;
+ return std::string(sym.getName());
+}
--- /dev/null
+//===- Symbols.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_SYMBOLS_H
+#define LLD_MACHO_SYMBOLS_H
+
+#include "InputSection.h"
+#include "Target.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Strings.h"
+#include "llvm/Object/Archive.h"
+
+namespace lld {
+namespace macho {
+
+class InputSection;
+class DylibFile;
+class ArchiveFile;
+
+struct StringRefZ {
+ StringRefZ(const char *s) : data(s), size(-1) {}
+ StringRefZ(StringRef s) : data(s.data()), size(s.size()) {}
+
+ const char *data;
+ const uint32_t size;
+};
+
+class Symbol {
+public:
+ enum Kind {
+ DefinedKind,
+ UndefinedKind,
+ DylibKind,
+ LazyKind,
+ };
+
+ Kind kind() const { return static_cast<Kind>(symbolKind); }
+
+ StringRef getName() const { return {name.data, name.size}; }
+
+ uint64_t getVA() const;
+
+ uint64_t getFileOffset() const;
+
+ uint32_t gotIndex = UINT32_MAX;
+
+protected:
+ Symbol(Kind k, StringRefZ name) : symbolKind(k), name(name) {}
+
+ Kind symbolKind;
+ StringRefZ name;
+};
+
+class Defined : public Symbol {
+public:
+ Defined(StringRefZ name, InputSection *isec, uint32_t value)
+ : Symbol(DefinedKind, name), isec(isec), value(value) {}
+
+ InputSection *isec;
+ uint32_t value;
+
+ static bool classof(const Symbol *s) { return s->kind() == DefinedKind; }
+};
+
+class Undefined : public Symbol {
+public:
+ Undefined(StringRefZ name) : Symbol(UndefinedKind, name) {}
+
+ static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
+};
+
+class DylibSymbol : public Symbol {
+public:
+ DylibSymbol(DylibFile *file, StringRefZ name)
+ : Symbol(DylibKind, name), file(file) {}
+
+ static bool classof(const Symbol *s) { return s->kind() == DylibKind; }
+
+ DylibFile *file;
+ uint32_t stubsIndex = UINT32_MAX;
+ uint32_t lazyBindOffset = UINT32_MAX;
+};
+
+class LazySymbol : public Symbol {
+public:
+ LazySymbol(ArchiveFile *file, const llvm::object::Archive::Symbol &sym)
+ : Symbol(LazyKind, sym.getName()), file(file), sym(sym) {}
+
+ static bool classof(const Symbol *s) { return s->kind() == LazyKind; }
+
+ void fetchArchiveMember();
+
+private:
+ ArchiveFile *file;
+ const llvm::object::Archive::Symbol sym;
+};
+
+inline uint64_t Symbol::getVA() const {
+ if (auto *d = dyn_cast<Defined>(this))
+ return d->isec->getVA() + d->value;
+ return 0;
+}
+
+inline uint64_t Symbol::getFileOffset() const {
+ if (auto *d = dyn_cast<Defined>(this))
+ return d->isec->getFileOffset() + d->value;
+ llvm_unreachable("attempt to get an offset from an undefined symbol");
+}
+
+union SymbolUnion {
+ alignas(Defined) char a[sizeof(Defined)];
+ alignas(Undefined) char b[sizeof(Undefined)];
+ alignas(DylibSymbol) char c[sizeof(DylibSymbol)];
+ alignas(LazySymbol) char d[sizeof(LazySymbol)];
+};
+
+template <typename T, typename... ArgT>
+void replaceSymbol(Symbol *s, ArgT &&... arg) {
+ static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
+ static_assert(alignof(T) <= alignof(SymbolUnion),
+ "SymbolUnion not aligned enough");
+ assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
+ "Not a Symbol");
+
+ new (s) T(std::forward<ArgT>(arg)...);
+}
+
+} // namespace macho
+
+std::string toString(const macho::Symbol &);
+} // namespace lld
+
+#endif
--- /dev/null
+//===- SyntheticSections.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SyntheticSections.h"
+#include "Config.h"
+#include "ExportTrie.h"
+#include "InputFiles.h"
+#include "MachOStructs.h"
+#include "MergedOutputSection.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "Writer.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/LEB128.h"
+
+using namespace llvm;
+using namespace llvm::support;
+using namespace llvm::support::endian;
+using namespace lld;
+using namespace lld::macho;
+
+InStruct macho::in;
+std::vector<SyntheticSection *> macho::syntheticSections;
+
+SyntheticSection::SyntheticSection(const char *segname, const char *name)
+ : OutputSection(SyntheticKind, name), segname(segname) {
+ syntheticSections.push_back(this);
+}
+
+// dyld3's MachOLoaded::getSlide() assumes that the __TEXT segment starts
+// from the beginning of the file (i.e. the header).
+MachHeaderSection::MachHeaderSection()
+ : SyntheticSection(segment_names::text, section_names::header) {}
+
+void MachHeaderSection::addLoadCommand(LoadCommand *lc) {
+ loadCommands.push_back(lc);
+ sizeOfCmds += lc->getSize();
+}
+
+uint64_t MachHeaderSection::getSize() const {
+ return sizeof(MachO::mach_header_64) + sizeOfCmds;
+}
+
+void MachHeaderSection::writeTo(uint8_t *buf) const {
+ auto *hdr = reinterpret_cast<MachO::mach_header_64 *>(buf);
+ hdr->magic = MachO::MH_MAGIC_64;
+ hdr->cputype = MachO::CPU_TYPE_X86_64;
+ hdr->cpusubtype = MachO::CPU_SUBTYPE_X86_64_ALL | MachO::CPU_SUBTYPE_LIB64;
+ hdr->filetype = config->outputType;
+ hdr->ncmds = loadCommands.size();
+ hdr->sizeofcmds = sizeOfCmds;
+ hdr->flags = MachO::MH_NOUNDEFS | MachO::MH_DYLDLINK | MachO::MH_TWOLEVEL;
+ if (config->outputType == MachO::MH_DYLIB && !config->hasReexports)
+ hdr->flags |= MachO::MH_NO_REEXPORTED_DYLIBS;
+
+ uint8_t *p = reinterpret_cast<uint8_t *>(hdr + 1);
+ for (LoadCommand *lc : loadCommands) {
+ lc->writeTo(p);
+ p += lc->getSize();
+ }
+}
+
+PageZeroSection::PageZeroSection()
+ : SyntheticSection(segment_names::pageZero, section_names::pageZero) {}
+
+GotSection::GotSection()
+ : SyntheticSection(segment_names::dataConst, section_names::got) {
+ align = 8;
+ flags = MachO::S_NON_LAZY_SYMBOL_POINTERS;
+
+ // TODO: section_64::reserved1 should be an index into the indirect symbol
+ // table, which we do not currently emit
+}
+
+void GotSection::addEntry(Symbol &sym) {
+ if (entries.insert(&sym)) {
+ sym.gotIndex = entries.size() - 1;
+ }
+}
+
+void GotSection::writeTo(uint8_t *buf) const {
+ for (size_t i = 0, n = entries.size(); i < n; ++i)
+ if (auto *defined = dyn_cast<Defined>(entries[i]))
+ write64le(&buf[i * WordSize], defined->getVA());
+}
+
+BindingSection::BindingSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::binding) {}
+
+bool BindingSection::isNeeded() const {
+ return bindings.size() != 0 || in.got->isNeeded();
+}
+
+namespace {
+struct Binding {
+ OutputSegment *segment = nullptr;
+ uint64_t offset = 0;
+ int64_t addend = 0;
+ uint8_t ordinal = 0;
+};
+} // namespace
+
+// Encode a sequence of opcodes that tell dyld to write the address of dysym +
+// addend at osec->addr + outSecOff.
+//
+// The bind opcode "interpreter" remembers the values of each binding field, so
+// we only need to encode the differences between bindings. Hence the use of
+// lastBinding.
+static void encodeBinding(const DylibSymbol &dysym, const OutputSection *osec,
+ uint64_t outSecOff, int64_t addend,
+ Binding &lastBinding, raw_svector_ostream &os) {
+ using namespace llvm::MachO;
+ OutputSegment *seg = osec->parent;
+ uint64_t offset = osec->getSegmentOffset() + outSecOff;
+ if (lastBinding.segment != seg) {
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
+ seg->index);
+ encodeULEB128(offset, os);
+ lastBinding.segment = seg;
+ lastBinding.offset = offset;
+ } else if (lastBinding.offset != offset) {
+ assert(lastBinding.offset <= offset);
+ os << static_cast<uint8_t>(BIND_OPCODE_ADD_ADDR_ULEB);
+ encodeULEB128(offset - lastBinding.offset, os);
+ lastBinding.offset = offset;
+ }
+
+ if (lastBinding.ordinal != dysym.file->ordinal) {
+ if (dysym.file->ordinal <= BIND_IMMEDIATE_MASK) {
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ dysym.file->ordinal);
+ } else {
+ error("TODO: Support larger dylib symbol ordinals");
+ return;
+ }
+ lastBinding.ordinal = dysym.file->ordinal;
+ }
+
+ if (lastBinding.addend != addend) {
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_ADDEND_SLEB);
+ encodeSLEB128(addend, os);
+ lastBinding.addend = addend;
+ }
+
+ os << static_cast<uint8_t>(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
+ << dysym.getName() << '\0'
+ << static_cast<uint8_t>(BIND_OPCODE_SET_TYPE_IMM | BIND_TYPE_POINTER)
+ << static_cast<uint8_t>(BIND_OPCODE_DO_BIND);
+ // DO_BIND causes dyld to both perform the binding and increment the offset
+ lastBinding.offset += WordSize;
+}
+
+// Emit bind opcodes, which are a stream of byte-sized opcodes that dyld
+// interprets to update a record with the following fields:
+// * segment index (of the segment to write the symbol addresses to, typically
+// the __DATA_CONST segment which contains the GOT)
+// * offset within the segment, indicating the next location to write a binding
+// * symbol type
+// * symbol library ordinal (the index of its library's LC_LOAD_DYLIB command)
+// * symbol name
+// * addend
+// When dyld sees BIND_OPCODE_DO_BIND, it uses the current record state to bind
+// a symbol in the GOT, and increments the segment offset to point to the next
+// entry. It does *not* clear the record state after doing the bind, so
+// subsequent opcodes only need to encode the differences between bindings.
+void BindingSection::finalizeContents() {
+ raw_svector_ostream os{contents};
+ Binding lastBinding;
+ bool didEncode = false;
+ size_t gotIdx = 0;
+ for (const Symbol *sym : in.got->getEntries()) {
+ if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
+ didEncode = true;
+ encodeBinding(*dysym, in.got, gotIdx * WordSize, 0, lastBinding, os);
+ }
+ ++gotIdx;
+ }
+
+ // Sorting the relocations by segment and address allows us to encode them
+ // more compactly.
+ llvm::sort(bindings, [](const BindingEntry &a, const BindingEntry &b) {
+ OutputSegment *segA = a.isec->parent->parent;
+ OutputSegment *segB = b.isec->parent->parent;
+ if (segA != segB)
+ return segA->fileOff < segB->fileOff;
+ OutputSection *osecA = a.isec->parent;
+ OutputSection *osecB = b.isec->parent;
+ if (osecA != osecB)
+ return osecA->addr < osecB->addr;
+ if (a.isec != b.isec)
+ return a.isec->outSecOff < b.isec->outSecOff;
+ return a.offset < b.offset;
+ });
+ for (const BindingEntry &b : bindings) {
+ didEncode = true;
+ encodeBinding(*b.dysym, b.isec->parent, b.isec->outSecOff + b.offset,
+ b.addend, lastBinding, os);
+ }
+ if (didEncode)
+ os << static_cast<uint8_t>(MachO::BIND_OPCODE_DONE);
+}
+
+void BindingSection::writeTo(uint8_t *buf) const {
+ memcpy(buf, contents.data(), contents.size());
+}
+
+StubsSection::StubsSection()
+ : SyntheticSection(segment_names::text, "__stubs") {}
+
+uint64_t StubsSection::getSize() const {
+ return entries.size() * target->stubSize;
+}
+
+void StubsSection::writeTo(uint8_t *buf) const {
+ size_t off = 0;
+ for (const DylibSymbol *sym : in.stubs->getEntries()) {
+ target->writeStub(buf + off, *sym);
+ off += target->stubSize;
+ }
+}
+
+void StubsSection::addEntry(DylibSymbol &sym) {
+ if (entries.insert(&sym))
+ sym.stubsIndex = entries.size() - 1;
+}
+
+StubHelperSection::StubHelperSection()
+ : SyntheticSection(segment_names::text, "__stub_helper") {}
+
+uint64_t StubHelperSection::getSize() const {
+ return target->stubHelperHeaderSize +
+ in.stubs->getEntries().size() * target->stubHelperEntrySize;
+}
+
+bool StubHelperSection::isNeeded() const {
+ return !in.stubs->getEntries().empty();
+}
+
+void StubHelperSection::writeTo(uint8_t *buf) const {
+ target->writeStubHelperHeader(buf);
+ size_t off = target->stubHelperHeaderSize;
+ for (const DylibSymbol *sym : in.stubs->getEntries()) {
+ target->writeStubHelperEntry(buf + off, *sym, addr + off);
+ off += target->stubHelperEntrySize;
+ }
+}
+
+void StubHelperSection::setup() {
+ stubBinder = dyn_cast_or_null<DylibSymbol>(symtab->find("dyld_stub_binder"));
+ if (stubBinder == nullptr) {
+ error("symbol dyld_stub_binder not found (normally in libSystem.dylib). "
+ "Needed to perform lazy binding.");
+ return;
+ }
+ in.got->addEntry(*stubBinder);
+
+ inputSections.push_back(in.imageLoaderCache);
+ symtab->addDefined("__dyld_private", in.imageLoaderCache, 0);
+}
+
+ImageLoaderCacheSection::ImageLoaderCacheSection() {
+ segname = segment_names::data;
+ name = "__data";
+ uint8_t *arr = bAlloc.Allocate<uint8_t>(WordSize);
+ memset(arr, 0, WordSize);
+ data = {arr, WordSize};
+}
+
+LazyPointerSection::LazyPointerSection()
+ : SyntheticSection(segment_names::data, "__la_symbol_ptr") {
+ align = 8;
+ flags = MachO::S_LAZY_SYMBOL_POINTERS;
+}
+
+uint64_t LazyPointerSection::getSize() const {
+ return in.stubs->getEntries().size() * WordSize;
+}
+
+bool LazyPointerSection::isNeeded() const {
+ return !in.stubs->getEntries().empty();
+}
+
+void LazyPointerSection::writeTo(uint8_t *buf) const {
+ size_t off = 0;
+ for (const DylibSymbol *sym : in.stubs->getEntries()) {
+ uint64_t stubHelperOffset = target->stubHelperHeaderSize +
+ sym->stubsIndex * target->stubHelperEntrySize;
+ write64le(buf + off, in.stubHelper->addr + stubHelperOffset);
+ off += WordSize;
+ }
+}
+
+LazyBindingSection::LazyBindingSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::lazyBinding) {}
+
+bool LazyBindingSection::isNeeded() const { return in.stubs->isNeeded(); }
+
+void LazyBindingSection::finalizeContents() {
+ // TODO: Just precompute output size here instead of writing to a temporary
+ // buffer
+ for (DylibSymbol *sym : in.stubs->getEntries())
+ sym->lazyBindOffset = encode(*sym);
+}
+
+void LazyBindingSection::writeTo(uint8_t *buf) const {
+ memcpy(buf, contents.data(), contents.size());
+}
+
+// Unlike the non-lazy binding section, the bind opcodes in this section aren't
+// interpreted all at once. Rather, dyld will start interpreting opcodes at a
+// given offset, typically only binding a single symbol before it finds a
+// BIND_OPCODE_DONE terminator. As such, unlike in the non-lazy-binding case,
+// we cannot encode just the differences between symbols; we have to emit the
+// complete bind information for each symbol.
+uint32_t LazyBindingSection::encode(const DylibSymbol &sym) {
+ uint32_t opstreamOffset = contents.size();
+ OutputSegment *dataSeg = in.lazyPointers->parent;
+ os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
+ dataSeg->index);
+ uint64_t offset = in.lazyPointers->addr - dataSeg->firstSection()->addr +
+ sym.stubsIndex * WordSize;
+ encodeULEB128(offset, os);
+ if (sym.file->ordinal <= MachO::BIND_IMMEDIATE_MASK)
+ os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
+ sym.file->ordinal);
+ else
+ fatal("TODO: Support larger dylib symbol ordinals");
+
+ os << static_cast<uint8_t>(MachO::BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM)
+ << sym.getName() << '\0'
+ << static_cast<uint8_t>(MachO::BIND_OPCODE_DO_BIND)
+ << static_cast<uint8_t>(MachO::BIND_OPCODE_DONE);
+ return opstreamOffset;
+}
+
+ExportSection::ExportSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::export_) {}
+
+void ExportSection::finalizeContents() {
+ // TODO: We should check symbol visibility.
+ for (const Symbol *sym : symtab->getSymbols())
+ if (auto *defined = dyn_cast<Defined>(sym))
+ trieBuilder.addSymbol(*defined);
+ size = trieBuilder.build();
+}
+
+void ExportSection::writeTo(uint8_t *buf) const { trieBuilder.writeTo(buf); }
+
+SymtabSection::SymtabSection(StringTableSection &stringTableSection)
+ : SyntheticSection(segment_names::linkEdit, section_names::symbolTable),
+ stringTableSection(stringTableSection) {
+ // TODO: When we introduce the SyntheticSections superclass, we should make
+ // all synthetic sections aligned to WordSize by default.
+ align = WordSize;
+}
+
+uint64_t SymtabSection::getSize() const {
+ return symbols.size() * sizeof(structs::nlist_64);
+}
+
+void SymtabSection::finalizeContents() {
+ // TODO support other symbol types
+ for (Symbol *sym : symtab->getSymbols())
+ if (isa<Defined>(sym))
+ symbols.push_back({sym, stringTableSection.addString(sym->getName())});
+}
+
+void SymtabSection::writeTo(uint8_t *buf) const {
+ auto *nList = reinterpret_cast<structs::nlist_64 *>(buf);
+ for (const SymtabEntry &entry : symbols) {
+ nList->n_strx = entry.strx;
+ // TODO support other symbol types
+ // TODO populate n_desc
+ if (auto *defined = dyn_cast<Defined>(entry.sym)) {
+ nList->n_type = MachO::N_EXT | MachO::N_SECT;
+ nList->n_sect = defined->isec->parent->index;
+ // For the N_SECT symbol type, n_value is the address of the symbol
+ nList->n_value = defined->value + defined->isec->getVA();
+ }
+ ++nList;
+ }
+}
+
+StringTableSection::StringTableSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::stringTable) {}
+
+uint32_t StringTableSection::addString(StringRef str) {
+ uint32_t strx = size;
+ strings.push_back(str);
+ size += str.size() + 1; // account for null terminator
+ return strx;
+}
+
+void StringTableSection::writeTo(uint8_t *buf) const {
+ uint32_t off = 0;
+ for (StringRef str : strings) {
+ memcpy(buf + off, str.data(), str.size());
+ off += str.size() + 1; // account for null terminator
+ }
+}
--- /dev/null
+//===- SyntheticSections.h -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_SYNTHETIC_SECTIONS_H
+#define LLD_MACHO_SYNTHETIC_SECTIONS_H
+
+#include "Config.h"
+#include "ExportTrie.h"
+#include "InputSection.h"
+#include "OutputSection.h"
+#include "Target.h"
+
+#include "llvm/ADT/SetVector.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace lld {
+namespace macho {
+
+namespace section_names {
+
+constexpr const char pageZero[] = "__pagezero";
+constexpr const char header[] = "__mach_header";
+constexpr const char binding[] = "__binding";
+constexpr const char lazyBinding[] = "__lazy_binding";
+constexpr const char export_[] = "__export";
+constexpr const char symbolTable[] = "__symbol_table";
+constexpr const char stringTable[] = "__string_table";
+constexpr const char got[] = "__got";
+
+} // namespace section_names
+
+class DylibSymbol;
+class LoadCommand;
+
+class SyntheticSection : public OutputSection {
+public:
+ SyntheticSection(const char *segname, const char *name);
+ virtual ~SyntheticSection() = default;
+
+ static bool classof(const OutputSection *sec) {
+ return sec->kind() == SyntheticKind;
+ }
+
+ const StringRef segname;
+};
+
+// The header of the Mach-O file, which must have a file offset of zero.
+class MachHeaderSection : public SyntheticSection {
+public:
+ MachHeaderSection();
+ void addLoadCommand(LoadCommand *);
+ bool isHidden() const override { return true; }
+ uint64_t getSize() const override;
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ std::vector<LoadCommand *> loadCommands;
+ uint32_t sizeOfCmds = 0;
+};
+
+// A hidden section that exists solely for the purpose of creating the
+// __PAGEZERO segment, which is used to catch null pointer dereferences.
+class PageZeroSection : public SyntheticSection {
+public:
+ PageZeroSection();
+ bool isHidden() const override { return true; }
+ uint64_t getSize() const override { return PageZeroSize; }
+ uint64_t getFileSize() const override { return 0; }
+ void writeTo(uint8_t *buf) const override {}
+};
+
+// This section will be populated by dyld with addresses to non-lazily-loaded
+// dylib symbols.
+class GotSection : public SyntheticSection {
+public:
+ GotSection();
+
+ const llvm::SetVector<const Symbol *> &getEntries() const { return entries; }
+
+ bool isNeeded() const override { return !entries.empty(); }
+
+ uint64_t getSize() const override { return entries.size() * WordSize; }
+
+ void writeTo(uint8_t *buf) const override;
+
+ void addEntry(Symbol &sym);
+
+private:
+ llvm::SetVector<const Symbol *> entries;
+};
+
+struct BindingEntry {
+ const DylibSymbol *dysym;
+ const InputSection *isec;
+ uint64_t offset;
+ int64_t addend;
+ BindingEntry(const DylibSymbol *dysym, const InputSection *isec,
+ uint64_t offset, int64_t addend)
+ : dysym(dysym), isec(isec), offset(offset), addend(addend) {}
+};
+
+// Stores bind opcodes for telling dyld which symbols to load non-lazily.
+class BindingSection : public SyntheticSection {
+public:
+ BindingSection();
+ void finalizeContents();
+ uint64_t getSize() const override { return contents.size(); }
+ // Like other sections in __LINKEDIT, the binding section is special: its
+ // offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in
+ // section headers.
+ bool isHidden() const override { return true; }
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) const override;
+
+ void addEntry(const DylibSymbol *dysym, const InputSection *isec,
+ uint64_t offset, int64_t addend) {
+ bindings.emplace_back(dysym, isec, offset, addend);
+ }
+
+private:
+ std::vector<BindingEntry> bindings;
+ SmallVector<char, 128> contents;
+};
+
+// The following sections implement lazy symbol binding -- very similar to the
+// PLT mechanism in ELF.
+//
+// ELF's .plt section is broken up into two sections in Mach-O: StubsSection and
+// StubHelperSection. Calls to functions in dylibs will end up calling into
+// StubsSection, which contains indirect jumps to addresses stored in the
+// LazyPointerSection (the counterpart to ELF's .plt.got).
+//
+// Initially, the LazyPointerSection contains addresses that point into one of
+// the entry points in the middle of the StubHelperSection. The code in
+// StubHelperSection will push on the stack an offset into the
+// LazyBindingSection. The push is followed by a jump to the beginning of the
+// StubHelperSection (similar to PLT0), which then calls into dyld_stub_binder.
+// dyld_stub_binder is a non-lazily-bound symbol, so this call looks it up in
+// the GOT.
+//
+// The stub binder will look up the bind opcodes in the LazyBindingSection at
+// the given offset. The bind opcodes will tell the binder to update the address
+// in the LazyPointerSection to point to the symbol, so that subsequent calls
+// don't have to redo the symbol resolution. The binder will then jump to the
+// resolved symbol.
+
+class StubsSection : public SyntheticSection {
+public:
+ StubsSection();
+ uint64_t getSize() const override;
+ bool isNeeded() const override { return !entries.empty(); }
+ void writeTo(uint8_t *buf) const override;
+
+ const llvm::SetVector<DylibSymbol *> &getEntries() const { return entries; }
+
+ void addEntry(DylibSymbol &sym);
+
+private:
+ llvm::SetVector<DylibSymbol *> entries;
+};
+
+class StubHelperSection : public SyntheticSection {
+public:
+ StubHelperSection();
+ uint64_t getSize() const override;
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) const override;
+
+ void setup();
+
+ DylibSymbol *stubBinder = nullptr;
+};
+
+// This section contains space for just a single word, and will be used by dyld
+// to cache an address to the image loader it uses. Note that unlike the other
+// synthetic sections, which are OutputSections, the ImageLoaderCacheSection is
+// an InputSection that gets merged into the __data OutputSection.
+class ImageLoaderCacheSection : public InputSection {
+public:
+ ImageLoaderCacheSection();
+ uint64_t getSize() const override { return WordSize; }
+};
+
+class LazyPointerSection : public SyntheticSection {
+public:
+ LazyPointerSection();
+ uint64_t getSize() const override;
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) const override;
+};
+
+class LazyBindingSection : public SyntheticSection {
+public:
+ LazyBindingSection();
+ void finalizeContents();
+ uint64_t getSize() const override { return contents.size(); }
+ uint32_t encode(const DylibSymbol &);
+ // Like other sections in __LINKEDIT, the lazy binding section is special: its
+ // offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in
+ // section headers.
+ bool isHidden() const override { return true; }
+ bool isNeeded() const override;
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ SmallVector<char, 128> contents;
+ llvm::raw_svector_ostream os{contents};
+};
+
+// Stores a trie that describes the set of exported symbols.
+class ExportSection : public SyntheticSection {
+public:
+ ExportSection();
+ void finalizeContents();
+ uint64_t getSize() const override { return size; }
+ // Like other sections in __LINKEDIT, the export section is special: its
+ // offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in
+ // section headers.
+ bool isHidden() const override { return true; }
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ TrieBuilder trieBuilder;
+ size_t size = 0;
+};
+
+// Stores the strings referenced by the symbol table.
+class StringTableSection : public SyntheticSection {
+public:
+ StringTableSection();
+ // Returns the start offset of the added string.
+ uint32_t addString(StringRef);
+ uint64_t getSize() const override { return size; }
+ // Like other sections in __LINKEDIT, the string table section is special: its
+ // offsets are recorded in the LC_SYMTAB load command, instead of in section
+ // headers.
+ bool isHidden() const override { return true; }
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ // An n_strx value of 0 always indicates the empty string, so we must locate
+ // our non-empty string values at positive offsets in the string table.
+ // Therefore we insert a dummy value at position zero.
+ std::vector<StringRef> strings{"\0"};
+ size_t size = 1;
+};
+
+struct SymtabEntry {
+ Symbol *sym;
+ size_t strx;
+};
+
+class SymtabSection : public SyntheticSection {
+public:
+ SymtabSection(StringTableSection &);
+ void finalizeContents();
+ size_t getNumSymbols() const { return symbols.size(); }
+ uint64_t getSize() const override;
+ // Like other sections in __LINKEDIT, the symtab section is special: its
+ // offsets are recorded in the LC_SYMTAB load command, instead of in section
+ // headers.
+ bool isHidden() const override { return true; }
+ void writeTo(uint8_t *buf) const override;
+
+private:
+ StringTableSection &stringTableSection;
+ std::vector<SymtabEntry> symbols;
+};
+
+struct InStruct {
+ BindingSection *binding = nullptr;
+ GotSection *got = nullptr;
+ LazyPointerSection *lazyPointers = nullptr;
+ StubsSection *stubs = nullptr;
+ StubHelperSection *stubHelper = nullptr;
+ ImageLoaderCacheSection *imageLoaderCache = nullptr;
+};
+
+extern InStruct in;
+extern std::vector<SyntheticSection *> syntheticSections;
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- Target.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Target.h"
+
+using namespace lld;
+using namespace lld::macho;
+
+TargetInfo *macho::target = nullptr;
--- /dev/null
+//===- Target.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_TARGET_H
+#define LLD_MACHO_TARGET_H
+
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include <cstddef>
+#include <cstdint>
+
+namespace lld {
+namespace macho {
+
+class Symbol;
+class DylibSymbol;
+class InputSection;
+struct Reloc;
+
+enum : uint64_t {
+ // We are currently only supporting 64-bit targets since macOS and iOS are
+ // deprecating 32-bit apps.
+ WordSize = 8,
+ PageSize = 4096,
+ PageZeroSize = 1ull << 32, // XXX should be 4096 for 32-bit targets
+ MaxAlignmentPowerOf2 = 32,
+};
+
+class TargetInfo {
+public:
+ virtual ~TargetInfo() = default;
+
+ // Validate the relocation structure and get its addend.
+ virtual uint64_t
+ getImplicitAddend(llvm::MemoryBufferRef, const llvm::MachO::section_64 &,
+ const llvm::MachO::relocation_info &) const = 0;
+ virtual void relocateOne(uint8_t *loc, const Reloc &, uint64_t val) const = 0;
+
+ // Write code for lazy binding. See the comments on StubsSection for more
+ // details.
+ virtual void writeStub(uint8_t *buf, const DylibSymbol &) const = 0;
+ virtual void writeStubHelperHeader(uint8_t *buf) const = 0;
+ virtual void writeStubHelperEntry(uint8_t *buf, const DylibSymbol &,
+ uint64_t entryAddr) const = 0;
+
+ // Symbols may be referenced via either the GOT or the stubs section,
+ // depending on the relocation type. prepareSymbolRelocation() will set up the
+ // GOT/stubs entries, and getSymbolVA() will return the addresses of those
+ // entries.
+ virtual void prepareSymbolRelocation(Symbol &, const InputSection *,
+ const Reloc &) = 0;
+ virtual uint64_t getSymbolVA(const Symbol &, uint8_t type) const = 0;
+
+ uint32_t cpuType;
+ uint32_t cpuSubtype;
+
+ size_t stubSize;
+ size_t stubHelperHeaderSize;
+ size_t stubHelperEntrySize;
+};
+
+TargetInfo *createX86_64TargetInfo();
+
+extern TargetInfo *target;
+
+} // namespace macho
+} // namespace lld
+
+#endif
--- /dev/null
+//===- Writer.cpp ---------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Writer.h"
+#include "Config.h"
+#include "InputFiles.h"
+#include "InputSection.h"
+#include "MergedOutputSection.h"
+#include "OutputSection.h"
+#include "OutputSegment.h"
+#include "SymbolTable.h"
+#include "Symbols.h"
+#include "SyntheticSections.h"
+#include "Target.h"
+
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/LEB128.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+namespace {
+class LCLinkEdit;
+class LCDyldInfo;
+class LCSymtab;
+
+class Writer {
+public:
+ Writer() : buffer(errorHandler().outputBuffer) {}
+
+ void scanRelocations();
+ void createOutputSections();
+ void createLoadCommands();
+ void assignAddresses(OutputSegment *);
+ void createSymtabContents();
+
+ void openFile();
+ void writeSections();
+
+ void run();
+
+ std::unique_ptr<FileOutputBuffer> &buffer;
+ uint64_t addr = 0;
+ uint64_t fileOff = 0;
+ MachHeaderSection *headerSection = nullptr;
+ LazyBindingSection *lazyBindingSection = nullptr;
+ ExportSection *exportSection = nullptr;
+ StringTableSection *stringTableSection = nullptr;
+ SymtabSection *symtabSection = nullptr;
+};
+
+// LC_DYLD_INFO_ONLY stores the offsets of symbol import/export information.
+class LCDyldInfo : public LoadCommand {
+public:
+ LCDyldInfo(BindingSection *bindingSection,
+ LazyBindingSection *lazyBindingSection,
+ ExportSection *exportSection)
+ : bindingSection(bindingSection), lazyBindingSection(lazyBindingSection),
+ exportSection(exportSection) {}
+
+ uint32_t getSize() const override { return sizeof(dyld_info_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<dyld_info_command *>(buf);
+ c->cmd = LC_DYLD_INFO_ONLY;
+ c->cmdsize = getSize();
+ if (bindingSection->isNeeded()) {
+ c->bind_off = bindingSection->fileOff;
+ c->bind_size = bindingSection->getFileSize();
+ }
+ if (lazyBindingSection->isNeeded()) {
+ c->lazy_bind_off = lazyBindingSection->fileOff;
+ c->lazy_bind_size = lazyBindingSection->getFileSize();
+ }
+ if (exportSection->isNeeded()) {
+ c->export_off = exportSection->fileOff;
+ c->export_size = exportSection->getFileSize();
+ }
+ }
+
+ BindingSection *bindingSection;
+ LazyBindingSection *lazyBindingSection;
+ ExportSection *exportSection;
+};
+
+class LCDysymtab : public LoadCommand {
+public:
+ uint32_t getSize() const override { return sizeof(dysymtab_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<dysymtab_command *>(buf);
+ c->cmd = LC_DYSYMTAB;
+ c->cmdsize = getSize();
+ }
+};
+
+class LCSegment : public LoadCommand {
+public:
+ LCSegment(StringRef name, OutputSegment *seg) : name(name), seg(seg) {}
+
+ uint32_t getSize() const override {
+ return sizeof(segment_command_64) +
+ seg->numNonHiddenSections() * sizeof(section_64);
+ }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<segment_command_64 *>(buf);
+ buf += sizeof(segment_command_64);
+
+ c->cmd = LC_SEGMENT_64;
+ c->cmdsize = getSize();
+ memcpy(c->segname, name.data(), name.size());
+ c->fileoff = seg->fileOff;
+ c->maxprot = seg->maxProt;
+ c->initprot = seg->initProt;
+
+ if (seg->getSections().empty())
+ return;
+
+ c->vmaddr = seg->firstSection()->addr;
+ c->vmsize =
+ seg->lastSection()->addr + seg->lastSection()->getSize() - c->vmaddr;
+ c->nsects = seg->numNonHiddenSections();
+
+ for (OutputSection *osec : seg->getSections()) {
+ c->filesize += osec->getFileSize();
+
+ if (osec->isHidden())
+ continue;
+
+ auto *sectHdr = reinterpret_cast<section_64 *>(buf);
+ buf += sizeof(section_64);
+
+ memcpy(sectHdr->sectname, osec->name.data(), osec->name.size());
+ memcpy(sectHdr->segname, name.data(), name.size());
+
+ sectHdr->addr = osec->addr;
+ sectHdr->offset = osec->fileOff;
+ sectHdr->align = Log2_32(osec->align);
+ sectHdr->flags = osec->flags;
+ sectHdr->size = osec->getSize();
+ }
+ }
+
+private:
+ StringRef name;
+ OutputSegment *seg;
+};
+
+class LCMain : public LoadCommand {
+ uint32_t getSize() const override { return sizeof(entry_point_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<entry_point_command *>(buf);
+ c->cmd = LC_MAIN;
+ c->cmdsize = getSize();
+ c->entryoff = config->entry->getFileOffset();
+ c->stacksize = 0;
+ }
+};
+
+class LCSymtab : public LoadCommand {
+public:
+ LCSymtab(SymtabSection *symtabSection, StringTableSection *stringTableSection)
+ : symtabSection(symtabSection), stringTableSection(stringTableSection) {}
+
+ uint32_t getSize() const override { return sizeof(symtab_command); }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<symtab_command *>(buf);
+ c->cmd = LC_SYMTAB;
+ c->cmdsize = getSize();
+ c->symoff = symtabSection->fileOff;
+ c->nsyms = symtabSection->getNumSymbols();
+ c->stroff = stringTableSection->fileOff;
+ c->strsize = stringTableSection->getFileSize();
+ }
+
+ SymtabSection *symtabSection = nullptr;
+ StringTableSection *stringTableSection = nullptr;
+};
+
+// There are several dylib load commands that share the same structure:
+// * LC_LOAD_DYLIB
+// * LC_ID_DYLIB
+// * LC_REEXPORT_DYLIB
+class LCDylib : public LoadCommand {
+public:
+ LCDylib(LoadCommandType type, StringRef path) : type(type), path(path) {}
+
+ uint32_t getSize() const override {
+ return alignTo(sizeof(dylib_command) + path.size() + 1, 8);
+ }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<dylib_command *>(buf);
+ buf += sizeof(dylib_command);
+
+ c->cmd = type;
+ c->cmdsize = getSize();
+ c->dylib.name = sizeof(dylib_command);
+
+ memcpy(buf, path.data(), path.size());
+ buf[path.size()] = '\0';
+ }
+
+private:
+ LoadCommandType type;
+ StringRef path;
+};
+
+class LCLoadDylinker : public LoadCommand {
+public:
+ uint32_t getSize() const override {
+ return alignTo(sizeof(dylinker_command) + path.size() + 1, 8);
+ }
+
+ void writeTo(uint8_t *buf) const override {
+ auto *c = reinterpret_cast<dylinker_command *>(buf);
+ buf += sizeof(dylinker_command);
+
+ c->cmd = LC_LOAD_DYLINKER;
+ c->cmdsize = getSize();
+ c->name = sizeof(dylinker_command);
+
+ memcpy(buf, path.data(), path.size());
+ buf[path.size()] = '\0';
+ }
+
+private:
+ // Recent versions of Darwin won't run any binary that has dyld at a
+ // different location.
+ const StringRef path = "/usr/lib/dyld";
+};
+} // namespace
+
+void Writer::scanRelocations() {
+ for (InputSection *isec : inputSections) {
+ for (Reloc &r : isec->relocs) {
+ if (auto *s = r.target.dyn_cast<lld::macho::Symbol *>()) {
+ if (isa<Undefined>(s))
+ error("undefined symbol " + s->getName() + ", referenced from " +
+ sys::path::filename(isec->file->getName()));
+ else
+ target->prepareSymbolRelocation(*s, isec, r);
+ }
+ }
+ }
+}
+
+void Writer::createLoadCommands() {
+ headerSection->addLoadCommand(
+ make<LCDyldInfo>(in.binding, lazyBindingSection, exportSection));
+ headerSection->addLoadCommand(
+ make<LCSymtab>(symtabSection, stringTableSection));
+ headerSection->addLoadCommand(make<LCDysymtab>());
+
+ switch (config->outputType) {
+ case MH_EXECUTE:
+ headerSection->addLoadCommand(make<LCMain>());
+ headerSection->addLoadCommand(make<LCLoadDylinker>());
+ break;
+ case MH_DYLIB:
+ headerSection->addLoadCommand(
+ make<LCDylib>(LC_ID_DYLIB, config->installName));
+ break;
+ default:
+ llvm_unreachable("unhandled output file type");
+ }
+
+ uint8_t segIndex = 0;
+ for (OutputSegment *seg : outputSegments) {
+ headerSection->addLoadCommand(make<LCSegment>(seg->name, seg));
+ seg->index = segIndex++;
+ }
+
+ uint64_t dylibOrdinal = 1;
+ for (InputFile *file : inputFiles) {
+ if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
+ headerSection->addLoadCommand(
+ make<LCDylib>(LC_LOAD_DYLIB, dylibFile->dylibName));
+ dylibFile->ordinal = dylibOrdinal++;
+
+ if (dylibFile->reexport)
+ headerSection->addLoadCommand(
+ make<LCDylib>(LC_REEXPORT_DYLIB, dylibFile->dylibName));
+ }
+ }
+}
+
+static size_t getSymbolPriority(const SymbolPriorityEntry &entry,
+ const InputFile &file) {
+ return std::max(entry.objectFiles.lookup(sys::path::filename(file.getName())),
+ entry.anyObjectFile);
+}
+
+// Each section gets assigned the priority of the highest-priority symbol it
+// contains.
+static DenseMap<const InputSection *, size_t> buildInputSectionPriorities() {
+ DenseMap<const InputSection *, size_t> sectionPriorities;
+
+ if (config->priorities.empty())
+ return sectionPriorities;
+
+ auto addSym = [&](Defined &sym) {
+ auto it = config->priorities.find(sym.getName());
+ if (it == config->priorities.end())
+ return;
+
+ SymbolPriorityEntry &entry = it->second;
+ size_t &priority = sectionPriorities[sym.isec];
+ priority = std::max(priority, getSymbolPriority(entry, *sym.isec->file));
+ };
+
+ // TODO: Make sure this handles weak symbols correctly.
+ for (InputFile *file : inputFiles)
+ if (isa<ObjFile>(file) || isa<ArchiveFile>(file))
+ for (lld::macho::Symbol *sym : file->symbols)
+ if (auto *d = dyn_cast<Defined>(sym))
+ addSym(*d);
+
+ return sectionPriorities;
+}
+
+static int segmentOrder(OutputSegment *seg) {
+ return StringSwitch<int>(seg->name)
+ .Case(segment_names::pageZero, -2)
+ .Case(segment_names::text, -1)
+ // Make sure __LINKEDIT is the last segment (i.e. all its hidden
+ // sections must be ordered after other sections).
+ .Case(segment_names::linkEdit, std::numeric_limits<int>::max())
+ .Default(0);
+}
+
+static int sectionOrder(OutputSection *osec) {
+ StringRef segname = osec->parent->name;
+ // Sections are uniquely identified by their segment + section name.
+ if (segname == segment_names::text) {
+ if (osec->name == section_names::header)
+ return -1;
+ } else if (segname == segment_names::linkEdit) {
+ return StringSwitch<int>(osec->name)
+ .Case(section_names::binding, -4)
+ .Case(section_names::export_, -3)
+ .Case(section_names::symbolTable, -2)
+ .Case(section_names::stringTable, -1)
+ .Default(0);
+ }
+ // ZeroFill sections must always be the at the end of their segments,
+ // otherwise subsequent sections may get overwritten with zeroes at runtime.
+ if (isZeroFill(osec->flags))
+ return std::numeric_limits<int>::max();
+ return 0;
+}
+
+template <typename T, typename F>
+static std::function<bool(T, T)> compareByOrder(F ord) {
+ return [=](T a, T b) { return ord(a) < ord(b); };
+}
+
+// Sorting only can happen once all outputs have been collected. Here we sort
+// segments, output sections within each segment, and input sections within each
+// output segment.
+static void sortSegmentsAndSections() {
+ llvm::stable_sort(outputSegments,
+ compareByOrder<OutputSegment *>(segmentOrder));
+
+ DenseMap<const InputSection *, size_t> isecPriorities =
+ buildInputSectionPriorities();
+
+ uint32_t sectionIndex = 0;
+ for (OutputSegment *seg : outputSegments) {
+ seg->sortOutputSections(compareByOrder<OutputSection *>(sectionOrder));
+ for (auto *osec : seg->getSections()) {
+ // Now that the output sections are sorted, assign the final
+ // output section indices.
+ if (!osec->isHidden())
+ osec->index = ++sectionIndex;
+
+ if (!isecPriorities.empty()) {
+ if (auto *merged = dyn_cast<MergedOutputSection>(osec)) {
+ llvm::stable_sort(merged->inputs,
+ [&](InputSection *a, InputSection *b) {
+ return isecPriorities[a] > isecPriorities[b];
+ });
+ }
+ }
+ }
+ }
+}
+
+void Writer::createOutputSections() {
+ // First, create hidden sections
+ headerSection = make<MachHeaderSection>();
+ lazyBindingSection = make<LazyBindingSection>();
+ stringTableSection = make<StringTableSection>();
+ symtabSection = make<SymtabSection>(*stringTableSection);
+ exportSection = make<ExportSection>();
+
+ switch (config->outputType) {
+ case MH_EXECUTE:
+ make<PageZeroSection>();
+ break;
+ case MH_DYLIB:
+ break;
+ default:
+ llvm_unreachable("unhandled output file type");
+ }
+
+ // Then merge input sections into output sections.
+ MapVector<std::pair<StringRef, StringRef>, MergedOutputSection *>
+ mergedOutputSections;
+ for (InputSection *isec : inputSections) {
+ MergedOutputSection *&osec =
+ mergedOutputSections[{isec->segname, isec->name}];
+ if (osec == nullptr)
+ osec = make<MergedOutputSection>(isec->name);
+ osec->mergeInput(isec);
+ }
+
+ for (const auto &it : mergedOutputSections) {
+ StringRef segname = it.first.first;
+ MergedOutputSection *osec = it.second;
+ getOrCreateOutputSegment(segname)->addOutputSection(osec);
+ }
+
+ for (SyntheticSection *ssec : syntheticSections) {
+ auto it = mergedOutputSections.find({ssec->segname, ssec->name});
+ if (it == mergedOutputSections.end()) {
+ if (ssec->isNeeded())
+ getOrCreateOutputSegment(ssec->segname)->addOutputSection(ssec);
+ } else {
+ error("section from " + it->second->firstSection()->file->getName() +
+ " conflicts with synthetic section " + ssec->segname + "," +
+ ssec->name);
+ }
+ }
+}
+
+void Writer::assignAddresses(OutputSegment *seg) {
+ addr = alignTo(addr, PageSize);
+ fileOff = alignTo(fileOff, PageSize);
+ seg->fileOff = fileOff;
+
+ for (auto *osec : seg->getSections()) {
+ addr = alignTo(addr, osec->align);
+ fileOff = alignTo(fileOff, osec->align);
+ osec->addr = addr;
+ osec->fileOff = isZeroFill(osec->flags) ? 0 : fileOff;
+ osec->finalize();
+
+ addr += osec->getSize();
+ fileOff += osec->getFileSize();
+ }
+}
+
+void Writer::openFile() {
+ Expected<std::unique_ptr<FileOutputBuffer>> bufferOrErr =
+ FileOutputBuffer::create(config->outputFile, fileOff,
+ FileOutputBuffer::F_executable);
+
+ if (!bufferOrErr)
+ error("failed to open " + config->outputFile + ": " +
+ llvm::toString(bufferOrErr.takeError()));
+ else
+ buffer = std::move(*bufferOrErr);
+}
+
+void Writer::writeSections() {
+ uint8_t *buf = buffer->getBufferStart();
+ for (OutputSegment *seg : outputSegments)
+ for (OutputSection *osec : seg->getSections())
+ osec->writeTo(buf + osec->fileOff);
+}
+
+void Writer::run() {
+ // dyld requires __LINKEDIT segment to always exist (even if empty).
+ OutputSegment *linkEditSegment =
+ getOrCreateOutputSegment(segment_names::linkEdit);
+
+ scanRelocations();
+ if (in.stubHelper->isNeeded())
+ in.stubHelper->setup();
+
+ // Sort and assign sections to their respective segments. No more sections nor
+ // segments may be created after these methods run.
+ createOutputSections();
+ sortSegmentsAndSections();
+
+ createLoadCommands();
+
+ // Ensure that segments (and the sections they contain) are allocated
+ // addresses in ascending order, which dyld requires.
+ //
+ // Note that at this point, __LINKEDIT sections are empty, but we need to
+ // determine addresses of other segments/sections before generating its
+ // contents.
+ for (OutputSegment *seg : outputSegments)
+ if (seg != linkEditSegment)
+ assignAddresses(seg);
+
+ // Fill __LINKEDIT contents.
+ in.binding->finalizeContents();
+ lazyBindingSection->finalizeContents();
+ exportSection->finalizeContents();
+ symtabSection->finalizeContents();
+
+ // Now that __LINKEDIT is filled out, do a proper calculation of its
+ // addresses and offsets.
+ assignAddresses(linkEditSegment);
+
+ openFile();
+ if (errorCount())
+ return;
+
+ writeSections();
+
+ if (auto e = buffer->commit())
+ error("failed to write to the output file: " + toString(std::move(e)));
+}
+
+void macho::writeResult() { Writer().run(); }
+
+void macho::createSyntheticSections() {
+ in.binding = make<BindingSection>();
+ in.got = make<GotSection>();
+ in.lazyPointers = make<LazyPointerSection>();
+ in.stubs = make<StubsSection>();
+ in.stubHelper = make<StubHelperSection>();
+ in.imageLoaderCache = make<ImageLoaderCacheSection>();
+}
--- /dev/null
+//===- Writer.h -------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_WRITER_H
+#define LLD_MACHO_WRITER_H
+
+#include <cstdint>
+
+namespace lld {
+namespace macho {
+
+class LoadCommand {
+public:
+ virtual ~LoadCommand() = default;
+ virtual uint32_t getSize() const = 0;
+ virtual void writeTo(uint8_t *buf) const = 0;
+};
+
+void writeResult();
+
+void createSyntheticSections();
+
+} // namespace macho
+} // namespace lld
+
+#endif
#include "llvm/Option/Option.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#if !defined(_MSC_VER) && !defined(__MINGW32__)
SmallString<128> s;
sys::path::append(s, path1, path2);
if (sys::fs::exists(s))
- return s.str().str();
+ return std::string(s);
return None;
}
add("-lldmap:" + StringRef(a->getValue()));
if (auto *a = args.getLastArg(OPT_reproduce))
add("-reproduce:" + StringRef(a->getValue()));
+ if (auto *a = args.getLastArg(OPT_thinlto_cache_dir))
+ add("-lldltocache:" + StringRef(a->getValue()));
+ if (auto *a = args.getLastArg(OPT_file_alignment))
+ add("-filealign:" + StringRef(a->getValue()));
+ if (auto *a = args.getLastArg(OPT_section_alignment))
+ add("-align:" + StringRef(a->getValue()));
if (auto *a = args.getLastArg(OPT_o))
add("-out:" + StringRef(a->getValue()));
add("-kill-at");
if (args.hasArg(OPT_appcontainer))
add("-appcontainer");
+ if (args.hasArg(OPT_no_seh))
+ add("-noseh");
if (args.getLastArgValue(OPT_m) != "thumb2pe" &&
args.getLastArgValue(OPT_m) != "arm64pe" && !args.hasArg(OPT_dynamicbase))
else
add("-opt:noref");
+ if (args.hasFlag(OPT_enable_auto_import, OPT_disable_auto_import, true))
+ add("-auto-import");
+ else
+ add("-auto-import:no");
+ if (args.hasFlag(OPT_enable_runtime_pseudo_reloc,
+ OPT_disable_runtime_pseudo_reloc, true))
+ add("-runtime-pseudo-reloc");
+ else
+ add("-runtime-pseudo-reloc:no");
+
if (auto *a = args.getLastArg(OPT_icf)) {
StringRef s = a->getValue();
if (s == "all")
HelpText<"Add a directory to the library search path">;
def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">;
def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">;
+def disable_auto_import: F<"disable-auto-import">,
+ HelpText<"Don't automatically import data symbols from other DLLs without dllimport">;
+def disable_runtime_pseudo_reloc: F<"disable-runtime-pseudo-reloc">,
+ HelpText<"Don't do automatic imports that require runtime fixups">;
def dynamicbase: F<"dynamicbase">, HelpText<"Enable ASLR">;
+def enable_auto_import: F<"enable-auto-import">,
+ HelpText<"Automatically import data symbols from other DLLs where needed">;
+def enable_runtime_pseudo_reloc: F<"enable-runtime-pseudo-reloc">,
+ HelpText<"Allow automatic imports that require runtime fixups">;
defm entry: Eq<"entry", "Name of entry point symbol">, MetaVarName<"<entry>">;
def exclude_all_symbols: F<"exclude-all-symbols">,
HelpText<"Don't automatically export any symbols">;
def export_all_symbols: F<"export-all-symbols">,
HelpText<"Export all symbols even if a def file or dllexport attributes are used">;
+defm file_alignment: Eq<"file-alignment", "Set file alignment">;
def gc_sections: F<"gc-sections">, HelpText<"Remove unused sections">;
def help: F<"help">, HelpText<"Print option help">;
def icf: J<"icf=">, HelpText<"Identical code folding">;
"Set the OS and subsystem minor version">;
def no_insert_timestamp: F<"no-insert-timestamp">,
HelpText<"Don't include PE header timestamp">;
+def no_seh: F<"no-seh">, HelpText<"Set the 'no SEH' flag in the executable">;
def no_whole_archive: F<"no-whole-archive">,
HelpText<"No longer include all object files for following archives">;
def large_address_aware: Flag<["--"], "large-address-aware">,
HelpText<"Path to file to write output">;
defm out_implib: Eq<"out-implib", "Import library name">;
defm output_def: Eq<"output-def", "Output def file">;
+defm section_alignment: Eq<"section-alignment", "Set section alignment">;
def shared: F<"shared">, HelpText<"Build a shared object">;
defm subs: Eq<"subsystem", "Specify subsystem">;
def stack: S<"stack">;
defm delayload: Eq<"delayload", "DLL to load only on demand">;
def mllvm: S<"mllvm">;
defm pdb: Eq<"pdb", "Output PDB debug info file, chosen implicitly if the argument is empty">;
+defm thinlto_cache_dir: EqLong<"thinlto-cache-dir",
+ "Path to ThinLTO cached object file directory">;
def Xlink : J<"Xlink=">, MetaVarName<"<arg>">,
HelpText<"Pass <arg> to the COFF linker">;
def: F<"build-id">;
def: F<"disable-auto-image-base">;
def: F<"enable-auto-image-base">;
-def: F<"enable-auto-import">, HelpText<"Ignored; listed for libtool compatibility">;
def: F<"end-group">;
def: Flag<["--"], "full-shutdown">;
def: F<"high-entropy-va">;
def: S<"major-image-version">;
def: S<"minor-image-version">;
-def: F<"no-seh">;
def: F<"nxcompat">;
def: F<"pic-executable">;
def: S<"plugin">;
if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR
NOT LLVM_DISTRIBUTION_COMPONENTS)
- set(export_to_lldtargets EXPORT lldTargets)
+ set(export_to_lldtargets EXPORT LLDTargets)
set_property(GLOBAL PROPERTY LLD_HAS_EXPORTS True)
endif()
if (LLD_BUILD_TOOLS)
if(${name} IN_LIST LLVM_DISTRIBUTION_COMPONENTS OR
NOT LLVM_DISTRIBUTION_COMPONENTS)
- set(export_to_lldtargets EXPORT lldTargets)
+ set(export_to_lldtargets EXPORT LLDTargets)
set_property(GLOBAL PROPERTY LLD_HAS_EXPORTS True)
endif()
--- /dev/null
+# Generate a list of CMake library targets so that other CMake projects can
+# link against them. LLVM calls its version of this file LLVMExports.cmake, but
+# the usual CMake convention seems to be ${Project}Targets.cmake.
+set(LLD_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/lld)
+set(lld_cmake_builddir "${CMAKE_BINARY_DIR}/${LLD_INSTALL_PACKAGE_DIR}")
+
+# Keep this in sync with llvm/cmake/CMakeLists.txt!
+set(LLVM_INSTALL_PACKAGE_DIR lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm)
+set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}")
+
+get_property(LLD_EXPORTS GLOBAL PROPERTY LLD_EXPORTS)
+export(TARGETS ${LLD_EXPORTS} FILE ${lld_cmake_builddir}/LLDTargets.cmake)
+
+# Generate LLDConfig.cmake for the build tree.
+set(LLD_CONFIG_CMAKE_DIR "${lld_cmake_builddir}")
+set(LLD_CONFIG_LLVM_CMAKE_DIR "${llvm_cmake_builddir}")
+set(LLD_CONFIG_EXPORTS_FILE "${lld_cmake_builddir}/LLDTargets.cmake")
+set(LLD_CONFIG_INCLUDE_DIRS
+ "${LLD_SOURCE_DIR}/include"
+ "${LLD_BINARY_DIR}/include"
+ )
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfig.cmake.in
+ ${lld_cmake_builddir}/LLDConfig.cmake
+ @ONLY)
+set(LLD_CONFIG_CMAKE_DIR)
+set(LLD_CONFIG_LLVM_CMAKE_DIR)
+set(LLD_CONFIG_EXPORTS_FILE)
+
+# Generate LLDConfig.cmake for the install tree.
+set(LLD_CONFIG_CODE "
+# Compute the installation prefix from this LLVMConfig.cmake file location.
+get_filename_component(LLD_INSTALL_PREFIX \"\${CMAKE_CURRENT_LIST_FILE}\" PATH)")
+# Construct the proper number of get_filename_component(... PATH)
+# calls to compute the installation prefix.
+string(REGEX REPLACE "/" ";" _count "${LLD_INSTALL_PACKAGE_DIR}")
+foreach(p ${_count})
+ set(LLD_CONFIG_CODE "${LLD_CONFIG_CODE}
+get_filename_component(LLD_INSTALL_PREFIX \"\${LLD_INSTALL_PREFIX}\" PATH)")
+endforeach(p)
+set(LLD_CONFIG_CMAKE_DIR "\${LLD_INSTALL_PREFIX}/${LLD_INSTALL_PACKAGE_DIR}")
+set(LLD_CONFIG_LLVM_CMAKE_DIR "\${LLD_INSTALL_PREFIX}/${LLVM_INSTALL_PACKAGE_DIR}")
+set(LLD_CONFIG_EXPORTS_FILE "\${LLD_CMAKE_DIR}/LLDTargets.cmake")
+set(LLD_CONFIG_INCLUDE_DIRS "\${LLD_INSTALL_PREFIX}/include")
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/LLDConfig.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfig.cmake
+ @ONLY)
+set(LLD_CONFIG_CODE)
+set(LLD_CONFIG_CMAKE_DIR)
+set(LLD_CONFIG_EXPORTS_FILE)
+
+if (NOT LLVM_INSTALL_TOOLCHAIN_ONLY)
+ get_property(lld_has_exports GLOBAL PROPERTY LLD_HAS_EXPORTS)
+ if(lld_has_exports)
+ install(EXPORT LLDTargets DESTINATION ${LLD_INSTALL_PACKAGE_DIR}
+ COMPONENT lld-cmake-exports)
+ endif()
+
+ install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/LLDConfig.cmake
+ DESTINATION ${LLD_INSTALL_PACKAGE_DIR}
+ COMPONENT lld-cmake-exports)
+
+ if(NOT LLVM_ENABLE_IDE)
+ # Add a dummy target so this can be used with LLVM_DISTRIBUTION_COMPONENTS
+ add_custom_target(lld-cmake-exports)
+ add_llvm_install_targets(install-lld-cmake-exports
+ COMPONENT lld-cmake-exports)
+ endif()
+endif()
--- /dev/null
+# This file allows users to call find_package(LLD) and pick up our targets.
+
+@LLD_CONFIG_CODE@
+
+find_package(LLVM REQUIRED CONFIG
+ HINTS "@LLD_CONFIG_LLVM_CMAKE_DIR@")
+
+set(LLD_EXPORTED_TARGETS "@LLD_EXPORTS@")
+set(LLD_CMAKE_DIR "@LLD_CONFIG_CMAKE_DIR@")
+set(LLD_INCLUDE_DIRS "@LLD_CONFIG_INCLUDE_DIRS@")
+
+# Provide all our library targets to users.
+include("@LLD_CONFIG_EXPORTS_FILE@")
* :ref:`genindex`
* :ref:`search`
-__ http://llvm.org/docs/DeveloperPolicy.html#license
+__ https://llvm.org/docs/DeveloperPolicy.html#license
--- /dev/null
+Linker Script implementation notes and policy
+=============================================
+
+LLD implements a large subset of the GNU ld linker script notation. The LLD
+implementation policy is to implement linker script features as they are
+documented in the ld `manual <https://sourceware.org/binutils/docs/ld/Scripts.html>`_
+We consider it a bug if the lld implementation does not agree with the manual
+and it is not mentioned in the exceptions below.
+
+The ld manual is not a complete specification, and is not sufficient to build
+an implementation. In particular some features are only defined by the
+implementation and have changed over time.
+
+The lld implementation policy for properties of linker scripts that are not
+defined by the documentation is to follow the GNU ld implementation wherever
+possible. We reserve the right to make different implementation choices where
+it is appropriate for LLD. Intentional deviations will be documented in this
+file.
+
+Symbol assignment
+~~~~~~~~~~~~~~~~~
+
+A symbol assignment looks like:
+
+::
+
+ symbol = expression;
+ symbol += expression;
+
+The first form defines ``symbol``. If ``symbol`` is already defined, it will be
+overridden. The other form requires ``symbol`` to be already defined.
+
+For a simple assignment like ``alias = aliasee;``, the ``st_type`` field is
+copied from the original symbol. Any arithmetic operation (e.g. ``+ 0`` will
+reset ``st_type`` to ``STT_NOTYPE``.
+
+The ``st_size`` field is set to 0.
+
+Output section description
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The description of an output section looks like:
+
+::
+
+ section [address] [(type)] : [AT(lma)] [ALIGN(section_align)] [SUBALIGN](subsection_align)] {
+ output-section-command
+ ...
+ } [>region] [AT>lma_region] [:phdr ...] [=fillexp] [,]
+
+Output section address
+----------------------
+
+When an *OutputSection* *S* has ``address``, LLD will set sh_addr to ``address``.
+
+The ELF specification says:
+
+> The value of sh_addr must be congruent to 0, modulo the value of sh_addralign.
+
+The presence of ``address`` can cause the condition unsatisfied. LLD will warn.
+GNU ld from Binutils 2.35 onwards will reduce sh_addralign so that
+sh_addr=0 (modulo sh_addralign).
+
+Output section alignment
+------------------------
+
+sh_addralign of an *OutputSection* *S* is the maximum of
+``ALIGN(section_align)`` and the maximum alignment of the input sections in
+*S*.
+
+When an *OutputSection* *S* has both ``address`` and ``ALIGN(section_align)``,
+GNU ld will set sh_addralign to ``ALIGN(section_align)``.
+
+Output section LMA
+------------------
+
+A load address (LMA) can be specified by ``AT(lma)`` or ``AT>lma_region``.
+
+- ``AT(lma)`` specifies the exact load address. If the linker script does not
+ have a PHDRS command, then a new loadable segment will be generated.
+- ``AT>lma_region`` specifies the LMA region. The lack of ``AT>lma_region``
+ means the default region is used. Note, GNU ld propagates the previous LMA
+ memory region when ``address`` is not specified. The LMA is set to the
+ current location of the memory region aligned to the section alignment.
+ If the linker script does not have a PHDRS command, then if
+ ``lma_region`` is different from the ``lma_region`` for
+ the previous OutputSection a new loadable segment will be generated.
+
+The two keywords cannot be specified at the same time.
+
+If neither ``AT(lma)`` nor ``AT>lma_region`` is specified:
+
+- If the previous section is also in the default LMA region, and the two
+ section have the same memory regions, the difference between the LMA and the
+ VMA is computed to be the same as the previous difference.
+- Otherwise, the LMA is set to the VMA.
so that they are linked as if they were in the native format from the beginning.
The details are described in this document.
-http://llvm.org/docs/LinkTimeOptimization.html
+https://llvm.org/docs/LinkTimeOptimization.html
Glossary
--------
========================
-lld 10.0.0 Release Notes
+lld 11.0.0 Release Notes
========================
.. contents::
:local:
-
Introduction
============
-This document contains the release notes for the lld linker, release 10.0.0.
+This document contains the release notes for the lld linker, release 11.0.0.
Here we describe the status of lld, including major improvements
from the previous release. All lld releases may be downloaded
from the `LLVM releases web site <https://llvm.org/releases/>`_.
ELF Improvements
----------------
-* Glob pattern, which you can use in linker scripts or version scripts,
- now supports `\` and `[!...]`. Except character classes
- (e.g. `[[:digit:]]`), lld's glob pattern should be fully compatible
- with GNU now. (`r375051
- <https://github.com/llvm/llvm-project/commit/48993d5ab9413f0e5b94dfa292a233ce55b09e3e>`_)
-
-* New ``elf32btsmipn32_fbsd`` and ``elf32ltsmipn32_fbsd`` emulations
- are supported.
-
-* Relax MIPS ``jalr`` and ``jr`` instructions marked by the ``R_MIPS_JALR``
- relocation.
-* For certain "undefined symbol" errors, a definition with a close spelling will be suggested.
- (`D67039 <https://reviews.llvm.org/D67039>`_)
-* ``extern "C"`` is suggested if an undefined reference is mangled(unmangled) while there
- is a likely unmangled(mangled) definition.
- (`D69592 <https://reviews.llvm.org/D69592>`_ `D69650 <https://reviews.llvm.org/D69650>`_)
-* New ``-z noseparate-code``, ``-z separate-code`` and ``-z separate-loadable-segments``.
- ``-z noseparate-code`` is the default, which can reduce sizes of linked binaries by up to
- 3 times maxpagesize.
- (`D64903 <https://reviews.llvm.org/D64903>`_ `D67481 <https://reviews.llvm.org/D67481>`_)
-* ``-z force-bti`` and ``-z pac-plt`` are added for AArch64 Branch Target Identification and Pointer Authentication.
- (`D62609 <https://reviews.llvm.org/D62609>`_)
-* ``--fix-cortex-a8`` is added to fix erratum 657417.
- (`D67284 <https://reviews.llvm.org/D67284>`_)
-* ``-z force-ibt`` and ``-z shstk`` are added for Intel Control-flow Enforcement Technology.
- (`D59780 <https://reviews.llvm.org/D59780>`_)
-* ``PT_GNU_PROPERTY`` is added to help loaders locate the ``.note.gnu.property`` section.
- It may be used by a future Linux kernel.
- (`D70961 <https://reviews.llvm.org/D70961>`_)
-* For ``--compress-debug-sections=zlib``, ``-O0`` and ``-O1`` enable compression level 1
- while ``-O2`` enables compression level 6. ``-O1`` (default) is faster than before.
- (`D70658 <https://reviews.llvm.org/D70658>`_)
-* Range extension thunks with addends are implemented for AArch64, PowerPC32 and PowerPC64.
- (`D70637 <https://reviews.llvm.org/D70637>`_ `D70937 <https://reviews.llvm.org/D70937>`_
- `D73424 <https://reviews.llvm.org/D73424>`_)
-* ``R_RISCV_ALIGN`` will be errored because linker relaxation for RISC-V is not supported.
- Pass ``-mno-relax`` to disable ``R_RISCV_ALIGN``.
+* ``--lto-emit-asm`` is added to emit assembly output for debugging purposes.
+ (`D77231 <https://reviews.llvm.org/D77231>`_)
+* ``--lto-whole-program-visibility`` is added to specify that classes have hidden LTO visibility in LTO and ThinLTO links of source files compiled with ``-fwhole-program-vtables``. See `LTOVisibility <https://clang.llvm.org/docs/LTOVisibility.html>`_ for details.
+ (`D71913 <https://reviews.llvm.org/D71913>`_)
+* ``--print-archive-stats=`` is added to print the number of members and the number of fetched members for each archive.
+ The feature is similar to GNU gold's ``--print-symbol-counts=``.
+ (`D78983 <https://reviews.llvm.org/D78983>`_)
+* ``--shuffle-sections=`` is added to introduce randomization in the output to help reduce measurement bias and detect static initialization order fiasco.
+ (`D74791 <https://reviews.llvm.org/D74791>`_)
+ (`D74887 <https://reviews.llvm.org/D74887>`_)
+* ``--time-trace`` is added. It records a time trace file that can be viewed in
+ chrome://tracing. The file can be specified with ``--time-trace-file``.
+ Trace granularity can be specified with ``--time-trace-granularity``.
+ (`D71060 <https://reviews.llvm.org/D71060>`_)
+* ``--thinlto-single-module`` is added to compile a subset of modules in ThinLTO for debugging purposes.
+ (`D80406 <https://reviews.llvm.org/D80406>`_)
+* ``--unique`` is added to create separate output sections for orphan sections.
+ (`D75536 <https://reviews.llvm.org/D75536>`_)
+* ``--warn-backrefs`` has been improved to emulate GNU ld's archive semantics.
+ If a link passes with warnings from ``--warn-backrefs``, it almost assuredly
+ means that the link will fail with GNU ld, or the symbol will get different
+ resolutions in GNU ld and LLD. ``--warn-backrefs-exclude=`` is added to
+ exclude known issues.
+ (`D77522 <https://reviews.llvm.org/D77522>`_)
+ (`D77630 <https://reviews.llvm.org/D77630>`_)
+ (`D77512 <https://reviews.llvm.org/D77512>`_)
+* ``--no-relax`` is accepted but ignored. The Linux kernel's RISC-V port uses this option.
+ (`D81359 <https://reviews.llvm.org/D81359>`_)
+* ``--rosegment`` (default) is added to complement ``--no-rosegment``.
+ GNU gold from 2.35 onwards support both options.
+* ``--threads=N`` is added. The default uses all threads.
+ (`D76885 <https://reviews.llvm.org/D76885>`_)
+* ``--wrap`` has better compatibility with GNU ld.
+* ``-z dead-reloc-in-nonalloc=<section_glob>=<value>`` is added to resolve an absolute relocation
+ referencing a discarded symbol.
+ (`D83264 <https://reviews.llvm.org/D83264>`_)
+* Changed tombstone values to (``.debug_ranges``/``.debug_loc``) 1 and (other ``.debug_*``) 0.
+ A tombstone value is the computed value of a relocation referencing a discarded symbol (``--gc-sections``, ICF or ``/DISCARD/``).
+ (`D84825 <https://reviews.llvm.org/D84825>`_)
+ In the future many .debug_* may switch to 0xffffffff/0xffffffffffffffff as the tombstone value.
+* ``-z keep-text-section-prefix`` moves ``.text.unknown.*`` input sections to ``.text.unknown``.
+* ``-z rel`` and ``-z rela`` are added to select the REL/RELA format for dynamic relocations.
+ The default is target specific and typically matches the form used in relocatable objects.
+* ``-z start-stop-visibility={default,protected,internal,hidden}`` is added.
+ GNU ld/gold from 2.35 onwards support this option.
+ (`D55682 <https://reviews.llvm.org/D55682>`_)
+* When ``-r`` or ``--emit-relocs`` is specified, the GNU ld compatible
+ ``--discard-all`` and ``--discard-locals`` semantics are implemented.
+ (`D77807 <https://reviews.llvm.org/D77807>`_)
+* ``--emit-relocs --strip-debug`` can now be used together.
+ (`D74375 <https://reviews.llvm.org/D74375>`_)
+* ``--gdb-index`` supports DWARF v5.
+ (`D79061 <https://reviews.llvm.org/D79061>`_)
+ (`D85579 <https://reviews.llvm.org/D85579>`_)
+* ``-r`` allows SHT_X86_64_UNWIND to be merged into SHT_PROGBITS.
+ This allows clang/GCC produced object files to be mixed together.
+ (`D85785 <https://reviews.llvm.org/D85785>`_)
+* Better linker script support related to output section alignments and LMA regions.
+ (`D74286 <https://reviews.llvm.org/D75724>`_)
+ (`D74297 <https://reviews.llvm.org/D75724>`_)
+ (`D75724 <https://reviews.llvm.org/D75724>`_)
+ (`D81986 <https://reviews.llvm.org/D81986>`_)
+* In a input section description, the filename can be specified in double quotes.
+ ``archive:file`` syntax is added.
+ (`D72517 <https://reviews.llvm.org/D72517>`_)
+ (`D75100 <https://reviews.llvm.org/D75100>`_)
+* Linker script specified empty ``(.init|.preinit|.fini)_array`` are allowed with RELRO.
+ (`D76915 <https://reviews.llvm.org/D76915>`_)
+* ``INSERT AFTER`` and ``INSERT BEFORE`` work for orphan sections now.
+ (`D74375 <https://reviews.llvm.org/D74375>`_)
+* ``INPUT_SECTION_FLAGS`` is supported in linker scripts.
+ (`D72745 <https://reviews.llvm.org/D72745>`_)
+* ``DF_1_PIE`` is set for position-independent executables.
+ (`D80872 <https://reviews.llvm.org/D80872>`_)
+* For a symbol assignment ``alias = aliasee;``, ``alias`` inherits the ``aliasee``'s symbol type.
+ (`D86263 <https://reviews.llvm.org/D86263>`_)
+* ``SHT_GNU_verneed`` in shared objects are parsed, and versioned undefined symbols in shared objects are respected.
+ (`D80059 <https://reviews.llvm.org/D80059>`_)
+* SHF_LINK_ORDER and non-SHF_LINK_ORDER sections can be mixed along as the SHF_LINK_ORDER components are contiguous.
+ (`D77007 <https://reviews.llvm.org/D77007>`_)
+* An out-of-range relocation diagnostic mentions the referenced symbol now.
+ (`D73518 <https://reviews.llvm.org/D73518>`_)
+* AArch64: ``R_AARCH64_PLT32`` is supported.
+ (`D81184 <https://reviews.llvm.org/D81184>`_)
+* ARM: SBREL type relocations are supported.
+ (`D74375 <https://reviews.llvm.org/D74375>`_)
+* ARM: ``R_ARM_ALU_PC_G0``, ``R_ARM_LDR_PC_G0``, ``R_ARM_THUMB_PC8`` and ``R_ARM_THUMB__PC12`` are supported.
+ (`D75349 <https://reviews.llvm.org/D75349>`_)
+ (`D77200 <https://reviews.llvm.org/D77200>`_)
+* ARM: various improvements to .ARM.exidx: ``/DISCARD/`` support for a subset, out-of-range handling, support for non monotonic section order.
+ (`PR44824 <https://llvm.org/PR44824>`_)
+* AVR: many relocation types are supported.
+ (`D78741 <https://reviews.llvm.org/D78741>`_)
+* Hexagon: General Dynamic and some other relocation types are supported.
+* PPC: Canonical PLT and range extension thunks with addends are supported.
+ (`D73399 <https://reviews.llvm.org/D73399>`_)
+ (`D73424 <https://reviews.llvm.org/D73424>`_)
+ (`D75394 <https://reviews.llvm.org/D75394>`_)
+* PPC and PPC64: copy relocations.
+ (`D73255 <https://reviews.llvm.org/D73255>`_)
+* PPC64: ``_savegpr[01]_{14..31}`` and ``_restgpr[01]_{14..31}`` can be synthesized.
+ (`D79977 <https://reviews.llvm.org/D79977>`_)
+* PPC64: ``R_PPC64_GOT_PCREL34`` and ``R_PPC64_REL24_NOTOC`` are supported. r2 save stub is supported.
+ (`D81948 <https://reviews.llvm.org/D81948>`_)
+ (`D82950 <https://reviews.llvm.org/D82950>`_)
+ (`D82816 <https://reviews.llvm.org/D82816>`_)
+* RISC-V: ``R_RISCV_IRELATIVE`` is supported.
+ (`D74022 <https://reviews.llvm.org/D74022>`_)
+* RISC-V: ``R_RISCV_ALIGN`` is errored because GNU ld style linker relaxation is not supported.
(`D71820 <https://reviews.llvm.org/D71820>`_)
-* The ARM port will no longer insert interworking thunks for non STT_FUNC symbols.
- (`D73474 <https://reviews.llvm.org/D73474>`_)
-* The quality of PowerPC32 port has been greatly improved (canonical PLT, copy
- relocations, non-preemptible IFUNC, range extension thunks with addends).
- It can link FreeBSD 13.0 userland.
-* The PowerPC64 port supports non-preemptible IFUNC.
- (`D71509 <https://reviews.llvm.org/D71509>`_)
-* lld creates a RO PT_LOAD and a RX PT_LOAD without a linker script.
- lld creates a unified RX PT_LOAD with a linker script.
- A future release will eliminate this difference and use a RO PT_LOAD and a RX PT_LOAD by default.
- The linker script case will require ``--no-rosegment`` to restore the current behavior.
-* GNU style compressed debug sections ``.zdebug`` (obsoleted by ``SHF_COMPRESSED``)
- are supported for input files, but not for the output.
- A future release may drop ``.zdebug`` support.
+* SPARCv9: more relocation types are supported.
+ (`D77672 <https://reviews.llvm.org/D77672>`_)
Breaking changes
----------------
-* ``-Ttext=$base`` (base is usually 0) is no longer supported.
- If PT_PHDR is needed, use ``--image-base=$base`` instead.
- If PT_PHDR is not needed, use a linker script with `.text 0 : { *(.text*) }` as the first
- output section description.
- See https://bugs.llvm.org/show_bug.cgi?id=44715 for more information.
- (`D67325 <https://reviews.llvm.org/D67325>`_)
-* ``-Ttext-segment`` is no longer supported. Its meaning was different from GNU ld's and
- could cause subtle bugs.
- (`D70468 <https://reviews.llvm.org/D70468>`_)
-
+* One-dash form of some long option (``--thinlto-*``, ``--lto-*``, ``--shuffle-sections=``)
+ are no longer supported.
+ (`D79371 <https://reviews.llvm.org/D79371>`_)
+* ``--export-dynamic-symbol`` no longer implies ``-u``.
+ The new behavior matches GNU ld from binutils 2.35 onwards.
+ (`D80487 <https://reviews.llvm.org/D80487>`_)
+* ARM: the default max page size was increased from 4096 to 65536.
+ This increases compatibility with systems where a non standard page
+ size was configured. This also is inline with GNU ld defaults.
+ (`D77330 <https://reviews.llvm.org/D77330>`_)
+* ARM: for non-STT_FUNC symbols, Thumb interworking thunks are not added and BL/BLX are not substituted.
+ (`D73474 <https://reviews.llvm.org/D73474>`_)
+ (`D73542 <https://reviews.llvm.org/D73542>`_)
+* AArch64: ``--force-bti`` is renamed to ``-z force-bti`. ``--pac-plt`` is renamed to ``-z pac-plt``.
+ This change is compatibile with GNU ld.
+* A readonly ``PT_LOAD`` is created in the presence of a ``SECTIONS`` command.
+ The new behavior is consistent with the longstanding behavior in the absence of a SECTIONS command.
+* Orphan section names like ``.rodata.foo`` and ``.text.foo`` are not grouped into ``.rodata`` and ``.text`` in the presence of a ``SECTIONS`` command.
+ The new behavior matches GNU ld.
+ (`D75225 <https://reviews.llvm.org/D75225>`_)
+* ``--no-threads`` is removed. Use ``--threads=1`` instead. ``--threads`` (no-op) is removed.
+
+COFF Improvements
+-----------------
+
+* Fixed exporting symbols whose names contain a period (``.``), which was
+ a regression in lld 7.
MinGW Improvements
------------------
-* Allow using custom .edata sections from input object files (for use
- by Wine)
- (`dadc6f248868 <https://reviews.llvm.org/rGdadc6f248868>`_)
-
-* Don't implicitly create import libraries unless requested
- (`6540e55067e3 <https://reviews.llvm.org/rG6540e55067e3>`_)
-
-* Support merging multiple resource object files
- (`3d3a9b3b413d <https://reviews.llvm.org/rG3d3a9b3b413d>`_)
- and properly handle the default manifest object files that GCC can pass
- (`d581dd501381 <https://reviews.llvm.org/rGd581dd501381>`_)
-
-* Demangle itanium symbol names in warnings/error messages
- (`a66fc1c99f3e <https://reviews.llvm.org/rGa66fc1c99f3e>`_)
-
-* Print source locations for undefined references and duplicate symbols,
- if possible
- (`1d06d48bb346 <https://reviews.llvm.org/rG1d06d48bb346>`_)
- and
- (`b38f577c015c <https://reviews.llvm.org/rGb38f577c015c>`_)
-
-* Look for more filename patterns when resolving ``-l`` options
- (`0226c35262df <https://reviews.llvm.org/rG0226c35262df>`_)
-
-* Don't error out on duplicate absolute symbols with the same value
- (which can happen for the default-null symbol for weak symbols)
- (`1737cc750c46 <https://reviews.llvm.org/rG1737cc750c46>`_)
-
-
-WebAssembly Improvements
-------------------------
-
-* `__data_end` and `__heap_base` are no longer exported by default,
- as it's best to keep them internal when possible. They can be
- explicitly exported with `--export=__data_end` and
- `--export=__heap_base`, respectively.
-* wasm-ld now elides .bss sections when the memory is not imported
+* Implemented new options for disabling auto import and runtime pseudo
+ relocations (``--disable-auto-import`` and
+ ``--disable-runtime-pseudo-reloc``), the ``--no-seh`` flag and options
+ for selecting file and section alignment (``--file-alignment`` and
+ ``--section-alignment``).
a warning. The ``--fatal-warnings`` flag can be used to disable this behaviour
and error out if mismatched are found.
-Imports and Exports
-~~~~~~~~~~~~~~~~~~~
+Exports
+~~~~~~~
When building a shared library any symbols marked as ``visibility=default`` will
be exported.
used to export symbols in the executable which are marked as
``visibility=default``.
+Imports
+~~~~~~~
+
+By default no undefined symbols are allowed in the final binary. The flag
+``--allow-undefined`` results in a WebAssembly import being defined for each
+undefined symbol. It is then up to the runtime to provide such symbols.
+
+Alternatively symbols can be marked in the source code as with the
+``import_name`` and/or ``import_module`` clang attributes which signals that
+they are expected to be undefined at static link time.
+
Garbage Collection
~~~~~~~~~~~~~~~~~~
# built documents.
#
# The short version.
-version = '10'
+version = '11'
# The full version, including alpha/beta/rc tags.
-release = '10'
+release = '11'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
-html_sidebars = {'index': 'indexsidebar.html'}
+html_sidebars = {'index': ['indexsidebar.html']}
# Additional templates that should be rendered to pages, maps page names to
# template names.
The lld project contains a test suite which is being built up as new code is
added to lld. All new lld functionality should have a tests added to the test
-suite. The test suite is `lit <http://llvm.org/cmds/lit.html/>`_ driven. Each
+suite. The test suite is `lit <https://llvm.org/cmds/lit.html/>`_ driven. Each
test is a text file with comments telling lit how to run the test and check the
result To facilitate testing, the lld project builds a tool called lld-core.
This tool reads a YAML file (default from stdin), parses it into one or more
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.
+lld is developed as part of the `LLVM <https://llvm.org>`_ project.
Creating a Reader
-----------------
This page gives you the shortest path to checking out and building lld. If you
run into problems, please file bugs in the `LLVM Bugzilla`__
-__ http://llvm.org/bugs/
+__ https://bugs.llvm.org/
Building lld
------------
For more information on using CMake see the `LLVM CMake guide`_.
-.. _LLVM CMake guide: http://llvm.org/docs/CMake.html
+.. _LLVM CMake guide: https://llvm.org/docs/CMake.html
machine, you can expect that LLD runs more than twice as fast as the GNU
gold linker. Your mileage 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, AArch64, and ARM (>= v6) are production quality.
- MIPS seems decent too. x86 should be OK but is not well tested yet.
+- It supports various CPUs/ABIs including AArch64, AMDGPU, ARM, Hexagon, MIPS
+ 32/64 big/little-endian, PowerPC, PowerPC64, RISC-V, SPARC V9, x86-32 and
+ x86-64. Among these, AArch64, ARM (>= v6), PowerPC, PowerPC64, x86-32 and
+ x86-64 have production quality. MIPS seems decent too.
- 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
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>`_.
+<https://llvm.org/docs/GettingStarted.html>`_.
If you haven't checked out LLVM, the easiest way to build LLD is to
check out the entire LLVM projects/sub-projects from a git mirror and
missingkeyfunction
Partitions
ReleaseNotes
+ ELF/linker_script
.It Fl -dynamic-list Ns = Ns Ar file
Read a list of dynamic symbols from
.Ar file .
+(executable) Put matched non-local defined symbols to the dynamic symbol table.
+(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object. Implies
+.Cm -Bsymbolic
+but does not set DF_SYMBOLIC
.It Fl -eh-frame-hdr
Request creation of
.Li .eh_frame_hdr
Exclude static libraries from automatic export.
.It Fl -export-dynamic , Fl E
Put symbols in the dynamic symbol table.
-.It Fl -export-dynamic-symbol Ns = Ns Ar symbol
-Include
-.Ar symbol
-in the dynamic symbol table.
+.It Fl -export-dynamic-symbol Ns = Ns Ar glob
+(executable) Put matched non-local defined symbols to the dynamic symbol table.
+(shared object) References to matched non-local STV_DEFAULT symbols shouldn't be bound to definitions within the shared object even if they would otherwise be due to
+.Cm -Bsymbolic
+,
+.Cm -Bsymbolic-functions
+or
+.Cm --dynamic-list
.It Fl -fatal-warnings
Treat warnings as errors.
.It Fl -filter Ns = Ns Ar value , Fl F Ar value
Page align sections.
.It Fl -no-omagic
Do not set the text data sections to be writable, page align sections.
+.It Fl -no-relax
+Disable target-specific relaxations. This is currently a no-op.
.It Fl -no-rosegment
Do not put read-only non-executable sections in their own segment.
-.It Fl -no-threads
-Do not run the linker multi-threaded.
.It Fl -no-undefined-version
Report version scripts that refer undefined symbols.
.It Fl -no-undefined
List identical folded sections.
.It Fl -print-map
Print a link map to the standard output.
+.It Fl -print-archive-stats Ns = Ns Ar file
+Write archive usage statistics to the specified file.
+Print the numbers of members and fetched members for each archive.
.It Fl -push-state
Save the current state of
.Fl -as-needed ,
Set address of section.
.It Fl -shared , Fl -Bsharable
Build a shared object.
+.It Fl -shuffle-sections Ns = Ns Ar seed
+Shuffle input sections using the given seed. If 0, use a random seed.
.It Fl -soname Ns = Ns Ar value , Fl h Ar value
Set
.Dv DT_SONAME
Pruning policy for the ThinLTO cache.
.It Fl -thinlto-jobs Ns = Ns Ar value
Number of ThinLTO jobs.
-.It Fl -threads
-Run the linker multi-threaded.
-This option is enabled by default.
+.It Fl -threads Ns = Ns Ar N
+Number of threads.
+.Cm all
+(default) means all of concurrent threads supported.
+.Cm 1
+disables multi-threading.
+.It Fl -time-trace
+Record time trace.
+.It Fl -time-trace-file Ns = Ns Ar file
+Write time trace output to
+.Ar file .
+.It Fl -time-trace-granularity Ns = Ns Ar value
+Minimum time granularity (in microseconds) traced by time profiler.
.It Fl -trace
Print the names of the input files.
.It Fl -trace-symbol Ns = Ns Ar symbol , Fl y Ar symbol
All symbols that match
a given pattern are handled as if they were given as arguments of
.Fl -undefined .
+.It Fl -unique
+Creates a separate output section for every orphan input section.
.It Fl -unresolved-symbols Ns = Ns Ar value
Determine how to handle unresolved symbols.
.It Fl -use-android-relr-tags
Warn about reverse or cyclic dependencies to or between static archives.
This can be used to ensure linker invocation remains compatible with
traditional Unix-like linkers.
+.It Fl -warn-backrefs-exclude Ns = Ns Ar glob
+Glob describing an archive (or an object file within --start-lib)
+which should be ignored for
+.Fl -warn-backrefs
.It Fl -warn-common
Warn about duplicate common symbols.
.It Fl -warn-ifunc-textrel
Linker option extensions.
.Bl -tag -width indent -compact
.Pp
+.It Cm dead-reloc-in-nonalloc Ns = Ns Ar section_glob=value
+Resolve a relocation in a matched non-SHF_ALLOC section referencing a discarded symbol to
+.Ar value
+Accepts globs, in the event of a section matching more than one option, the last
+option takes precedence. An order of least specific to most specific match is
+recommended.
+.Pp
.It Cm execstack
Make the main stack executable.
Stack permissions are recorded in the
.It Cm pac-plt
AArch64 only, use pointer authentication in PLT.
.Pp
+.It Cm rel
+Use REL format for dynamic relocations.
+.Pp
+.It Cm rela
+Use RELA format for dynamic relocations.
+.Pp
.It Cm retpolineplt
Emit retpoline format PLT entries as a mitigation for CVE-2017-5715.
.Pp
llvm::Optional<std::pair<std::string, unsigned>>
getVariableLoc(StringRef name);
+ llvm::DWARFContext *getContext() { return dwarf.get(); }
+
private:
std::unique_ptr<llvm::DWARFContext> dwarf;
std::vector<const llvm::DWARFDebugLine::LineTable *> lineTables;
llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
}
+namespace macho {
+bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
+ llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
+}
+
namespace wasm {
bool link(llvm::ArrayRef<const char *> args, bool canExitEarly,
llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS);
llvm::SpecificBumpPtrAllocator<T> alloc;
};
+// Use a static local for these singletons so they are only registered if an
+// object of this instance is ever constructed. Otherwise we will create and
+// register ELF allocators for COFF and the reverse.
+template <typename T>
+inline llvm::SpecificBumpPtrAllocator<T> &getSpecificAllocSingleton() {
+ static SpecificAlloc<T> instance;
+ return instance.alloc;
+}
+
// Use this arena if your object has a destructor.
// Your destructor will be invoked from freeArena().
template <typename T, typename... U> T *make(U &&... args) {
- static SpecificAlloc<T> alloc;
- return new (alloc.alloc.Allocate()) T(std::forward<U>(args)...);
+ return new (getSpecificAllocSingleton<T>().Allocate())
+ T(std::forward<U>(args)...);
}
} // namespace lld
// Write the contents of the a buffer to a file
void saveBuffer(llvm::StringRef buffer, const llvm::Twine &path);
-// This class represents multiple glob patterns.
-class StringMatcher {
+// A single pattern to match against. A pattern can either be double-quoted
+// text that should be matched exactly after removing the quoting marks or a
+// glob pattern in the sense of GlobPattern.
+class SingleStringMatcher {
public:
- StringMatcher() = default;
- explicit StringMatcher(llvm::ArrayRef<llvm::StringRef> pat);
+ // Create a StringPattern from Pattern to be matched exactly irregardless
+ // of globbing characters if ExactMatch is true.
+ SingleStringMatcher(llvm::StringRef Pattern);
+ // Match s against this pattern, exactly if ExactMatch is true.
bool match(llvm::StringRef s) const;
private:
- std::vector<llvm::GlobPattern> patterns;
+ // Whether to do an exact match irregardless of the presence of wildcard
+ // character.
+ bool ExactMatch;
+
+ // GlobPattern object if not doing an exact match.
+ llvm::GlobPattern GlobPatternMatcher;
+
+ // StringRef to match exactly if doing an exact match.
+ llvm::StringRef ExactPattern;
+};
+
+// This class represents multiple patterns to match against. A pattern can
+// either be a double-quoted text that should be matched exactly after removing
+// the quoted marks or a glob pattern.
+class StringMatcher {
+private:
+ // Patterns to match against.
+ std::vector<SingleStringMatcher> patterns;
+
+public:
+ StringMatcher() = default;
+
+ // Matcher for a single pattern.
+ StringMatcher(llvm::StringRef Pattern)
+ : patterns({SingleStringMatcher(Pattern)}) {}
+
+ // Add a new pattern to the existing ones to match against.
+ void addPattern(SingleStringMatcher Matcher) { patterns.push_back(Matcher); }
+
+ bool empty() { return patterns.empty(); }
+
+ // Match s against the patterns.
+ bool match(llvm::StringRef s) const;
};
} // namespace lld
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h"
#include <assert.h>
+#include <atomic>
#include <chrono>
#include <map>
#include <memory>
void stop();
+ std::chrono::time_point<std::chrono::high_resolution_clock> startTime;
+
Timer *t = nullptr;
};
static Timer &root();
- void start();
- void stop();
+ void addToTotal(std::chrono::nanoseconds time) { total += time.count(); }
void print();
double millis() const;
explicit Timer(llvm::StringRef name);
void print(int depth, double totalDuration, bool recurse = true) const;
- std::chrono::time_point<std::chrono::high_resolution_clock> startTime;
- std::chrono::nanoseconds total;
+ std::atomic<std::chrono::nanoseconds::rep> total;
std::vector<Timer *> children;
std::string name;
- Timer *parent;
};
} // namespace lld
-#define LLD_VERSION @LLD_VERSION@
#define LLD_VERSION_STRING "@LLD_VERSION@"
-#define LLD_VERSION_MAJOR @LLD_VERSION_MAJOR@
-#define LLD_VERSION_MINOR @LLD_VERSION_MINOR@
-#define LLD_REVISION_STRING "@LLD_REVISION@"
-#define LLD_REPOSITORY_STRING "@LLD_REPOSITORY@"
/// Returns the path of the archive file name if this file is instantiated
/// from an archive file. Otherwise returns the empty string.
StringRef archivePath() const { return _archivePath; }
- void setArchivePath(StringRef path) { _archivePath = path; }
+ void setArchivePath(StringRef path) { _archivePath = std::string(path); }
/// Returns the path name of this file. It doesn't include archive file name.
StringRef memberPath() const { return _path; }
/// Some relocations require a symbol and a value (e.g. foo + 4).
virtual Addend addend() const = 0;
- /// During linking, some optimzations may change addend value.
+ /// During linking, some optimizations may change addend value.
virtual void setAddend(Addend) = 0;
/// Returns target specific attributes of the reference.
enum class ExportMode {
globals, // Default, all global symbols exported.
- whiteList, // -exported_symbol[s_list], only listed symbols exported.
- blackList // -unexported_symbol[s_list], no listed symbol exported.
+ exported, // -exported_symbol[s_list], only listed symbols exported.
+ unexported // -unexported_symbol[s_list], no listed symbol exported.
};
enum class DebugInfoMode {
private:
Writer &writer() const override;
mach_o::MachODylibFile* loadIndirectDylib(StringRef path);
- void checkExportWhiteList(const DefinedAtom *atom) const;
- void checkExportBlackList(const DefinedAtom *atom) const;
struct ArchInfo {
StringRef archName;
MachOLinkingContext::Arch arch;
int add(std::string msg) {
std::lock_guard<std::recursive_mutex> lock(_mutex);
- // Value zero is always the successs value.
+ // Value zero is always the success value.
if (_messages.empty())
_messages.push_back("Success");
_messages.push_back(msg);
static dynamic_error_category categorySingleton;
std::error_code make_dynamic_error_code(StringRef msg) {
- return std::error_code(categorySingleton.add(msg), categorySingleton);
+ return std::error_code(categorySingleton.add(std::string(msg)),
+ categorySingleton);
}
char GenericError::ID = 0;
for (const OwningAtomPtr<Atom> &atom : _atoms) {
if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(atom.get())) {
for (const Reference *ref : *defAtom) {
- // A reference of type kindAssociate should't be updated.
+ // A reference of type kindAssociate shouldn't be updated.
// Instead, an atom having such reference will be removed
// if the target atom is coalesced away, so that they will
// go away as a group.
static std::string canonicalizePath(StringRef path) {
char sep = llvm::sys::path::get_separator().front();
if (sep != '/') {
- std::string fixedPath = path;
+ std::string fixedPath = std::string(path);
std::replace(fixedPath.begin(), fixedPath.end(), sep, '/');
return fixedPath;
} else {
- return path;
+ return std::string(path);
}
}
// Handle -exported_symbols_list <file>
for (auto expFile : parsedArgs.filtered(OPT_exported_symbols_list)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) {
error("-exported_symbols_list cannot be combined with "
"-unexported_symbol[s_list]");
return false;
}
- ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList);
+ ctx.setExportMode(MachOLinkingContext::ExportMode::exported);
if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) {
error(ec.message() + ", processing '-exported_symbols_list " +
expFile->getValue());
// Handle -exported_symbol <symbol>
for (auto symbol : parsedArgs.filtered(OPT_exported_symbol)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::unexported) {
error("-exported_symbol cannot be combined with "
"-unexported_symbol[s_list]");
return false;
}
- ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList);
+ ctx.setExportMode(MachOLinkingContext::ExportMode::exported);
ctx.addExportSymbol(symbol->getValue());
}
// Handle -unexported_symbols_list <file>
for (auto expFile : parsedArgs.filtered(OPT_unexported_symbols_list)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) {
error("-unexported_symbols_list cannot be combined with "
"-exported_symbol[s_list]");
return false;
}
- ctx.setExportMode(MachOLinkingContext::ExportMode::blackList);
+ ctx.setExportMode(MachOLinkingContext::ExportMode::unexported);
if (std::error_code ec = parseExportsList(expFile->getValue(), ctx)) {
error(ec.message() + ", processing '-unexported_symbols_list " +
expFile->getValue());
// Handle -unexported_symbol <symbol>
for (auto symbol : parsedArgs.filtered(OPT_unexported_symbol)) {
- if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) {
+ if (ctx.exportMode() == MachOLinkingContext::ExportMode::exported) {
error("-unexported_symbol cannot be combined with "
"-exported_symbol[s_list]");
return false;
}
- ctx.setExportMode(MachOLinkingContext::ExportMode::blackList);
+ ctx.setExportMode(MachOLinkingContext::ExportMode::unexported);
ctx.addExportSymbol(symbol->getValue());
}
/// Used by GOTPass to update GOT References.
void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override {
- // If GOT slot was instanciated, transform:
+ // If GOT slot was instantiated, transform:
// gotPage21/gotOffset12 -> page21/offset12scale8
// If GOT slot optimized away, transform:
// gotPage21/gotOffset12 -> page21/addOffset12
public:
MachOTentativeDefAtom(const File &f, const StringRef name, Scope scope,
uint64_t size, DefinedAtom::Alignment align)
- : SimpleDefinedAtom(f), _name(name), _scope(scope), _size(size),
- _align(align) {}
+ : SimpleDefinedAtom(f), _name(std::string(name)), _scope(scope),
+ _size(size), _align(align) {}
~MachOTentativeDefAtom() override = default;
Demangle
Object
Support
+ TextAPI
LINK_LIBS
lldCommon
pm.add(std::make_unique<CompactUnwindPass>(ctx));
}
-} // end namesapce mach_o
-} // end namesapce lld
+} // end namespace mach_o
+} // end namespace lld
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Format.h"
+#include "llvm/TextAPI/MachO/InterfaceFile.h"
+#include "llvm/TextAPI/MachO/TextAPIReader.h"
#include <unordered_map>
namespace lld {
void loadReExportedDylibs(FindDylib find) {
for (ReExportedDylib &entry : _reExportedDylibs) {
- entry.file = find(entry.path);
+ if (!entry.file)
+ entry.file = find(entry.path);
}
}
return std::error_code();
}
-private:
+protected:
OwningAtomPtr<SharedLibraryAtom> exports(StringRef name,
StringRef installName) const {
// First, check if requested symbol is directly implemented by this dylib.
struct ReExportedDylib {
ReExportedDylib(StringRef p) : path(p), file(nullptr) { }
+ ReExportedDylib(StringRef p, MachODylibFile *file) : path(p), file(file) { }
StringRef path;
MachODylibFile *file;
};
mutable std::unordered_map<StringRef, AtomAndFlags> _nameToAtom;
};
+class TAPIFile : public MachODylibFile {
+public:
+
+ TAPIFile(std::unique_ptr<MemoryBuffer> mb, MachOLinkingContext *ctx)
+ : MachODylibFile(std::move(mb), ctx) {}
+
+ std::error_code doParse() override {
+
+ llvm::Expected<std::unique_ptr<llvm::MachO::InterfaceFile>> result =
+ llvm::MachO::TextAPIReader::get(*_mb);
+ if (!result)
+ return std::make_error_code(std::errc::invalid_argument);
+
+ std::unique_ptr<llvm::MachO::InterfaceFile> interface{std::move(*result)};
+ return loadFromInterface(*interface);
+ }
+
+private:
+ std::error_code loadFromInterface(llvm::MachO::InterfaceFile &interface) {
+ llvm::MachO::Architecture arch;
+ switch(_ctx->arch()) {
+ case MachOLinkingContext::arch_x86:
+ arch = llvm::MachO::AK_i386;
+ break;
+ case MachOLinkingContext::arch_x86_64:
+ arch = llvm::MachO::AK_x86_64;
+ break;
+ case MachOLinkingContext::arch_arm64:
+ arch = llvm::MachO::AK_arm64;
+ break;
+ default:
+ return std::make_error_code(std::errc::invalid_argument);
+ }
+
+ setInstallName(interface.getInstallName().copy(allocator()));
+ // TODO(compnerd) filter out symbols based on the target platform
+ for (const auto symbol : interface.symbols())
+ if (symbol->getArchitectures().has(arch))
+ addExportedSymbol(symbol->getName(), symbol->isWeakDefined(), true);
+
+ for (const llvm::MachO::InterfaceFileRef &reexport :
+ interface.reexportedLibraries())
+ addReExportedDylib(reexport.getInstallName().copy(allocator()));
+
+ for (const auto& document : interface.documents()) {
+ for (auto& reexport : _reExportedDylibs) {
+ if (reexport.path != document->getInstallName())
+ continue;
+ assert(!reexport.file);
+ _ownedFiles.push_back(std::make_unique<TAPIFile>(
+ MemoryBuffer::getMemBuffer("", _mb->getBufferIdentifier()), _ctx));
+ reexport.file = _ownedFiles.back().get();
+ std::error_code err = _ownedFiles.back()->loadFromInterface(*document);
+ if (err)
+ return err;
+ }
+ }
+
+ return std::error_code();
+ }
+
+ std::vector<std::unique_ptr<TAPIFile>> _ownedFiles;
+};
+
} // end namespace mach_o
} // end namespace lld
pm.add(std::make_unique<GOTPass>(ctx));
}
-} // end namesapce mach_o
-} // end namesapce lld
+} // end namespace mach_o
+} // end namespace lld
});
std::vector<LayoutPass::SortKey> vec = decorate(atomRange);
- sort(llvm::parallel::par, vec.begin(), vec.end(),
- [&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool {
- return compareAtoms(l, r, _customSorter);
- });
+ llvm::parallelSort(
+ vec,
+ [&](const LayoutPass::SortKey &l, const LayoutPass::SortKey &r) -> bool {
+ return compareAtoms(l, r, _customSorter);
+ });
LLVM_DEBUG(checkTransitivity(vec, _customSorter));
undecorate(atomRange, vec);
return llvm::None;
}
+ // Search for stub library
+ fullPath.assign(path);
+ llvm::sys::path::append(fullPath, Twine("lib") + libName + ".tbd");
+ if (fileExists(fullPath))
+ return fullPath.str().copy(_allocator);
+
// Search for dynamic library
fullPath.assign(path);
llvm::sys::path::append(fullPath, Twine("lib") + libName + ".dylib");
}
// If -exported_symbols_list used, all exported symbols must be defined.
- if (_exportMode == ExportMode::whiteList) {
+ if (_exportMode == ExportMode::exported) {
for (const auto &symbol : _exportedSymbols)
addInitialUndefinedSymbol(symbol.getKey());
}
if (needsStubsPass())
addDeadStripRoot(binderSymbolName());
// If using -exported_symbols_list, make all exported symbols live.
- if (_exportMode == ExportMode::whiteList) {
+ if (_exportMode == ExportMode::exported) {
setGlobalsAreDeadStripRoots(false);
for (const auto &symbol : _exportedSymbols)
addDeadStripRoot(symbol.getKey());
case ExportMode::globals:
llvm_unreachable("exportSymbolNamed() should not be called in this mode");
break;
- case ExportMode::whiteList:
+ case ExportMode::exported:
return _exportedSymbols.count(sym);
- case ExportMode::blackList:
+ case ExportMode::unexported:
return !_exportedSymbols.count(sym);
}
llvm_unreachable("_exportMode unknown enum value");
std::string MachOLinkingContext::demangle(StringRef symbolName) const {
// Only try to demangle symbols if -demangle on command line
if (!demangleSymbols())
- return symbolName;
+ return std::string(symbolName);
// Only try to demangle symbols that look like C++ symbols
if (!symbolName.startswith("__Z"))
- return symbolName;
+ return std::string(symbolName);
SmallString<256> symBuff;
StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff);
return result;
}
- return symbolName;
+ return std::string(symbolName);
}
static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
MachOLinkingContext &_ctx;
};
+class MachOTAPIReader : public Reader {
+public:
+ MachOTAPIReader(MachOLinkingContext &ctx) : _ctx(ctx) {}
+
+ bool canParse(file_magic magic, MemoryBufferRef mb) const override {
+ return magic == file_magic::tapi_file;
+ }
+
+ ErrorOr<std::unique_ptr<File>>
+ loadFile(std::unique_ptr<MemoryBuffer> mb,
+ const Registry ®istry) const override {
+ std::unique_ptr<File> ret =
+ std::make_unique<TAPIFile>(std::move(mb), &_ctx);
+ return std::move(ret);
+ }
+
+private:
+ MachOLinkingContext &_ctx;
+};
+
} // namespace normalized
} // namespace mach_o
MachOLinkingContext::Arch arch = ctx.arch();
add(std::unique_ptr<Reader>(new mach_o::normalized::MachOObjectReader(ctx)));
add(std::unique_ptr<Reader>(new mach_o::normalized::MachODylibReader(ctx)));
+ add(std::unique_ptr<Reader>(new mach_o::normalized::MachOTAPIReader(ctx)));
addKindTable(Reference::KindNamespace::mach_o, ctx.archHandler().kindArch(),
ctx.archHandler().kindStrings());
add(std::unique_ptr<YamlIOTaggedDocumentHandler>(
inGlobalsRegion = false;
return llvm::Error::success();
case Atom::scopeLinkageUnit:
- if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) &&
+ if ((_ctx.exportMode() == MachOLinkingContext::ExportMode::exported) &&
_ctx.exportSymbolNamed(atom->name())) {
return llvm::make_error<GenericError>(
Twine("cannot export hidden symbol ") + atom->name());
// Add all stabs.
for (auto &stab : _stabs) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.type = static_cast<NListType>(stab.type);
sym.scope = 0;
sym.sect = stab.other;
AtomAndIndex ai = { atom, sect->finalSectionIndex, symbolScope };
globals.push_back(ai);
} else {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = atom->name();
sym.type = N_SECT;
sym.scope = symbolScope;
char tmpName[16];
sprintf(tmpName, "L%04u", tempNum++);
StringRef tempRef(tmpName);
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = tempRef.copy(file.ownedAllocations);
sym.type = N_SECT;
sym.scope = 0;
std::sort(globals.begin(), globals.end(), AtomSorter());
const uint32_t globalStartIndex = file.localSymbols.size();
for (AtomAndIndex &ai : globals) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
sym.name = ai.atom->name();
sym.type = N_SECT;
sym.scope = ai.scope;
std::sort(undefs.begin(), undefs.end(), AtomSorter());
const uint32_t start = file.globalSymbols.size() + file.localSymbols.size();
for (AtomAndIndex &ai : undefs) {
- Symbol sym;
+ lld::mach_o::normalized::Symbol sym;
uint16_t desc = 0;
if (!rMode) {
uint8_t ordinal = 0;
llvm_unreachable("unknown scope value!");
}
-void appendSymbolsInSection(const std::vector<Symbol> &inSymbols,
- uint32_t sectionIndex,
- SmallVector<const Symbol *, 64> &outSyms) {
- for (const Symbol &sym : inSymbols) {
+void appendSymbolsInSection(
+ const std::vector<lld::mach_o::normalized::Symbol> &inSymbols,
+ uint32_t sectionIndex,
+ SmallVector<const lld::mach_o::normalized::Symbol *, 64> &outSyms) {
+ for (const lld::mach_o::normalized::Symbol &sym : inSymbols) {
// Only look at definition symbols.
if ((sym.type & N_TYPE) != N_SECT)
continue;
}
// Find all symbols in this section.
- SmallVector<const Symbol *, 64> symbols;
+ SmallVector<const lld::mach_o::normalized::Symbol *, 64> symbols;
appendSymbolsInSection(normalizedFile.globalSymbols, sectIndex, symbols);
appendSymbolsInSection(normalizedFile.localSymbols, sectIndex, symbols);
// Sort symbols.
std::sort(symbols.begin(), symbols.end(),
- [](const Symbol *lhs, const Symbol *rhs) -> bool {
+ [](const lld::mach_o::normalized::Symbol *lhs,
+ const lld::mach_o::normalized::Symbol *rhs) -> bool {
if (lhs == rhs)
return false;
// First by address.
uint64_t rhsAddr = rhs->value;
if (lhsAddr != rhsAddr)
return lhsAddr < rhsAddr;
- // If same address, one is an alias so sort by scope.
+ // If same address, one is an alias so sort by scope.
Atom::Scope lScope = atomScope(lhs->scope);
Atom::Scope rScope = atomScope(rhs->scope);
if (lScope != rScope)
scatterable, copyRefs);
}
- const Symbol *lastSym = nullptr;
- for (const Symbol *sym : symbols) {
+ const lld::mach_o::normalized::Symbol *lastSym = nullptr;
+ for (const lld::mach_o::normalized::Symbol *sym : symbols) {
if (lastSym != nullptr) {
// Ignore any assembler added "ltmpNNN" symbol at start of section
// if there is another symbol at the start.
auto atomBySymbol = [&] (uint32_t symbolIndex, const lld::Atom **result)
-> llvm::Error {
// Find symbol from index.
- const Symbol *sym = nullptr;
+ const lld::mach_o::normalized::Symbol *sym = nullptr;
uint32_t numStabs = normalizedFile.stabsSymbols.size();
uint32_t numLocal = normalizedFile.localSymbols.size();
uint32_t numGlobal = normalizedFile.globalSymbols.size();
llvm::Expected<std::unique_ptr<lld::File>>
objectToAtoms(const NormalizedFile &normalizedFile, StringRef path,
bool copyRefs) {
- std::unique_ptr<MachOFile> file(new MachOFile(path));
+ auto file = std::make_unique<MachOFile>(path);
if (auto ec = normalizedObjectToAtoms(file.get(), normalizedFile, copyRefs))
return std::move(ec);
return std::unique_ptr<File>(std::move(file));
dylibToAtoms(const NormalizedFile &normalizedFile, StringRef path,
bool copyRefs) {
// Instantiate SharedLibraryFile object.
- std::unique_ptr<MachODylibFile> file(new MachODylibFile(path));
+ auto file = std::make_unique<MachODylibFile>(path);
if (auto ec = normalizedDylibToAtoms(file.get(), normalizedFile, copyRefs))
return std::move(ec);
return std::unique_ptr<File>(std::move(file));
pm.add(std::make_unique<TLVPass>(ctx));
}
-} // end namesapce mach_o
-} // end namesapce lld
+} // end namespace mach_o
+} // end namespace lld
llvm::raw_string_ostream buffer(storage);
buffer << llvm::format("L%03d", _unnamedCounter++);
StringRef newName = copyString(buffer.str());
- _refNames[target] = newName;
+ _refNames[target] = std::string(newName);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "unnamed atom: creating ref-name: '"
<< newName << "' ("
llvm::raw_string_ostream buffer(Storage);
buffer << atom.name() << llvm::format(".%03d", ++_collisionCount);
StringRef newName = copyString(buffer.str());
- _refNames[&atom] = newName;
+ _refNames[&atom] = std::string(newName);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "name collision: creating ref-name: '"
<< newName << "' ("
llvm::raw_string_ostream buffer2(Storage2);
buffer2 << prevAtom->name() << llvm::format(".%03d", ++_collisionCount);
StringRef newName2 = copyString(buffer2.str());
- _refNames[prevAtom] = newName2;
+ _refNames[prevAtom] = std::string(newName2);
DEBUG_WITH_TYPE("WriterYAML",
llvm::dbgs() << "name collision: creating ref-name: '"
<< newName2 << "' ("
add_lld_tool(lld
lld.cpp
+
+ SUPPORT_PLUGINS
)
+export_executable_symbols_for_plugins(lld)
target_link_libraries(lld
PRIVATE
lldCOFF
lldDriver
lldELF
+ lldMachO2
lldMinGW
lldWasm
)
#include "llvm/Support/Host.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/PluginLoader.h"
#include <cstdlib>
using namespace lld;
enum Flavor {
Invalid,
- Gnu, // -flavor gnu
- WinLink, // -flavor link
- Darwin, // -flavor darwin
- Wasm, // -flavor wasm
+ Gnu, // -flavor gnu
+ WinLink, // -flavor link
+ Darwin, // -flavor darwin
+ DarwinNew, // -flavor darwinnew
+ Wasm, // -flavor wasm
};
LLVM_ATTRIBUTE_NORETURN static void die(const Twine &s) {
.CasesLower("wasm", "ld-wasm", Wasm)
.CaseLower("link", WinLink)
.CasesLower("ld64", "ld64.lld", "darwin", Darwin)
+ .CaseLower("darwinnew", DarwinNew)
.Default(Invalid);
}
-#ifndef __OpenBSD__
static cl::TokenizerCallback getDefaultQuotingStyle() {
if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32)
return cl::TokenizeWindowsCommandLine;
}
return false;
}
-#endif
static Flavor parseProgname(StringRef progname) {
-#if __APPLE__
- // Use Darwin driver for "ld" on Darwin.
- if (progname == "ld")
- return Darwin;
-#endif
-
-#if LLVM_ON_UNIX
- // Use GNU driver for "ld" on other Unix-like system.
+ // Use GNU driver for "ld" by default.
if (progname == "ld")
return Gnu;
-#endif
// Progname may be something like "lld-gnu". Parse it.
SmallVector<StringRef, 3> v;
std::vector<const char *> args(argv, argv + argc);
switch (parseFlavor(args)) {
case Gnu:
-#ifndef __OpenBSD__
if (isPETarget(args))
return !mingw::link(args, canExitEarly(), llvm::outs(), llvm::errs());
-#endif
return !elf::link(args, canExitEarly(), llvm::outs(), llvm::errs());
-#ifndef __OpenBSD__
case WinLink:
return !coff::link(args, canExitEarly(), llvm::outs(), llvm::errs());
case Darwin:
return !mach_o::link(args, canExitEarly(), llvm::outs(), llvm::errs());
+ case DarwinNew:
+ return !macho::link(args, canExitEarly(), llvm::outs(), llvm::errs());
case Wasm:
return !wasm::link(args, canExitEarly(), llvm::outs(), llvm::errs());
-#endif
default:
die("lld is a generic driver.\n"
"Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld"
protected:
int inputFileCount() { return _ctx.getNodes().size(); }
- std::string inputFile(int index) {
+ StringRef inputFile(int index) {
Node &node = *_ctx.getNodes()[index];
if (node.kind() == Node::Kind::File)
return cast<FileNode>(&node)->getFile()->path();
fromBinary(fileBytes, sizeof(fileBytes), "x86_64");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
EXPECT_TRUE(f->undefinedSymbols.empty());
fromBinary(fileBytes, sizeof(fileBytes), "i386");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
EXPECT_TRUE(f->undefinedSymbols.empty());
fromBinary(fileBytes, sizeof(fileBytes), "ppc");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
EXPECT_TRUE(f->undefinedSymbols.empty());
fromBinary(fileBytes, sizeof(fileBytes), "armv7");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
EXPECT_TRUE(f->undefinedSymbols.empty());
fromBinary(fileBytes, sizeof(fileBytes), "x86_64");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
EXPECT_TRUE(f->undefinedSymbols.empty());
fromBinary(fileBytes, sizeof(fileBytes), "armv7");
EXPECT_EQ(f2->arch, lld::MachOLinkingContext::arch_armv7);
EXPECT_EQ((int)(f2->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f2->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f2->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f2->localSymbols.empty());
EXPECT_TRUE(f2->globalSymbols.empty());
EXPECT_TRUE(f2->undefinedSymbols.empty());
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_EQ(f->sections.size(), 2UL);
const Section& text = f->sections[0];
EXPECT_TRUE(text.segmentName.equals("__TEXT"));
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_EQ(f->sections.size(), 2UL);
const Section& text = f->sections[0];
EXPECT_TRUE(text.segmentName.equals("__TEXT"));
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_EQ(f->sections.size(), 2UL);
const Section& text = f->sections[0];
EXPECT_TRUE(text.segmentName.equals("__TEXT"));
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
EXPECT_EQ((int)(f->fileType), MH_OBJECT);
- EXPECT_EQ((int)(f->flags), MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_EQ(f->sections.size(), 2UL);
const Section& text = f->sections[0];
EXPECT_TRUE(text.segmentName.equals("__TEXT"));
0xef, 0xff, 0xff, 0xff, // The maximum valid length value for DWARF32
0x00, 0x00 // Wrong version
};
- static const uint8_t debugAbbrevDummyContent[] = {0x00};
+ static const uint8_t dummyContent[] = {0x00};
NormalizedFile fReservedLength, fValidBigLength;
fReservedLength.arch = lld::MachOLinkingContext::arch_x86;
section.content = llvm::makeArrayRef(debugInfoWithValidBigLengthContent);
fValidBigLength.sections.push_back(section);
section.sectionName = "__debug_abbrev";
- section.content = llvm::makeArrayRef(debugAbbrevDummyContent);
+ section.content = llvm::makeArrayRef(dummyContent);
+ fReservedLength.sections.push_back(section);
+ fValidBigLength.sections.push_back(section);
+ section.sectionName = "__debug_str";
fReservedLength.sections.push_back(section);
fValidBigLength.sections.push_back(section);
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_ppc);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)(int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->sections.empty());
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)(int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->sections.empty());
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->sections.empty());
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv6);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->sections.empty());
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->sections.empty());
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7s);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f->sections.empty());
EXPECT_TRUE(f->localSymbols.empty());
EXPECT_TRUE(f->globalSymbols.empty());
NormalizedFile f;
f.arch = lld::MachOLinkingContext::arch_x86_64;
f.fileType = llvm::MachO::MH_OBJECT;
- f.flags = llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
+ f.flags = (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS;
f.os = lld::MachOLinkingContext::OS::macOSX;
toYAML(f, intermediate);
}
std::unique_ptr<NormalizedFile> f2 = fromYAML(intermediate);
EXPECT_EQ(f2->arch, lld::MachOLinkingContext::arch_x86_64);
EXPECT_EQ((int)(f2->fileType), llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f2->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f2->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_TRUE(f2->sections.empty());
EXPECT_TRUE(f2->localSymbols.empty());
EXPECT_TRUE(f2->globalSymbols.empty());
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86_64);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_EQ(f->sections.size(), 2UL);
const Section& sect1 = f->sections[0];
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_x86);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_EQ(f->sections.size(), 2UL);
const Section& sect1 = f->sections[0];
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv6);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_EQ(f->sections.size(), 2UL);
const Section& sect1 = f->sections[0];
"...\n");
EXPECT_EQ(f->arch, lld::MachOLinkingContext::arch_armv7);
EXPECT_EQ(f->fileType, llvm::MachO::MH_OBJECT);
- EXPECT_EQ((int)(f->flags), llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
+ EXPECT_EQ((int)(f->flags), (int)llvm::MachO::MH_SUBSECTIONS_VIA_SYMBOLS);
EXPECT_EQ(f->sections.size(), 2UL);
const Section& sect1 = f->sections[0];
bool compressRelocations;
bool demangle;
bool disableVerify;
+ bool experimentalPic;
bool emitRelocs;
bool exportAll;
bool exportDynamic;
bool importMemory;
bool sharedMemory;
bool importTable;
+ bool is64;
bool mergeDataSegments;
bool pie;
bool printGcSections;
bool stripDebug;
bool stackFirst;
bool trace;
- uint32_t globalBase;
- uint32_t initialMemory;
- uint32_t maxMemory;
- uint32_t zStackSize;
+ uint64_t globalBase;
+ uint64_t initialMemory;
+ uint64_t maxMemory;
+ uint64_t zStackSize;
unsigned ltoPartitions;
unsigned ltoo;
unsigned optimize;
- unsigned thinLTOJobs;
+ llvm::StringRef thinLTOJobs;
llvm::StringRef entry;
llvm::StringRef outputFile;
#include "Writer.h"
#include "lld/Common/Args.h"
#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Filesystem.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Reproduce.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "lld/Common/Version.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Object/Wasm.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/Parallel.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TarWriter.h"
}
}
+static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) {
+ if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {
+ StringRef s = arg->getValue();
+ if (s != "windows" && s != "posix")
+ error("invalid response file quoting: " + s);
+ if (s == "windows")
+ return cl::TokenizeWindowsCommandLine;
+ return cl::TokenizeGNUCommandLine;
+ }
+ if (Triple(sys::getProcessTriple()).isOSWindows())
+ return cl::TokenizeWindowsCommandLine;
+ return cl::TokenizeGNUCommandLine;
+}
+
// Find a file by concatenating given paths.
static Optional<std::string> findFile(StringRef path1, const Twine &path2) {
SmallString<128> s;
path::append(s, path1, path2);
if (fs::exists(s))
- return s.str().str();
+ return std::string(s);
return None;
}
unsigned missingIndex;
unsigned missingCount;
- // Expand response files (arguments in the form of @<filename>)
- cl::ExpandResponseFiles(saver, cl::TokenizeGNUCommandLine, vec);
-
+ // We need to get the quoting style for response files before parsing all
+ // options so we parse here before and ignore all the options but
+ // --rsp-quoting.
opt::InputArgList args = this->ParseArgs(vec, missingIndex, missingCount);
+ // Expand response files (arguments in the form of @<filename>)
+ // and then parse the argument again.
+ cl::ExpandResponseFiles(saver, getQuotingStyle(args), vec);
+ args = this->ParseArgs(vec, missingIndex, missingCount);
+
handleColorDiagnostics(args);
for (auto *arg : args.filtered(OPT_UNKNOWN))
error("unknown argument: " + arg->getAsString(args));
break;
}
}
+ if (files.empty() && errorCount() == 0)
+ error("no input files");
}
static StringRef getEntry(opt::InputArgList &args) {
config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true);
config->disableVerify = args.hasArg(OPT_disable_verify);
config->emitRelocs = args.hasArg(OPT_emit_relocs);
+ config->experimentalPic = args.hasArg(OPT_experimental_pic);
config->entry = getEntry(args);
config->exportAll = args.hasArg(OPT_export_all);
config->exportTable = args.hasArg(OPT_export_table);
config->thinLTOCachePolicy = CHECK(
parseCachePruningPolicy(args.getLastArgValue(OPT_thinlto_cache_policy)),
"--thinlto-cache-policy: invalid cache policy");
- config->thinLTOJobs = args::getInteger(args, OPT_thinlto_jobs, -1u);
errorHandler().verbose = args.hasArg(OPT_verbose);
LLVM_DEBUG(errorHandler().verbose = true);
- threadsEnabled = args.hasFlag(OPT_threads, OPT_no_threads, true);
config->initialMemory = args::getInteger(args, OPT_initial_memory, 0);
config->globalBase = args::getInteger(args, OPT_global_base, 1024);
config->exportDynamic =
args.hasFlag(OPT_export_dynamic, OPT_no_export_dynamic, config->shared);
+ // Parse wasm32/64.
+ config->is64 = false;
+ if (auto *arg = args.getLastArg(OPT_m)) {
+ StringRef s = arg->getValue();
+ if (s == "wasm32")
+ config->is64 = false;
+ else if (s == "wasm64")
+ config->is64 = true;
+ else
+ error("invalid target architecture: " + s);
+ }
+
+ // --threads= takes a positive integer and provides the default value for
+ // --thinlto-jobs=.
+ if (auto *arg = args.getLastArg(OPT_threads)) {
+ StringRef v(arg->getValue());
+ unsigned threads = 0;
+ if (!llvm::to_integer(v, threads, 0) || threads == 0)
+ error(arg->getSpelling() + ": expected a positive integer, but got '" +
+ arg->getValue() + "'");
+ parallel::strategy = hardware_concurrency(threads);
+ config->thinLTOJobs = v;
+ }
+ if (auto *arg = args.getLastArg(OPT_thinlto_jobs))
+ config->thinLTOJobs = arg->getValue();
+
if (auto *arg = args.getLastArg(OPT_features)) {
config->features =
llvm::Optional<std::vector<std::string>>(std::vector<std::string>());
for (StringRef s : arg->getValues())
- config->features->push_back(s);
+ config->features->push_back(std::string(s));
}
}
error("invalid optimization level for LTO: " + Twine(config->ltoo));
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 (!get_threadpool_strategy(config->thinLTOJobs))
+ error("--thinlto-jobs: invalid job count: " + config->thinLTOJobs);
if (config->pie && config->shared)
error("-shared and -pie may not be used together");
error("-r -and --undefined may not be used together");
if (config->pie)
error("-r and -pie may not be used together");
+ if (config->sharedMemory)
+ error("-r and --shared-memory may not be used together");
+ }
+
+ // To begin to prepare for Module Linking-style shared libraries, start
+ // warning about uses of `-shared` and related flags outside of Experimental
+ // mode, to give anyone using them a heads-up that they will be changing.
+ //
+ // Also, warn about flags which request explicit exports.
+ if (!config->experimentalPic) {
+ // -shared will change meaning when Module Linking is implemented.
+ if (config->shared) {
+ warn("creating shared libraries, with -shared, is not yet stable");
+ }
+
+ // -pie will change meaning when Module Linking is implemented.
+ if (config->pie) {
+ warn("creating PIEs, with -pie, is not yet stable");
+ }
}
}
static UndefinedGlobal *
createUndefinedGlobal(StringRef name, llvm::wasm::WasmGlobalType *type) {
auto *sym = cast<UndefinedGlobal>(symtab->addUndefinedGlobal(
- name, name, defaultModule, WASM_SYMBOL_UNDEFINED, nullptr, type));
+ name, None, None, WASM_SYMBOL_UNDEFINED, nullptr, type));
config->allowUndefinedSymbols.insert(sym->getName());
sym->isUsedInRegularObj = true;
return sym;
static GlobalSymbol *createGlobalVariable(StringRef name, bool isMutable,
int value) {
llvm::wasm::WasmGlobal wasmGlobal;
- wasmGlobal.Type = {WASM_TYPE_I32, isMutable};
- wasmGlobal.InitExpr.Value.Int32 = value;
- wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ if (config->is64) {
+ wasmGlobal.Type = {WASM_TYPE_I64, isMutable};
+ wasmGlobal.InitExpr.Value.Int64 = value;
+ wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I64_CONST;
+ } else {
+ wasmGlobal.Type = {WASM_TYPE_I32, isMutable};
+ wasmGlobal.InitExpr.Value.Int32 = value;
+ wasmGlobal.InitExpr.Opcode = WASM_OPCODE_I32_CONST;
+ }
wasmGlobal.SymbolName = name;
return symtab->addSyntheticGlobal(name, WASM_SYMBOL_VISIBILITY_HIDDEN,
make<InputGlobal>(wasmGlobal, nullptr));
static WasmSignature nullSignature = {{}, {}};
static WasmSignature i32ArgSignature = {{}, {ValType::I32}};
+ static WasmSignature i64ArgSignature = {{}, {ValType::I64}};
static llvm::wasm::WasmGlobalType globalTypeI32 = {WASM_TYPE_I32, false};
+ static llvm::wasm::WasmGlobalType globalTypeI64 = {WASM_TYPE_I64, false};
static llvm::wasm::WasmGlobalType mutableGlobalTypeI32 = {WASM_TYPE_I32,
true};
+ static llvm::wasm::WasmGlobalType mutableGlobalTypeI64 = {WASM_TYPE_I64,
+ true};
WasmSym::callCtors = symtab->addSyntheticFunction(
"__wasm_call_ctors", WASM_SYMBOL_VISIBILITY_HIDDEN,
make<SyntheticFunction>(nullSignature, "__wasm_call_ctors"));
if (config->isPic) {
- WasmSym::stackPointer =
- createUndefinedGlobal("__stack_pointer", &mutableGlobalTypeI32);
+ WasmSym::stackPointer = createUndefinedGlobal(
+ "__stack_pointer",
+ config->is64 ? &mutableGlobalTypeI64 : &mutableGlobalTypeI32);
// For PIC code, we import two global variables (__memory_base and
// __table_base) from the environment and use these as the offset at
// which to load our static data and function table.
// See:
// https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
- WasmSym::memoryBase =
- createUndefinedGlobal("__memory_base", &globalTypeI32);
+ WasmSym::memoryBase = createUndefinedGlobal(
+ "__memory_base", config->is64 ? &globalTypeI64 : &globalTypeI32);
WasmSym::tableBase = createUndefinedGlobal("__table_base", &globalTypeI32);
WasmSym::memoryBase->markLive();
WasmSym::tableBase->markLive();
WasmSym::tlsAlign = createGlobalVariable("__tls_align", false, 1);
WasmSym::initTLS = symtab->addSyntheticFunction(
"__wasm_init_tls", WASM_SYMBOL_VISIBILITY_HIDDEN,
- make<SyntheticFunction>(i32ArgSignature, "__wasm_init_tls"));
+ make<SyntheticFunction>(config->is64 ? i64ArgSignature
+ : i32ArgSignature,
+ "__wasm_init_tls"));
}
}
os << toString(*arg) << "\n";
}
}
- return data.str();
+ return std::string(data.str());
}
// The --wrap option is a feature to rename symbols so that you can write
};
static Symbol *addUndefined(StringRef name) {
- return symtab->addUndefinedFunction(name, "", "", WASM_SYMBOL_UNDEFINED,
+ return symtab->addUndefinedFunction(name, None, None, WASM_SYMBOL_UNDEFINED,
nullptr, nullptr, false);
}
errorHandler().errorLimit = args::getInteger(args, OPT_error_limit, 20);
readConfigs(args);
+
+ createFiles(args);
+ if (errorCount())
+ return;
+
setConfigs();
checkOptions(args);
+ if (errorCount())
+ return;
if (auto *arg = args.getLastArg(OPT_allow_undefined_file))
readImportFile(arg->getValue());
- if (!args.hasArg(OPT_INPUT)) {
- error("no input files");
+ // 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 (auto e = tryCreateFile(config->outputFile))
+ error("cannot open output file " + config->outputFile + ": " + e.message());
+ // TODO(sbc): add check for map file too once we add support for that.
+ if (errorCount())
return;
- }
// Handle --trace-symbol.
for (auto *arg : args.filtered(OPT_trace_symbol))
createSyntheticSymbols();
- createFiles(args);
- if (errorCount())
- return;
-
// Add all files to the symbol table. This will add almost all
// symbols that we need to the symbol table.
for (InputFile *f : files)
if (entrySym && entrySym->isDefined())
entrySym->forceExport = true;
else
- error("entry symbol not defined (pass --no-entry to supress): " +
+ error("entry symbol not defined (pass --no-entry to suppress): " +
config->entry);
}
llvm_unreachable("unknown reloc type");
}
+bool relocIs64(uint8_t relocType) {
+ switch (relocType) {
+ case R_WASM_MEMORY_ADDR_LEB64:
+ case R_WASM_MEMORY_ADDR_SLEB64:
+ case R_WASM_MEMORY_ADDR_REL_SLEB64:
+ case R_WASM_MEMORY_ADDR_I64:
+ return true;
+ default:
+ return false;
+ }
+}
+
std::string toString(const wasm::InputChunk *c) {
return (toString(c->file) + ":(" + c->getName() + ")").str();
}
void InputChunk::verifyRelocTargets() const {
for (const WasmRelocation &rel : relocations) {
- uint32_t existingValue;
+ uint64_t existingValue;
unsigned bytesRead = 0;
- uint32_t offset = rel.Offset - getInputSectionOffset();
+ auto offset = rel.Offset - getInputSectionOffset();
const uint8_t *loc = data().data() + offset;
switch (rel.Type) {
case R_WASM_TYPE_INDEX_LEB:
case R_WASM_GLOBAL_INDEX_LEB:
case R_WASM_EVENT_INDEX_LEB:
case R_WASM_MEMORY_ADDR_LEB:
+ case R_WASM_MEMORY_ADDR_LEB64:
existingValue = decodeULEB128(loc, &bytesRead);
break;
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
+ case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_REL_SLEB:
- existingValue = static_cast<uint32_t>(decodeSLEB128(loc, &bytesRead));
+ case R_WASM_MEMORY_ADDR_REL_SLEB64:
+ existingValue = static_cast<uint64_t>(decodeSLEB128(loc, &bytesRead));
break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_FUNCTION_OFFSET_I32:
case R_WASM_SECTION_OFFSET_I32:
- existingValue = static_cast<uint32_t>(read32le(loc));
+ case R_WASM_GLOBAL_INDEX_I32:
+ existingValue = read32le(loc);
+ break;
+ case R_WASM_MEMORY_ADDR_I64:
+ existingValue = read64le(loc);
break;
default:
llvm_unreachable("unknown relocation type");
if (bytesRead && bytesRead != 5)
warn("expected LEB at relocation site be 5-byte padded");
- if (rel.Type != R_WASM_GLOBAL_INDEX_LEB) {
- uint32_t expectedValue = file->calcExpectedValue(rel);
+ if (rel.Type != R_WASM_GLOBAL_INDEX_LEB &&
+ rel.Type != R_WASM_GLOBAL_INDEX_I32) {
+ auto expectedValue = file->calcExpectedValue(rel);
if (expectedValue != existingValue)
warn("unexpected existing value for " + relocTypeToString(rel.Type) +
": existing=" + Twine(existingValue) +
for (const WasmRelocation &rel : relocations) {
uint8_t *loc = buf + rel.Offset + off;
- uint32_t value = file->calcNewValue(rel);
+ auto value = file->calcNewValue(rel);
LLVM_DEBUG(dbgs() << "apply reloc: type=" << relocTypeToString(rel.Type));
if (rel.Type != R_WASM_TYPE_INDEX_LEB)
LLVM_DEBUG(dbgs() << " sym=" << file->getSymbols()[rel.Index]->getName());
case R_WASM_MEMORY_ADDR_LEB:
encodeULEB128(value, loc, 5);
break;
+ case R_WASM_MEMORY_ADDR_LEB64:
+ encodeULEB128(value, loc, 10);
+ break;
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_TABLE_INDEX_REL_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_REL_SLEB:
encodeSLEB128(static_cast<int32_t>(value), loc, 5);
break;
+ case R_WASM_MEMORY_ADDR_SLEB64:
+ case R_WASM_MEMORY_ADDR_REL_SLEB64:
+ encodeSLEB128(static_cast<int64_t>(value), loc, 10);
+ break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_MEMORY_ADDR_I32:
case R_WASM_FUNCTION_OFFSET_I32:
case R_WASM_SECTION_OFFSET_I32:
+ case R_WASM_GLOBAL_INDEX_I32:
write32le(loc, value);
break;
+ case R_WASM_MEMORY_ADDR_I64:
+ write64le(loc, value);
+ break;
default:
llvm_unreachable("unknown relocation type");
}
// Write a relocation value without padding and return the number of bytes
// witten.
static unsigned writeCompressedReloc(uint8_t *buf, const WasmRelocation &rel,
- uint32_t value) {
+ uint64_t value) {
switch (rel.Type) {
case R_WASM_TYPE_INDEX_LEB:
case R_WASM_FUNCTION_INDEX_LEB:
case R_WASM_GLOBAL_INDEX_LEB:
case R_WASM_EVENT_INDEX_LEB:
case R_WASM_MEMORY_ADDR_LEB:
+ case R_WASM_MEMORY_ADDR_LEB64:
return encodeULEB128(value, buf);
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
- return encodeSLEB128(static_cast<int32_t>(value), buf);
+ case R_WASM_MEMORY_ADDR_SLEB64:
+ return encodeSLEB128(static_cast<int64_t>(value), buf);
default:
llvm_unreachable("unexpected relocation type");
}
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
return 5;
+ case R_WASM_MEMORY_ADDR_LEB64:
+ case R_WASM_MEMORY_ADDR_SLEB64:
+ return 10;
default:
llvm_unreachable("unexpected relocation type");
}
}
-static unsigned getRelocWidth(const WasmRelocation &rel, uint32_t value) {
- uint8_t buf[5];
+static unsigned getRelocWidth(const WasmRelocation &rel, uint64_t value) {
+ uint8_t buf[10];
return writeCompressedReloc(buf, rel, value);
}
LLVM_DEBUG(dbgs() << "generating runtime relocations: " << getName()
<< " count=" << relocations.size() << "\n");
+ unsigned opcode_ptr_const =
+ config->is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST;
+ unsigned opcode_ptr_add =
+ config->is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD;
+
// TODO(sbc): Encode the relocations in the data section and write a loop
// here to apply them.
uint32_t segmentVA = outputSeg->startVA + outputSegmentOffset;
for (const WasmRelocation &rel : relocations) {
- uint32_t offset = rel.Offset - getInputSectionOffset();
- uint32_t outputOffset = segmentVA + offset;
+ uint64_t offset = rel.Offset - getInputSectionOffset();
+ uint64_t outputOffset = segmentVA + offset;
LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)
<< " addend=" << rel.Addend << " index=" << rel.Index
writeUleb128(os, WasmSym::memoryBase->getGlobalIndex(), "memory_base");
// Add the offset of the relocation
- writeU8(os, WASM_OPCODE_I32_CONST, "I32_CONST");
+ writeU8(os, opcode_ptr_const, "CONST");
writeSleb128(os, outputOffset, "offset");
- writeU8(os, WASM_OPCODE_I32_ADD, "ADD");
+ writeU8(os, opcode_ptr_add, "ADD");
+
+ bool is64 = relocIs64(rel.Type);
+ unsigned opcode_reloc_const =
+ is64 ? WASM_OPCODE_I64_CONST : WASM_OPCODE_I32_CONST;
+ unsigned opcode_reloc_add =
+ is64 ? WASM_OPCODE_I64_ADD : WASM_OPCODE_I32_ADD;
+ unsigned opcode_reloc_store =
+ is64 ? WASM_OPCODE_I64_STORE : WASM_OPCODE_I32_STORE;
Symbol *sym = file->getSymbol(rel);
// Now figure out what we want to store
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, sym->getGOTIndex(), "global index");
if (rel.Addend) {
- writeU8(os, WASM_OPCODE_I32_CONST, "CONST");
+ writeU8(os, opcode_reloc_const, "CONST");
writeSleb128(os, rel.Addend, "addend");
- writeU8(os, WASM_OPCODE_I32_ADD, "ADD");
+ writeU8(os, opcode_reloc_add, "ADD");
}
} else {
const GlobalSymbol* baseSymbol = WasmSym::memoryBase;
baseSymbol = WasmSym::tableBase;
writeU8(os, WASM_OPCODE_GLOBAL_GET, "GLOBAL_GET");
writeUleb128(os, baseSymbol->getGlobalIndex(), "base");
- writeU8(os, WASM_OPCODE_I32_CONST, "CONST");
+ writeU8(os, opcode_reloc_const, "CONST");
writeSleb128(os, file->calcNewValue(rel), "offset");
- writeU8(os, WASM_OPCODE_I32_ADD, "ADD");
+ writeU8(os, opcode_reloc_add, "ADD");
}
// Store that value at the virtual address
- writeU8(os, WASM_OPCODE_I32_STORE, "I32_STORE");
+ writeU8(os, opcode_reloc_store, "I32_STORE");
writeUleb128(os, 2, "align");
writeUleb128(os, 0, "offset");
}
void writeTo(uint8_t *sectionStart) const override;
StringRef getName() const override { return function->SymbolName; }
StringRef getDebugName() const override { return function->DebugName; }
- StringRef getExportName() const {
- return function ? function->ExportName : "";
+ llvm::Optional<StringRef> getExportName() const {
+ return function ? function->ExportName : llvm::Optional<StringRef>();
}
uint32_t getComdat() const override { return function->Comdat; }
uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); }
return "<internal>";
if (file->archiveName.empty())
- return file->getName();
+ return std::string(file->getName());
return (file->archiveName + "(" + file->getName() + ")").str();
}
// Relocations can contain addend for combined sections. This function takes a
// relocation and returns updated addend by offset in the output section.
-uint32_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
+uint64_t ObjFile::calcNewAddend(const WasmRelocation &reloc) const {
switch (reloc.Type) {
case R_WASM_MEMORY_ADDR_LEB:
+ case R_WASM_MEMORY_ADDR_LEB64:
+ case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_REL_SLEB:
+ case R_WASM_MEMORY_ADDR_REL_SLEB64:
case R_WASM_MEMORY_ADDR_I32:
+ case R_WASM_MEMORY_ADDR_I64:
case R_WASM_FUNCTION_OFFSET_I32:
return reloc.Addend;
case R_WASM_SECTION_OFFSET_I32:
// Calculate the value we expect to find at the relocation location.
// This is used as a sanity check before applying a relocation to a given
// location. It is useful for catching bugs in the compiler and linker.
-uint32_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
+uint64_t ObjFile::calcExpectedValue(const WasmRelocation &reloc) const {
switch (reloc.Type) {
case R_WASM_TABLE_INDEX_I32:
- case R_WASM_TABLE_INDEX_SLEB:
- case R_WASM_TABLE_INDEX_REL_SLEB: {
+ case R_WASM_TABLE_INDEX_SLEB: {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
return tableEntries[sym.Info.ElementIndex];
}
+ case R_WASM_TABLE_INDEX_REL_SLEB: {
+ const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
+ return tableEntriesRel[sym.Info.ElementIndex];
+ }
+ case R_WASM_MEMORY_ADDR_LEB:
+ case R_WASM_MEMORY_ADDR_LEB64:
case R_WASM_MEMORY_ADDR_SLEB:
+ case R_WASM_MEMORY_ADDR_SLEB64:
+ case R_WASM_MEMORY_ADDR_REL_SLEB:
+ case R_WASM_MEMORY_ADDR_REL_SLEB64:
case R_WASM_MEMORY_ADDR_I32:
- case R_WASM_MEMORY_ADDR_LEB:
- case R_WASM_MEMORY_ADDR_REL_SLEB: {
+ case R_WASM_MEMORY_ADDR_I64: {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
if (sym.isUndefined())
return 0;
const WasmSegment &segment =
wasmObj->dataSegments()[sym.Info.DataRef.Segment];
- return segment.Data.Offset.Value.Int32 + sym.Info.DataRef.Offset +
- reloc.Addend;
+ if (segment.Data.Offset.Opcode == WASM_OPCODE_I32_CONST)
+ return segment.Data.Offset.Value.Int32 + sym.Info.DataRef.Offset +
+ reloc.Addend;
+ else if (segment.Data.Offset.Opcode == WASM_OPCODE_I64_CONST)
+ return segment.Data.Offset.Value.Int64 + sym.Info.DataRef.Offset +
+ reloc.Addend;
+ else
+ llvm_unreachable("unknown init expr opcode");
}
case R_WASM_FUNCTION_OFFSET_I32: {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
return reloc.Index;
case R_WASM_FUNCTION_INDEX_LEB:
case R_WASM_GLOBAL_INDEX_LEB:
+ case R_WASM_GLOBAL_INDEX_I32:
case R_WASM_EVENT_INDEX_LEB: {
const WasmSymbol &sym = wasmObj->syms()[reloc.Index];
return sym.Info.ElementIndex;
}
// Translate from the relocation's index into the final linked output value.
-uint32_t ObjFile::calcNewValue(const WasmRelocation &reloc) const {
+uint64_t ObjFile::calcNewValue(const WasmRelocation &reloc) const {
const Symbol* sym = nullptr;
if (reloc.Type != R_WASM_TYPE_INDEX_LEB) {
sym = symbols[reloc.Index];
// We can end up with relocations against non-live symbols. For example
- // in debug sections.
+ // in debug sections. We return reloc.Addend because always returning zero
+ // causes the generation of spurious range-list terminators in the
+ // .debug_ranges section.
if ((isa<FunctionSymbol>(sym) || isa<DataSymbol>(sym)) && !sym->isLive())
- return 0;
+ return reloc.Addend;
}
switch (reloc.Type) {
return index;
}
- case R_WASM_MEMORY_ADDR_SLEB:
- case R_WASM_MEMORY_ADDR_I32:
case R_WASM_MEMORY_ADDR_LEB:
+ case R_WASM_MEMORY_ADDR_LEB64:
+ case R_WASM_MEMORY_ADDR_SLEB:
+ case R_WASM_MEMORY_ADDR_SLEB64:
case R_WASM_MEMORY_ADDR_REL_SLEB:
+ case R_WASM_MEMORY_ADDR_REL_SLEB64:
+ case R_WASM_MEMORY_ADDR_I32:
+ case R_WASM_MEMORY_ADDR_I64:
if (isa<UndefinedData>(sym) || sym->isUndefWeak())
return 0;
return cast<DefinedData>(sym)->getVirtualAddress() + reloc.Addend;
case R_WASM_FUNCTION_INDEX_LEB:
return getFunctionSymbol(reloc.Index)->getFunctionIndex();
case R_WASM_GLOBAL_INDEX_LEB:
+ case R_WASM_GLOBAL_INDEX_I32:
if (auto gs = dyn_cast<GlobalSymbol>(sym))
return gs->getGlobalIndex();
return sym->getGOTIndex();
return getEventSymbol(reloc.Index)->getEventIndex();
case R_WASM_FUNCTION_OFFSET_I32: {
auto *f = cast<DefinedFunction>(sym);
- return f->function->outputOffset + f->function->getFunctionCodeOffset() +
- reloc.Addend;
+ return f->function->outputOffset +
+ (f->function->getFunctionCodeOffset() + reloc.Addend);
}
case R_WASM_SECTION_OFFSET_I32:
return getSectionSymbol(reloc.Index)->section->outputOffset + reloc.Addend;
return;
ArrayRef<WasmRelocation> relocs = section->Relocations;
- assert(std::is_sorted(relocs.begin(), relocs.end(),
- [](const WasmRelocation &r1, const WasmRelocation &r2) {
- return r1.Offset < r2.Offset;
- }));
- assert(std::is_sorted(
- chunks.begin(), chunks.end(), [](InputChunk *c1, InputChunk *c2) {
- return c1->getInputSectionOffset() < c2->getInputSectionOffset();
+ assert(llvm::is_sorted(
+ relocs, [](const WasmRelocation &r1, const WasmRelocation &r2) {
+ return r1.Offset < r2.Offset;
}));
+ assert(llvm::is_sorted(chunks, [](InputChunk *c1, InputChunk *c2) {
+ return c1->getInputSectionOffset() < c2->getInputSectionOffset();
+ }));
auto relocsNext = relocs.begin();
auto relocsEnd = relocs.end();
// verifying the existing table index relocations
uint32_t totalFunctions =
wasmObj->getNumImportedFunctions() + wasmObj->functions().size();
+ tableEntriesRel.resize(totalFunctions);
tableEntries.resize(totalFunctions);
for (const WasmElemSegment &seg : wasmObj->elements()) {
- if (seg.Offset.Opcode != WASM_OPCODE_I32_CONST)
+ int64_t offset;
+ if (seg.Offset.Opcode == WASM_OPCODE_I32_CONST)
+ offset = seg.Offset.Value.Int32;
+ else if (seg.Offset.Opcode == WASM_OPCODE_I64_CONST)
+ offset = seg.Offset.Value.Int64;
+ else
fatal(toString(this) + ": invalid table elements");
- uint32_t offset = seg.Offset.Value.Int32;
- for (uint32_t index = 0; index < seg.Functions.size(); index++) {
-
- uint32_t functionIndex = seg.Functions[index];
+ for (size_t index = 0; index < seg.Functions.size(); index++) {
+ auto functionIndex = seg.Functions[index];
+ tableEntriesRel[functionIndex] = index;
tableEntries[functionIndex] = offset + index;
}
}
}
case WASM_SYMBOL_TYPE_DATA: {
InputSegment *seg = segments[sym.Info.DataRef.Segment];
- uint32_t offset = sym.Info.DataRef.Offset;
- uint32_t size = sym.Info.DataRef.Size;
+ auto offset = sym.Info.DataRef.Offset;
+ auto size = sym.Info.DataRef.Size;
if (sym.isBindingLocal())
return make<DefinedData>(name, flags, this, seg, offset, size);
if (seg->discarded)
if (objSym.isUndefined() || excludedByComdat) {
flags |= WASM_SYMBOL_UNDEFINED;
if (objSym.isExecutable())
- return symtab->addUndefinedFunction(name, "", "", flags, &f, nullptr,
+ return symtab->addUndefinedFunction(name, None, None, flags, &f, nullptr,
true);
return symtab->addUndefinedData(name, flags, &f);
}
return symtab->addDefinedData(name, flags, &f, nullptr, 0, 0);
}
+bool BitcodeFile::doneLTO = false;
+
void BitcodeFile::parse() {
+ if (doneLTO) {
+ error(toString(this) + ": attempt to add bitcode file after LTO.");
+ return;
+ }
+
obj = check(lto::InputFile::create(MemoryBufferRef(
mb.getBuffer(), saver.save(archiveName + mb.getBufferIdentifier()))));
Triple t(obj->getTargetTriple());
if (t.getArch() != Triple::wasm32) {
- error(toString(mb.getBufferIdentifier()) + ": machine type must be wasm32");
+ error(toString(this) + ": machine type must be wasm32");
return;
}
std::vector<bool> keptComdats;
public:
explicit ObjFile(MemoryBufferRef m, StringRef archiveName)
: InputFile(ObjectKind, m) {
- this->archiveName = archiveName;
+ this->archiveName = std::string(archiveName);
}
static bool classof(const InputFile *f) { return f->kind() == ObjectKind; }
void dumpInfo() const;
uint32_t calcNewIndex(const WasmRelocation &reloc) const;
- uint32_t calcNewValue(const WasmRelocation &reloc) const;
- uint32_t calcNewAddend(const WasmRelocation &reloc) const;
- uint32_t calcExpectedValue(const WasmRelocation &reloc) const;
+ uint64_t calcNewValue(const WasmRelocation &reloc) const;
+ uint64_t calcNewAddend(const WasmRelocation &reloc) const;
+ uint64_t calcExpectedValue(const WasmRelocation &reloc) const;
Symbol *getSymbol(const WasmRelocation &reloc) const {
return symbols[reloc.Index];
};
std::vector<bool> typeIsUsed;
// Maps function indices to table indices
std::vector<uint32_t> tableEntries;
+ std::vector<uint32_t> tableEntriesRel;
std::vector<bool> keptComdats;
std::vector<InputSegment *> segments;
std::vector<InputFunction *> functions;
public:
explicit BitcodeFile(MemoryBufferRef m, StringRef archiveName)
: InputFile(BitcodeKind, m) {
- this->archiveName = archiveName;
+ this->archiveName = std::string(archiveName);
}
static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; }
void parse();
std::unique_ptr<llvm::lto::InputFile> obj;
+
+ // Set to true once LTO is complete in order prevent further bitcode objects
+ // being added.
+ static bool doneLTO;
};
inline bool isBitcode(MemoryBufferRef mb) {
if (config->saveTemps)
checkError(c.addSaveTemps(config->outputFile.str() + ".",
/*UseInputModulePath*/ true));
-
- lto::ThinBackend backend;
- if (config->thinLTOJobs != -1U)
- backend = lto::createInProcessThinBackend(config->thinLTOJobs);
+ lto::ThinBackend backend = lto::createInProcessThinBackend(
+ llvm::heavyweight_hardware_concurrency(config->thinLTOJobs));
return std::make_unique<lto::LTO>(std::move(c), backend,
config->ltoPartitions);
}
static void undefine(Symbol *s) {
if (auto f = dyn_cast<DefinedFunction>(s))
- replaceSymbol<UndefinedFunction>(f, f->getName(), "", "", 0, f->getFile(),
- f->signature);
+ replaceSymbol<UndefinedFunction>(f, f->getName(), None, None, 0,
+ f->getFile(), f->signature);
else if (isa<DefinedData>(s))
replaceSymbol<UndefinedData>(s, s->getName(), 0, s->getFile());
else
def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
HelpText<"Add a directory to the library search path">;
-def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">;
+def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
-def no_threads: F<"no-threads">,
- HelpText<"Do not run the linker multi-threaded">;
+def mllvm: S<"mllvm">, HelpText<"Options to pass to LLVM">;
def no_color_diagnostics: F<"no-color-diagnostics">,
HelpText<"Do not use colors in diagnostics">;
defm reproduce: Eq<"reproduce", "Dump linker invocation and input files for debugging">;
+defm rsp_quoting: Eq<"rsp-quoting", "Quoting style for response files">,
+ MetaVarName<"[posix,windows]">;
+
def shared: F<"shared">, HelpText<"Build a shared object">;
def strip_all: F<"strip-all">, HelpText<"Strip all symbols">;
def strip_debug: F<"strip-debug">, HelpText<"Strip debugging information">;
-def threads: F<"threads">, HelpText<"Run the linker multi-threaded">;
+defm threads
+ : Eq<"threads", "Number of threads. '1' disables multi-threading. By "
+ "default all available hardware threads are used">;
def trace: F<"trace">, HelpText<"Print the names of the input files">;
def: J<"entry=">, Alias<entry>;
def: Flag<["-"], "E">, Alias<export_dynamic>, HelpText<"Alias for --export-dynamic">;
def: Flag<["-"], "i">, Alias<initial_memory>;
-def: Flag<["-"], "m">, Alias<max_memory>;
def: Flag<["-"], "r">, Alias<relocatable>;
def: Flag<["-"], "s">, Alias<strip_all>, HelpText<"Alias for --strip-all">;
def: Flag<["-"], "S">, Alias<strip_debug>, HelpText<"Alias for --strip-debug">;
def thinlto_cache_dir: J<"thinlto-cache-dir=">,
HelpText<"Path to ThinLTO cached object file directory">;
defm thinlto_cache_policy: Eq<"thinlto-cache-policy", "Pruning policy for the ThinLTO cache">;
-def thinlto_jobs: J<"thinlto-jobs=">, HelpText<"Number of ThinLTO jobs">;
+def thinlto_jobs: J<"thinlto-jobs=">,
+ HelpText<"Number of ThinLTO jobs. Default to --threads=">;
+
+// Experimental PIC mode.
+def experimental_pic: F<"experimental-pic">,
+ HelpText<"Enable Experimental PIC">;
#include "OutputSegment.h"
#include "WriterUtils.h"
#include "lld/Common/ErrorHandler.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/LEB128.h"
+#include "llvm/Support/Parallel.h"
#define DEBUG_TYPE "lld"
std::string toString(const wasm::OutputSection &sec) {
if (!sec.name.empty())
return (sec.getSectionName() + "(" + sec.name + ")").str();
- return sec.getSectionName();
+ return std::string(sec.getSectionName());
}
namespace wasm {
}
static bool allowUndefined(const Symbol* sym) {
- // Historically --allow-undefined doesn't work for data symbols since we don't
- // have any way to represent these as imports in the final binary. The idea
- // behind allowing undefined symbols is to allow importing these symbols from
- // the embedder and we can't do this for data symbols (at least not without
- // compiling with -fPIC)
- if (isa<DataSymbol>(sym))
- return false;
+ // Undefined functions with explicit import name are allowed to be undefined
+ // at link time.
+ if (auto *F = dyn_cast<UndefinedFunction>(sym))
+ if (F->importName)
+ return true;
return (config->allowUndefined ||
config->allowUndefinedSymbols.count(sym->getName()) != 0);
}
out.elemSec->addEntry(cast<FunctionSymbol>(sym));
break;
case R_WASM_GLOBAL_INDEX_LEB:
+ case R_WASM_GLOBAL_INDEX_I32:
if (!isa<GlobalSymbol>(sym))
addGOTEntry(sym);
break;
case R_WASM_TABLE_INDEX_SLEB:
case R_WASM_MEMORY_ADDR_SLEB:
case R_WASM_MEMORY_ADDR_LEB:
+ case R_WASM_MEMORY_ADDR_SLEB64:
+ case R_WASM_MEMORY_ADDR_LEB64:
// Certain relocation types can't be used when building PIC output,
// since they would require absolute symbol addresses at link time.
error(toString(file) + ": relocation " + relocTypeToString(reloc.Type) +
break;
case R_WASM_TABLE_INDEX_I32:
case R_WASM_MEMORY_ADDR_I32:
+ case R_WASM_MEMORY_ADDR_I64:
// These relocation types are only present in the data section and
// will be converted into code by `generateRelocationCode`. This code
// requires the symbols to have GOT entires.
// Because all bitcode files that the program consists of are passed
// to the compiler at once, it can do whole-program optimization.
void SymbolTable::addCombinedLTOObject() {
+ // Prevent further LTO objects being included
+ BitcodeFile::doneLTO = true;
+
if (bitcodeFiles.empty())
return;
sym->isUsedInRegularObj = false;
sym->canInline = true;
sym->traced = trace;
+ sym->forceExport = false;
symVector.emplace_back(sym);
return {sym, true};
}
}
// Check the type of new symbol matches that of the symbol is replacing.
-// Returns true if the function types match, false is there is a singature
+// Returns true if the function types match, false is there is a signature
// mismatch.
static bool signatureMatches(FunctionSymbol *existing,
const WasmSignature *newSig) {
std::tie(s, wasInserted) = insert(name, file);
auto replaceSym = [&](Symbol *sym) {
- // If the new defined function doesn't have signture (i.e. bitcode
+ // If the new defined function doesn't have signature (i.e. bitcode
// functions) but the old symbol does, then preserve the old signature
const WasmSignature *oldSig = s->getSignature();
auto* newSym = replaceSymbol<DefinedFunction>(sym, name, flags, file, function);
Symbol *SymbolTable::addDefinedData(StringRef name, uint32_t flags,
InputFile *file, InputSegment *segment,
- uint32_t address, uint32_t size) {
+ uint64_t address, uint64_t size) {
LLVM_DEBUG(dbgs() << "addDefinedData:" << name << " addr:" << address
<< "\n");
Symbol *s;
// become available when the LTO object is read. In this case we silently
// replace the empty attributes with the valid ones.
template <typename T>
-static void setImportAttributes(T *existing, StringRef importName,
- StringRef importModule, InputFile *file) {
- if (!importName.empty()) {
- if (existing->importName.empty())
+static void setImportAttributes(T *existing, Optional<StringRef> importName,
+ Optional<StringRef> importModule,
+ uint32_t flags, InputFile *file) {
+ if (importName) {
+ if (!existing->importName)
existing->importName = importName;
if (existing->importName != importName)
error("import name mismatch for symbol: " + toString(*existing) +
- "\n>>> defined as " + existing->importName + " in " +
- toString(existing->getFile()) + "\n>>> defined as " + importName +
+ "\n>>> defined as " + *existing->importName + " in " +
+ toString(existing->getFile()) + "\n>>> defined as " + *importName +
" in " + toString(file));
}
- if (!importModule.empty()) {
- if (existing->importModule.empty())
+ if (importModule) {
+ if (!existing->importModule)
existing->importModule = importModule;
if (existing->importModule != importModule)
error("import module mismatch for symbol: " + toString(*existing) +
- "\n>>> defined as " + existing->importModule + " in " +
- toString(existing->getFile()) + "\n>>> defined as " + importModule +
- " in " + toString(file));
+ "\n>>> defined as " + *existing->importModule + " in " +
+ toString(existing->getFile()) + "\n>>> defined as " +
+ *importModule + " in " + toString(file));
+ }
+
+ // Update symbol binding, if the existing symbol is weak
+ uint32_t binding = flags & WASM_SYMBOL_BINDING_MASK;
+ if (existing->isWeak() && binding != WASM_SYMBOL_BINDING_WEAK) {
+ existing->flags = (existing->flags & ~WASM_SYMBOL_BINDING_MASK) | binding;
}
}
-Symbol *SymbolTable::addUndefinedFunction(StringRef name, StringRef importName,
- StringRef importModule,
+Symbol *SymbolTable::addUndefinedFunction(StringRef name,
+ Optional<StringRef> importName,
+ Optional<StringRef> importModule,
uint32_t flags, InputFile *file,
const WasmSignature *sig,
bool isCalledDirectly) {
LLVM_DEBUG(dbgs() << "addUndefinedFunction: " << name << " ["
<< (sig ? toString(*sig) : "none")
- << "] IsCalledDirectly:" << isCalledDirectly << "\n");
+ << "] IsCalledDirectly:" << isCalledDirectly << " flags=0x"
+ << utohexstr(flags) << "\n");
assert(flags & WASM_SYMBOL_UNDEFINED);
Symbol *s;
reportTypeError(s, file, WASM_SYMBOL_TYPE_FUNCTION);
return s;
}
- auto *existingUndefined = dyn_cast<UndefinedFunction>(existingFunction);
if (!existingFunction->signature && sig)
existingFunction->signature = sig;
+ auto *existingUndefined = dyn_cast<UndefinedFunction>(existingFunction);
if (isCalledDirectly && !signatureMatches(existingFunction, sig)) {
- // If the existing undefined functions is not called direcltly then let
+ // If the existing undefined functions is not called directly then let
// this one take precedence. Otherwise the existing function is either
- // direclty called or defined, in which case we need a function variant.
+ // directly called or defined, in which case we need a function variant.
if (existingUndefined && !existingUndefined->isCalledDirectly)
replaceSym();
else if (getFunctionVariant(s, sig, file, &s))
replaceSym();
}
if (existingUndefined)
- setImportAttributes(existingUndefined, importName, importModule, file);
+ setImportAttributes(existingUndefined, importName, importModule, flags,
+ file);
}
return s;
return s;
}
-Symbol *SymbolTable::addUndefinedGlobal(StringRef name, StringRef importName,
- StringRef importModule, uint32_t flags,
- InputFile *file,
+Symbol *SymbolTable::addUndefinedGlobal(StringRef name,
+ Optional<StringRef> importName,
+ Optional<StringRef> importModule,
+ uint32_t flags, InputFile *file,
const WasmGlobalType *type) {
LLVM_DEBUG(dbgs() << "addUndefinedGlobal: " << name << "\n");
assert(flags & WASM_SYMBOL_UNDEFINED);
// Create a new variant;
LLVM_DEBUG(dbgs() << "added new variant\n");
variant = reinterpret_cast<Symbol *>(make<SymbolUnion>());
+ variant->isUsedInRegularObj =
+ !file || file->kind() == InputFile::ObjectKind;
+ variant->canInline = true;
+ variant->traced = false;
+ variant->forceExport = false;
variants.push_back(variant);
} else {
LLVM_DEBUG(dbgs() << "variant already exists: " << toString(*variant) << "\n");
auto *func = make<SyntheticFunction>(sig, sym->getName(), debugName);
func->setBody(unreachableFn);
syntheticFunctions.emplace_back(func);
- replaceSymbol<DefinedFunction>(sym, sym->getName(), sym->getFlags(), nullptr,
- func);
+ // Mark new symbols as local. For relocatable output we don't want them
+ // to be exported outside the object file.
+ replaceSymbol<DefinedFunction>(sym, debugName, WASM_SYMBOL_BINDING_LOCAL,
+ nullptr, func);
return func;
}
if (symbol != defined) {
auto *f = cast<FunctionSymbol>(symbol);
reportFunctionSignatureMismatch(symName, f, defined, false);
- StringRef debugName = saver.save("unreachable:" + toString(*f));
+ StringRef debugName = saver.save("signature_mismatch:" + toString(*f));
replaceWithUnreachable(f, *f->signature, debugName);
}
}
#include "lld/Common/LLVM.h"
#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/Optional.h"
namespace lld {
namespace wasm {
Symbol *addDefinedFunction(StringRef name, uint32_t flags, InputFile *file,
InputFunction *function);
Symbol *addDefinedData(StringRef name, uint32_t flags, InputFile *file,
- InputSegment *segment, uint32_t address,
- uint32_t size);
+ InputSegment *segment, uint64_t address,
+ uint64_t size);
Symbol *addDefinedGlobal(StringRef name, uint32_t flags, InputFile *file,
InputGlobal *g);
Symbol *addDefinedEvent(StringRef name, uint32_t flags, InputFile *file,
InputEvent *e);
- Symbol *addUndefinedFunction(StringRef name, StringRef importName,
- StringRef importModule, uint32_t flags,
- InputFile *file, const WasmSignature *signature,
+ Symbol *addUndefinedFunction(StringRef name,
+ llvm::Optional<StringRef> importName,
+ llvm::Optional<StringRef> importModule,
+ uint32_t flags, InputFile *file,
+ const WasmSignature *signature,
bool isCalledDirectly);
Symbol *addUndefinedData(StringRef name, uint32_t flags, InputFile *file);
- Symbol *addUndefinedGlobal(StringRef name, StringRef importName,
- StringRef importModule, uint32_t flags,
- InputFile *file, const WasmGlobalType *type);
+ Symbol *addUndefinedGlobal(StringRef name,
+ llvm::Optional<StringRef> importName,
+ llvm::Optional<StringRef> importModule,
+ uint32_t flags, InputFile *file,
+ const WasmGlobalType *type);
void addLazy(ArchiveFile *f, const llvm::object::Archive::Symbol *sym);
llvm::DenseMap<llvm::CachedHashStringRef, int> symMap;
std::vector<Symbol *> symVector;
- // For certain symbols types, e.g. function symbols, we allow for muliple
+ // For certain symbols types, e.g. function symbols, we allow for multiple
// variants of the same symbol with different signatures.
llvm::DenseMap<llvm::CachedHashStringRef, std::vector<Symbol *>> symVariants;
}
std::string maybeDemangleSymbol(StringRef name) {
+ // WebAssembly requires caller and callee signatures to match, so we mangle
+ // `main` in the case where we need to pass it arguments.
+ if (name == "__main_argc_argv")
+ return "main";
if (wasm::config->demangle)
return demangleItanium(name);
- return name;
+ return std::string(name);
}
std::string toString(wasm::Symbol::Kind kind) {
LLVM_DEBUG(dbgs() << "setGOTIndex " << name << " -> " << index << "\n");
assert(gotIndex == INVALID_INDEX);
if (config->isPic) {
- // Any symbol that is assigned a GOT entry must be exported othewise the
+ // Any symbol that is assigned a GOT entry must be exported otherwise the
// dynamic linker won't be able create the entry that contains it.
forceExport = true;
}
function ? &function->signature : nullptr),
function(function) {}
-uint32_t DefinedData::getVirtualAddress() const {
+uint64_t DefinedData::getVirtualAddress() const {
LLVM_DEBUG(dbgs() << "getVirtualAddress: " << getName() << "\n");
if (segment) {
// For thread local data, the symbol location is relative to the start of
return offset;
}
-void DefinedData::setVirtualAddress(uint32_t value) {
+void DefinedData::setVirtualAddress(uint64_t value) {
LLVM_DEBUG(dbgs() << "setVirtualAddress " << name << " -> " << value << "\n");
assert(!segment);
offset = value;
}
-uint32_t DefinedData::getOutputSegmentOffset() const {
+uint64_t DefinedData::getOutputSegmentOffset() const {
LLVM_DEBUG(dbgs() << "getOutputSegmentOffset: " << getName() << "\n");
return segment->outputSegmentOffset + offset;
}
-uint32_t DefinedData::getOutputSegmentIndex() const {
+uint64_t DefinedData::getOutputSegmentIndex() const {
LLVM_DEBUG(dbgs() << "getOutputSegmentIndex: " << getName() << "\n");
return segment->outputSeg->index;
}
#include "Config.h"
#include "lld/Common/LLVM.h"
+#include "llvm/ADT/Optional.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Wasm.h"
// Returns the file from which this symbol was created.
InputFile *getFile() const { return file; }
- uint32_t getFlags() const { return flags; }
-
InputChunk *getChunk() const;
// Indicates that the section or import for this symbol will be included in
protected:
Symbol(StringRef name, Kind k, uint32_t flags, InputFile *f)
- : name(name), file(f), flags(flags), symbolKind(k),
- referenced(!config->gcSections), requiresGOT(false),
- isUsedInRegularObj(false), forceExport(false), canInline(false),
- traced(false) {}
+ : name(name), file(f), symbolKind(k), referenced(!config->gcSections),
+ requiresGOT(false), isUsedInRegularObj(false), forceExport(false),
+ canInline(false), traced(false), flags(flags) {}
StringRef name;
InputFile *file;
- uint32_t flags;
uint32_t outputSymbolIndex = INVALID_INDEX;
uint32_t gotIndex = INVALID_INDEX;
Kind symbolKind;
// True if this symbol is specified by --trace-symbol option.
bool traced : 1;
+
+ uint32_t flags;
};
class FunctionSymbol : public Symbol {
class UndefinedFunction : public FunctionSymbol {
public:
- UndefinedFunction(StringRef name, StringRef importName,
- StringRef importModule, uint32_t flags,
+ UndefinedFunction(StringRef name, llvm::Optional<StringRef> importName,
+ llvm::Optional<StringRef> importModule, uint32_t flags,
InputFile *file = nullptr,
const WasmSignature *type = nullptr,
bool isCalledDirectly = true)
: FunctionSymbol(name, UndefinedFunctionKind, flags, file, type),
- importName(importName), importModule(importModule), isCalledDirectly(isCalledDirectly) {}
+ importName(importName), importModule(importModule),
+ isCalledDirectly(isCalledDirectly) {}
static bool classof(const Symbol *s) {
return s->kind() == UndefinedFunctionKind;
}
- StringRef importName;
- StringRef importModule;
+ llvm::Optional<StringRef> importName;
+ llvm::Optional<StringRef> importModule;
bool isCalledDirectly;
};
public:
// Constructor for regular data symbols originating from input files.
DefinedData(StringRef name, uint32_t flags, InputFile *f,
- InputSegment *segment, uint32_t offset, uint32_t size)
+ InputSegment *segment, uint64_t offset, uint64_t size)
: DataSymbol(name, DefinedDataKind, flags, f), segment(segment),
offset(offset), size(size) {}
static bool classof(const Symbol *s) { return s->kind() == DefinedDataKind; }
// Returns the output virtual address of a defined data symbol.
- uint32_t getVirtualAddress() const;
- void setVirtualAddress(uint32_t va);
+ uint64_t getVirtualAddress() const;
+ void setVirtualAddress(uint64_t va);
// Returns the offset of a defined data symbol within its OutputSegment.
- uint32_t getOutputSegmentOffset() const;
- uint32_t getOutputSegmentIndex() const;
- uint32_t getSize() const { return size; }
+ uint64_t getOutputSegmentOffset() const;
+ uint64_t getOutputSegmentIndex() const;
+ uint64_t getSize() const { return size; }
InputSegment *segment = nullptr;
protected:
- uint32_t offset = 0;
- uint32_t size = 0;
+ uint64_t offset = 0;
+ uint64_t size = 0;
};
class UndefinedData : public DataSymbol {
class UndefinedGlobal : public GlobalSymbol {
public:
- UndefinedGlobal(StringRef name, StringRef importName, StringRef importModule,
- uint32_t flags, InputFile *file = nullptr,
+ UndefinedGlobal(StringRef name, llvm::Optional<StringRef> importName,
+ llvm::Optional<StringRef> importModule, uint32_t flags,
+ InputFile *file = nullptr,
const WasmGlobalType *type = nullptr)
: GlobalSymbol(name, UndefinedGlobalKind, flags, file, type),
importName(importName), importModule(importModule) {}
return s->kind() == UndefinedGlobalKind;
}
- StringRef importName;
- StringRef importModule;
+ llvm::Optional<StringRef> importName;
+ llvm::Optional<StringRef> importModule;
};
// Wasm events are features that suspend the current execution and transfer the
// Lazy symbols can have a signature because they can replace an
// UndefinedFunction which which case we need to be able to preserve the
- // signture.
+ // signature.
// TODO(sbc): This repetition of the signature field is inelegant. Revisit
// the use of class hierarchy to represent symbol taxonomy.
const WasmSignature *signature = nullptr;
// It is important to keep the size of SymbolUnion small for performance and
// memory usage reasons. 96 bytes is a soft limit based on the size of
// UndefinedFunction on a 64-bit system.
-static_assert(sizeof(SymbolUnion) <= 96, "SymbolUnion too large");
+static_assert(sizeof(SymbolUnion) <= 112, "SymbolUnion too large");
void printTraceSymbol(Symbol *sym);
void printTraceSymbolUndefined(StringRef name, const InputFile* file);
}
if (config->sharedMemory)
import.Memory.Flags |= WASM_LIMITS_FLAG_IS_SHARED;
+ if (config->is64)
+ import.Memory.Flags |= WASM_LIMITS_FLAG_IS_64;
writeImport(os, import);
}
for (const Symbol *sym : importedSymbols) {
WasmImport import;
if (auto *f = dyn_cast<UndefinedFunction>(sym)) {
- import.Field = f->importName;
- import.Module = f->importModule;
+ import.Field = f->importName ? *f->importName : sym->getName();
+ import.Module = f->importModule ? *f->importModule : defaultModule;
} else if (auto *g = dyn_cast<UndefinedGlobal>(sym)) {
- import.Field = g->importName;
- import.Module = g->importModule;
+ import.Field = g->importName ? *g->importName : sym->getName();
+ import.Module = g->importModule ? *g->importModule : defaultModule;
} else {
import.Field = sym->getName();
import.Module = defaultModule;
flags |= WASM_LIMITS_FLAG_HAS_MAX;
if (config->sharedMemory)
flags |= WASM_LIMITS_FLAG_IS_SHARED;
+ if (config->is64)
+ flags |= WASM_LIMITS_FLAG_IS_64;
writeUleb128(os, flags, "memory limits flags");
writeUleb128(os, numMemoryPages, "initial pages");
if (hasMax)
writeUleb128(os, maxMemoryPages, "max pages");
}
+void EventSection::writeBody() {
+ raw_ostream &os = bodyOutputStream;
+
+ writeUleb128(os, inputEvents.size(), "event count");
+ for (InputEvent *e : inputEvents) {
+ e->event.Type.SigIndex = out.typeSec->lookupType(e->signature);
+ writeEvent(os, e->event);
+ }
+}
+
+void EventSection::addEvent(InputEvent *event) {
+ if (!event->live)
+ return;
+ uint32_t eventIndex =
+ out.importSec->getNumImportedEvents() + inputEvents.size();
+ LLVM_DEBUG(dbgs() << "addEvent: " << eventIndex << "\n");
+ event->setEventIndex(eventIndex);
+ inputEvents.push_back(event);
+}
+
void GlobalSection::assignIndexes() {
uint32_t globalIndex = out.importSec->getNumImportedGlobals();
for (InputGlobal *g : inputGlobals)
writeUleb128(os, numGlobals(), "global count");
for (InputGlobal *g : inputGlobals)
writeGlobal(os, g->global);
+ // TODO(wvo): when do these need I64_CONST?
for (const Symbol *sym : staticGotSymbols) {
WasmGlobal global;
global.Type = {WASM_TYPE_I32, false};
inputGlobals.push_back(global);
}
-void EventSection::writeBody() {
- raw_ostream &os = bodyOutputStream;
-
- writeUleb128(os, inputEvents.size(), "event count");
- for (InputEvent *e : inputEvents) {
- e->event.Type.SigIndex = out.typeSec->lookupType(e->signature);
- writeEvent(os, e->event);
- }
-}
-
-void EventSection::addEvent(InputEvent *event) {
- if (!event->live)
- return;
- uint32_t eventIndex =
- out.importSec->getNumImportedEvents() + inputEvents.size();
- LLVM_DEBUG(dbgs() << "addEvent: " << eventIndex << "\n");
- event->setEventIndex(eventIndex);
- inputEvents.push_back(event);
-}
-
void ExportSection::writeBody() {
raw_ostream &os = bodyOutputStream;
}
bool StartSection::isNeeded() const {
- return !config->relocatable && numSegments && config->sharedMemory;
+ return !config->relocatable && hasInitializedSegments && config->sharedMemory;
}
void StartSection::writeBody() {
for (const Symbol *sym : symtabEntries) {
assert(sym->isDefined() || sym->isUndefined());
WasmSymbolType kind = sym->getWasmType();
- uint32_t flags = sym->getFlags();
+ uint32_t flags = sym->flags;
writeU8(sub.os, kind, "sym kind");
writeUleb128(sub.os, flags, "sym flags");
protected:
};
-class MemorySection : public SyntheticSection {
-public:
- MemorySection() : SyntheticSection(llvm::wasm::WASM_SEC_MEMORY) {}
-
- bool isNeeded() const override { return !config->importMemory; }
- void writeBody() override;
-
- uint32_t numMemoryPages = 0;
- uint32_t maxMemoryPages = 0;
-};
-
class TableSection : public SyntheticSection {
public:
TableSection() : SyntheticSection(llvm::wasm::WASM_SEC_TABLE) {}
void writeBody() override;
};
-class GlobalSection : public SyntheticSection {
+class MemorySection : public SyntheticSection {
public:
- GlobalSection() : SyntheticSection(llvm::wasm::WASM_SEC_GLOBAL) {}
- uint32_t numGlobals() const {
- assert(isSealed);
- return inputGlobals.size() + dataAddressGlobals.size() +
- staticGotSymbols.size();
- }
- bool isNeeded() const override { return numGlobals() > 0; }
- void assignIndexes() override;
- void writeBody() override;
- void addGlobal(InputGlobal *global);
- void addDataAddressGlobal(DefinedData *global);
- void addStaticGOTEntry(Symbol *sym);
+ MemorySection() : SyntheticSection(llvm::wasm::WASM_SEC_MEMORY) {}
- std::vector<const DefinedData *> dataAddressGlobals;
+ bool isNeeded() const override { return !config->importMemory; }
+ void writeBody() override;
-protected:
- bool isSealed = false;
- std::vector<InputGlobal *> inputGlobals;
- std::vector<Symbol *> staticGotSymbols;
+ uint64_t numMemoryPages = 0;
+ uint64_t maxMemoryPages = 0;
};
// The event section contains a list of declared wasm events associated with the
std::vector<InputEvent *> inputEvents;
};
+class GlobalSection : public SyntheticSection {
+public:
+ GlobalSection() : SyntheticSection(llvm::wasm::WASM_SEC_GLOBAL) {}
+ uint32_t numGlobals() const {
+ assert(isSealed);
+ return inputGlobals.size() + dataAddressGlobals.size() +
+ staticGotSymbols.size();
+ }
+ bool isNeeded() const override { return numGlobals() > 0; }
+ void assignIndexes() override;
+ void writeBody() override;
+ void addGlobal(InputGlobal *global);
+ void addDataAddressGlobal(DefinedData *global);
+ void addStaticGOTEntry(Symbol *sym);
+
+ std::vector<const DefinedData *> dataAddressGlobals;
+
+protected:
+ bool isSealed = false;
+ std::vector<InputGlobal *> inputGlobals;
+ std::vector<Symbol *> staticGotSymbols;
+};
+
class ExportSection : public SyntheticSection {
public:
ExportSection() : SyntheticSection(llvm::wasm::WASM_SEC_EXPORT) {}
class StartSection : public SyntheticSection {
public:
- StartSection(uint32_t numSegments)
- : SyntheticSection(llvm::wasm::WASM_SEC_START), numSegments(numSegments) {
- }
+ StartSection(bool hasInitializedSegments)
+ : SyntheticSection(llvm::wasm::WASM_SEC_START),
+ hasInitializedSegments(hasInitializedSegments) {}
bool isNeeded() const override;
void writeBody() override;
protected:
- uint32_t numSegments;
+ bool hasInitializedSegments;
};
class ElemSection : public SyntheticSection {
class RelocSection : public SyntheticSection {
public:
RelocSection(StringRef name, OutputSection *sec)
- : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, name), sec(sec) {}
+ : SyntheticSection(llvm::wasm::WASM_SEC_CUSTOM, std::string(name)),
+ sec(sec) {}
void writeBody() override;
bool isNeeded() const override { return sec->getNumRelocations() > 0; };
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
-#include "lld/Common/Threads.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/LEB128.h"
+#include "llvm/Support/Parallel.h"
#include <cstdarg>
#include <map>
private:
void openFile();
+ bool needsPassiveInitialization(const OutputSegment *segment);
+ bool hasPassiveInitializedSegments();
+
void createInitMemoryFunction();
void createApplyRelocationsFunction();
void createCallCtorsFunction();
for (InputSection *section : file->customSections) {
StringRef name = section->getName();
// These custom sections are known the linker and synthesized rather than
- // blindly copied
+ // blindly copied.
if (name == "linking" || name == "name" || name == "producers" ||
name == "target_features" || name.startswith("reloc."))
continue;
- // .. or it is a debug section
+ // These custom sections are generated by `clang -fembed-bitcode`.
+ // These are used by the rust toolchain to ship LTO data along with
+ // compiled object code, but they don't want this included in the linker
+ // output.
+ if (name == ".llvmbc" || name == ".llvmcmd")
+ continue;
+ // Strip debug section in that option was specified.
if (stripDebug && name.startswith(".debug_"))
continue;
+ // Otherwise include custom sections by default and concatenate their
+ // contents.
customSectionMapping[name].push_back(section);
}
}
StringRef name = pair.first();
LLVM_DEBUG(dbgs() << "createCustomSection: " << name << "\n");
- OutputSection *sec = make<CustomSection>(name, pair.second);
+ OutputSection *sec = make<CustomSection>(std::string(name), pair.second);
if (config->relocatable || config->emitRelocs) {
auto *sym = make<OutputSectionSymbol>(sec);
out.linkingSec->addToSymtab(sym);
// rather than overwriting global data, but also increases code size since all
// static data loads and stores requires larger offsets.
void Writer::layoutMemory() {
- uint32_t memoryPtr = 0;
+ uint64_t memoryPtr = 0;
auto placeStack = [&]() {
if (config->relocatable || config->isPic)
log("mem: stack base = " + Twine(memoryPtr));
memoryPtr += config->zStackSize;
auto *sp = cast<DefinedGlobal>(WasmSym::stackPointer);
- sp->global->global.InitExpr.Value.Int32 = memoryPtr;
+ switch (sp->global->global.InitExpr.Opcode) {
+ case WASM_OPCODE_I32_CONST:
+ sp->global->global.InitExpr.Value.Int32 = memoryPtr;
+ break;
+ case WASM_OPCODE_I64_CONST:
+ sp->global->global.InitExpr.Value.Int64 = memoryPtr;
+ break;
+ default:
+ llvm_unreachable("init expr must be i32/i64.const");
+ }
log("mem: stack top = " + Twine(memoryPtr));
};
if (WasmSym::globalBase)
WasmSym::globalBase->setVirtualAddress(memoryPtr);
- uint32_t dataStart = memoryPtr;
+ uint64_t dataStart = memoryPtr;
// Arbitrarily set __dso_handle handle to point to the start of the data
// segments.
if (WasmSym::tlsSize && seg->name == ".tdata") {
auto *tlsSize = cast<DefinedGlobal>(WasmSym::tlsSize);
+ assert(tlsSize->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
tlsSize->global->global.InitExpr.Value.Int32 = seg->size;
auto *tlsAlign = cast<DefinedGlobal>(WasmSym::tlsAlign);
- tlsAlign->global->global.InitExpr.Value.Int32 = 1U << seg->alignment;
+ assert(tlsAlign->global->global.InitExpr.Opcode == WASM_OPCODE_I32_CONST);
+ tlsAlign->global->global.InitExpr.Value.Int32 = int64_t{1}
+ << seg->alignment;
}
}
if (WasmSym::heapBase)
WasmSym::heapBase->setVirtualAddress(memoryPtr);
+ uint64_t maxMemorySetting = 1ULL << (config->is64 ? 48 : 32);
+
if (config->initialMemory != 0) {
if (config->initialMemory != alignTo(config->initialMemory, WasmPageSize))
error("initial memory must be " + Twine(WasmPageSize) + "-byte aligned");
if (memoryPtr > config->initialMemory)
error("initial memory too small, " + Twine(memoryPtr) + " bytes needed");
- else
- memoryPtr = config->initialMemory;
+ if (config->initialMemory > maxMemorySetting)
+ error("initial memory too large, cannot be greater than " +
+ Twine(maxMemorySetting));
+ memoryPtr = config->initialMemory;
}
out.dylinkSec->memSize = memoryPtr;
out.memorySec->numMemoryPages =
error("maximum memory must be " + Twine(WasmPageSize) + "-byte aligned");
if (memoryPtr > config->maxMemory)
error("maximum memory too small, " + Twine(memoryPtr) + " bytes needed");
+ if (config->maxMemory > maxMemorySetting)
+ error("maximum memory too large, cannot be greater than " +
+ Twine(maxMemorySetting));
out.memorySec->maxMemoryPages = config->maxMemory / WasmPageSize;
log("mem: max pages = " + Twine(out.memorySec->maxMemoryPages));
}
addSection(out.functionSec);
addSection(out.tableSec);
addSection(out.memorySec);
- addSection(out.globalSec);
addSection(out.eventSec);
+ addSection(out.globalSec);
addSection(out.exportSec);
addSection(out.startSec);
addSection(out.elemSec);
for (auto &feature : file->getWasmObj()->getTargetFeatures()) {
switch (feature.Prefix) {
case WASM_FEATURE_PREFIX_USED:
- used.insert({feature.Name, fileName});
+ used.insert({feature.Name, std::string(fileName)});
break;
case WASM_FEATURE_PREFIX_REQUIRED:
- used.insert({feature.Name, fileName});
- required.insert({feature.Name, fileName});
+ used.insert({feature.Name, std::string(fileName)});
+ required.insert({feature.Name, std::string(fileName)});
break;
case WASM_FEATURE_PREFIX_DISALLOWED:
- disallowed.insert({feature.Name, fileName});
+ disallowed.insert({feature.Name, std::string(fileName)});
break;
default:
error("Unrecognized feature policy prefix " +
}
if (inferFeatures)
- allowed.insert(used.keys().begin(), used.keys().end());
+ for (const auto &key : used.keys())
+ allowed.insert(std::string(key));
- if (allowed.count("atomics") && !config->sharedMemory) {
+ if (!config->relocatable && allowed.count("atomics") &&
+ !config->sharedMemory) {
if (inferFeatures)
error(Twine("'atomics' feature is used by ") + used["atomics"] +
", so --shared-memory must be used");
if (!config->checkFeatures)
return;
- if (disallowed.count("atomics") && config->sharedMemory)
- error("'atomics' feature is disallowed by " + disallowed["atomics"] +
- ", so --shared-memory must not be used");
-
- if (!allowed.count("atomics") && config->sharedMemory)
- error("'atomics' feature must be used in order to use shared "
- "memory");
+ if (config->sharedMemory) {
+ if (disallowed.count("shared-mem"))
+ error("--shared-memory is disallowed by " + disallowed["shared-mem"] +
+ " because it was not compiled with 'atomics' or 'bulk-memory' "
+ "features.");
- if (!allowed.count("bulk-memory") && config->sharedMemory)
- error("'bulk-memory' feature must be used in order to use shared "
- "memory");
+ for (auto feature : {"atomics", "bulk-memory"})
+ if (!allowed.count(feature))
+ error(StringRef("'") + feature +
+ "' feature must be used in order to use shared memory");
+ }
- if (!allowed.count("bulk-memory") && tlsUsed)
- error("'bulk-memory' feature must be used in order to use thread-local "
- "storage");
+ if (tlsUsed) {
+ for (auto feature : {"atomics", "bulk-memory"})
+ if (!allowed.count(feature))
+ error(StringRef("'") + feature +
+ "' feature must be used in order to use thread-local storage");
+ }
// Validate that used features are allowed in output
if (!inferFeatures) {
for (auto &feature : used.keys()) {
- if (!allowed.count(feature))
+ if (!allowed.count(std::string(feature)))
error(Twine("Target feature '") + feature + "' used by " +
used[feature] + " is not allowed.");
}
". Use --no-check-features to suppress.");
}
for (auto &feature : required.keys()) {
- if (!objectFeatures.count(feature))
+ if (!objectFeatures.count(std::string(feature)))
error(Twine("Missing target feature '") + feature + "' in " + fileName +
", required by " + required[feature] +
". Use --no-check-features to suppress.");
StringRef name = sym->getName();
WasmExport export_;
if (auto *f = dyn_cast<DefinedFunction>(sym)) {
- StringRef exportName = f->function->getExportName();
- if (!exportName.empty()) {
- name = exportName;
+ if (Optional<StringRef> exportName = f->function->getExportName()) {
+ name = *exportName;
}
export_ = {name, WASM_EXTERNAL_FUNCTION, f->getFunctionIndex()};
} else if (auto *g = dyn_cast<DefinedGlobal>(sym)) {
cast<SyntheticFunction>(func->function)->setBody(body);
}
+bool Writer::needsPassiveInitialization(const OutputSegment *segment) {
+ return segment->initFlags & WASM_SEGMENT_IS_PASSIVE &&
+ segment->name != ".tdata" && !segment->isBss;
+}
+
+bool Writer::hasPassiveInitializedSegments() {
+ return std::find_if(segments.begin(), segments.end(),
+ [this](const OutputSegment *s) {
+ return this->needsPassiveInitialization(s);
+ }) != segments.end();
+}
+
void Writer::createInitMemoryFunction() {
LLVM_DEBUG(dbgs() << "createInitMemoryFunction\n");
assert(WasmSym::initMemoryFlag);
raw_string_ostream os(bodyContent);
writeUleb128(os, 0, "num locals");
- if (segments.size()) {
+ if (hasPassiveInitializedSegments()) {
// Initialize memory in a thread-safe manner. The thread that successfully
// increments the flag from 0 to 1 is is responsible for performing the
// memory initialization. Other threads go sleep on the flag until the
// Did increment 0, so conditionally initialize passive data segments
for (const OutputSegment *s : segments) {
- if (s->initFlags & WASM_SEGMENT_IS_PASSIVE && s->name != ".tdata") {
+ if (needsPassiveInitialization(s)) {
// destination address
writeI32Const(os, s->startVA, "destination address");
// source segment offset
// Unconditionally drop passive data segments
for (const OutputSegment *s : segments) {
- if (s->initFlags & WASM_SEGMENT_IS_PASSIVE && s->name != ".tdata") {
+ if (needsPassiveInitialization(s)) {
// data.drop instruction
writeU8(os, WASM_OPCODE_MISC_PREFIX, "bulk-memory prefix");
writeUleb128(os, WASM_OPCODE_DATA_DROP, "data.drop");
for (const WasmInitEntry &f : initFunctions) {
writeU8(os, WASM_OPCODE_CALL, "CALL");
writeUleb128(os, f.sym->getFunctionIndex(), "function index");
+ for (size_t i = 0; i < f.sym->signature->Returns.size(); i++) {
+ writeU8(os, WASM_OPCODE_DROP, "DROP");
+ }
}
writeU8(os, WASM_OPCODE_END, "END");
}
if (sym->isDiscarded())
continue;
assert(sym->isLive());
- if (*sym->signature != WasmSignature{{}, {}})
- error("invalid signature for init func: " + toString(*sym));
+ if (sym->signature->Params.size() != 0)
+ error("constructor functions cannot take arguments: " + toString(*sym));
LLVM_DEBUG(dbgs() << "initFunctions: " << toString(*sym) << "\n");
initFunctions.emplace_back(WasmInitEntry{sym, f.Priority});
}
out.functionSec = make<FunctionSection>();
out.tableSec = make<TableSection>();
out.memorySec = make<MemorySection>();
- out.globalSec = make<GlobalSection>();
out.eventSec = make<EventSection>();
+ out.globalSec = make<GlobalSection>();
out.exportSec = make<ExportSection>();
- out.startSec = make<StartSection>(segments.size());
+ out.startSec = make<StartSection>(hasPassiveInitializedSegments());
out.elemSec = make<ElemSection>();
out.dataCountSec = make<DataCountSection>(segments);
out.linkingSec = make<LinkingSection>(initFunctions, segments);
return "v128";
case ValType::EXNREF:
return "exnref";
+ case ValType::EXTERNREF:
+ return "externref";
}
llvm_unreachable("Invalid wasm::ValType");
}
s += "void";
else
s += toString(sig.Returns[0]);
- return s.str();
+ return std::string(s.str());
}
std::string toString(const WasmGlobalType &type) {
LLVM_DEBUG(dbgs() << format(" | %08lld: ", offset) << msg << "\n");
}
-void writeUleb128(raw_ostream &os, uint32_t number, const Twine &msg) {
+void writeUleb128(raw_ostream &os, uint64_t number, const Twine &msg) {
debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]");
encodeULEB128(number, os);
}
-void writeSleb128(raw_ostream &os, int32_t number, const Twine &msg) {
+void writeSleb128(raw_ostream &os, int64_t number, const Twine &msg) {
debugWrite(os.tell(), msg + "[" + utohexstr(number) + "]");
encodeSLEB128(number, os);
}
support::endian::write(os, number, support::little);
}
+void writeU64(raw_ostream &os, uint64_t number, const Twine &msg) {
+ debugWrite(os.tell(), msg + "[0x" + utohexstr(number) + "]");
+ support::endian::write(os, number, support::little);
+}
+
void writeValueType(raw_ostream &os, ValType type, const Twine &msg) {
writeU8(os, static_cast<uint8_t>(type),
msg + "[type: " + toString(type) + "]");
writeSleb128(os, number, msg);
}
-void writeI64Const(raw_ostream &os, int32_t number, const Twine &msg) {
+void writeI64Const(raw_ostream &os, int64_t number, const Twine &msg) {
writeU8(os, WASM_OPCODE_I64_CONST, "i64.const");
writeSleb128(os, number, msg);
}
-void writeMemArg(raw_ostream &os, uint32_t alignment, uint32_t offset) {
+void writeMemArg(raw_ostream &os, uint32_t alignment, uint64_t offset) {
writeUleb128(os, alignment, "alignment");
writeUleb128(os, offset, "offset");
}
case WASM_OPCODE_I64_CONST:
writeSleb128(os, initExpr.Value.Int64, "literal (i64)");
break;
+ case WASM_OPCODE_F32_CONST:
+ writeU32(os, initExpr.Value.Float32, "literal (f32)");
+ break;
+ case WASM_OPCODE_F64_CONST:
+ writeU64(os, initExpr.Value.Float64, "literal (f64)");
+ break;
case WASM_OPCODE_GLOBAL_GET:
writeUleb128(os, initExpr.Value.Global, "literal (global index)");
break;
+ case WASM_OPCODE_REF_NULL:
+ writeValueType(os, ValType::EXTERNREF, "literal (externref type)");
+ break;
default:
fatal("unknown opcode in init expr: " + Twine(initExpr.Opcode));
}
case WASM_EXTERNAL_GLOBAL:
writeUleb128(os, export_.Index, "global index");
break;
+ case WASM_EXTERNAL_EVENT:
+ writeUleb128(os, export_.Index, "event index");
+ break;
case WASM_EXTERNAL_MEMORY:
writeUleb128(os, export_.Index, "memory index");
break;
void debugWrite(uint64_t offset, const Twine &msg);
-void writeUleb128(raw_ostream &os, uint32_t number, const Twine &msg);
+void writeUleb128(raw_ostream &os, uint64_t number, const Twine &msg);
-void writeSleb128(raw_ostream &os, int32_t number, const Twine &msg);
+void writeSleb128(raw_ostream &os, int64_t number, const Twine &msg);
void writeBytes(raw_ostream &os, const char *bytes, size_t count,
const Twine &msg);
void writeI32Const(raw_ostream &os, int32_t number, const Twine &msg);
-void writeI64Const(raw_ostream &os, int32_t number, const Twine &msg);
+void writeI64Const(raw_ostream &os, int64_t number, const Twine &msg);
-void writeMemArg(raw_ostream &os, uint32_t alignment, uint32_t offset);
+void writeMemArg(raw_ostream &os, uint32_t alignment, uint64_t offset);
void writeInitExpr(raw_ostream &os, const llvm::wasm::WasmInitExpr &initExpr);