From f410558f5d60087e4c310119a1751b437121c3b9 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 18 Sep 2018 21:50:19 +0000 Subject: [PATCH] ld64-351.8.tar.gz --- doc/man/man1/ld.1 | 7 + ld64.xcodeproj/project.pbxproj | 8 +- src/create_configure | 2 +- src/ld/InputFiles.cpp | 74 ++++--- src/ld/InputFiles.h | 2 +- src/ld/Options.cpp | 204 ++++++++++++++---- src/ld/Options.h | 49 ++++- src/ld/OutputFile.cpp | 39 +++- src/ld/Resolver.cpp | 75 +++++-- src/ld/Resolver.h | 3 +- src/ld/SymbolTable.cpp | 35 +++ src/ld/SymbolTable.h | 3 +- src/ld/ld.cpp | 4 + src/ld/ld.hpp | 3 + src/ld/parsers/generic_dylib_file.hpp | 2 +- src/ld/parsers/lto_file.cpp | 3 +- src/ld/parsers/macho_relocatable_file.cpp | 59 +++-- src/ld/parsers/textstub_dylib_file.cpp | 167 +++++++++++--- src/ld/parsers/textstub_dylib_file.hpp | 3 + src/ld/passes/branch_shim.cpp | 2 +- src/other/machochecker.cpp | 2 +- .../Makefile | 54 +++++ .../bar.c | 1 + .../baz.c | 1 + .../foo.c | 8 + .../main.c | 8 + .../linker_options-framework-static/Makefile | 45 ++++ .../linker_options-framework-static/foo.c | 3 + .../linker_options-framework-static/main.c | 8 + .../test-cases/lto-duplicate_symbols/Makefile | 46 ++++ .../test-cases/lto-duplicate_symbols/main.c | 7 + .../lto-duplicate_symbols/non-static.c | 4 + .../test-cases/lto-duplicate_symbols/static.c | 9 + 33 files changed, 781 insertions(+), 159 deletions(-) create mode 100644 unit-tests/test-cases/linker_options-framework-static-chain/Makefile create mode 100644 unit-tests/test-cases/linker_options-framework-static-chain/bar.c create mode 100644 unit-tests/test-cases/linker_options-framework-static-chain/baz.c create mode 100644 unit-tests/test-cases/linker_options-framework-static-chain/foo.c create mode 100644 unit-tests/test-cases/linker_options-framework-static-chain/main.c create mode 100644 unit-tests/test-cases/linker_options-framework-static/Makefile create mode 100644 unit-tests/test-cases/linker_options-framework-static/foo.c create mode 100644 unit-tests/test-cases/linker_options-framework-static/main.c create mode 100644 unit-tests/test-cases/lto-duplicate_symbols/Makefile create mode 100644 unit-tests/test-cases/lto-duplicate_symbols/main.c create mode 100644 unit-tests/test-cases/lto-duplicate_symbols/non-static.c create mode 100644 unit-tests/test-cases/lto-duplicate_symbols/static.c diff --git a/doc/man/man1/ld.1 b/doc/man/man1/ld.1 index 30692dc..64641a6 100644 --- a/doc/man/man1/ld.1 +++ b/doc/man/man1/ld.1 @@ -548,6 +548,10 @@ projects that assume they are built and run on the same OS version. Don't run deduplication pass in linker .It Fl verbose_deduplicate Prints names of functions that are eliminated by deduplication and total code savings size. +.It Fl no_inits +Error if the output contains any static initializers +.It Fl no_warn_inits +Do not warn if the output contains any static initializers .It Fl dirty_data_list Ar filename Specifies a file containing the names of data symbols likely to be dirtied. If the linker is creating a __DATA_DIRTY segment, those symbols will be moved @@ -615,6 +619,9 @@ any function whose FDE cannot be expressed in the compact unwind format. .It Fl warn_weak_exports Issue a warning if the resulting final linked image contains weak external symbols. Such symbols require dyld to do extra work at launch time to coalesce those symbols. +.It Fl no_weak_exports +Issue an erro if the resulting final linked image contains weak external symbols. Such +symbols require dyld to do extra work at launch time to coalesce those symbols. .It Fl objc_gc_compaction Marks the Objective-C image info in the final linked image with the bit that says that the code was built to work the compacting garbage collection. diff --git a/ld64.xcodeproj/project.pbxproj b/ld64.xcodeproj/project.pbxproj index 7b7260e..3297210 100644 --- a/ld64.xcodeproj/project.pbxproj +++ b/ld64.xcodeproj/project.pbxproj @@ -841,7 +841,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n cd \"${TARGET_BUILD_DIR}\"\n cd ..\n mkdir -p lib\n cd lib\n ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib\"\n ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libtapi.dylib\"\nfi\n\n"; + shellScript = "if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n cd \"${TARGET_BUILD_DIR}\"\n cd ..\n mkdir -p lib\n cd lib\n ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libLTO.dylib\"\n ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libtapi.dylib\"\n ln -s -f \"${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib\"\nfi\n\n"; showEnvVarsInLog = 0; }; F96D5367094A2754008E9EE8 /* ShellScript */ = { @@ -1280,10 +1280,10 @@ "-stdlib=libc++", "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", - "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", "-L$(DT_TOOLCHAIN_DIR)/usr/lib", "-ltapi", + "@$(DERIVED_FILE_DIR)/linkExtras", ); PREBINDING = NO; PRODUCT_NAME = ld; @@ -1353,10 +1353,10 @@ "-stdlib=libc++", "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", - "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", "-L$(DT_TOOLCHAIN_DIR)/usr/lib", "-ltapi", + "@$(DERIVED_FILE_DIR)/linkExtras", ); PREBINDING = NO; PRODUCT_NAME = ld; @@ -1626,10 +1626,10 @@ "-stdlib=libc++", "-lxar", "-Wl,-lazy_library,$(DT_TOOLCHAIN_DIR)/usr/lib/libLTO.dylib", - "@$(DERIVED_FILE_DIR)/linkExtras", "-Wl,-exported_symbol,__mh_execute_header", "-L$(DT_TOOLCHAIN_DIR)/usr/lib", "-ltapi", + "@$(DERIVED_FILE_DIR)/linkExtras", ); PREBINDING = NO; PRODUCT_NAME = ld; diff --git a/src/create_configure b/src/create_configure index a740779..7ba470b 100755 --- a/src/create_configure +++ b/src/create_configure @@ -39,7 +39,7 @@ else fi if [ -f "${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" ]; then - echo "-Wl,-lazy_library,${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" > ${DERIVED_FILE_DIR}/linkExtras + echo "${DT_TOOLCHAIN_DIR}/usr/lib/libswiftDemangle.dylib" > ${DERIVED_FILE_DIR}/linkExtras echo "#define DEMANGLE_SWIFT 1" >> ${DERIVED_FILE_DIR}/configure.h else echo "" > ${DERIVED_FILE_DIR}/linkExtras diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index 1c69de6..2a79ae2 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -214,6 +214,15 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib) { + // handle inlined framework first. + if (info.isInlined) { + auto interface = _options.findTAPIFile(info.path); + auto file = textstub::dylib::parse(info.path, std::move(interface), info.modTime, info.ordinal, _options, indirectDylib); + assert(file && "could not locate the inlined file"); + if (!file) + throwf("could not parse inline dylib file: %s(%s)", interface->getInstallName().c_str(), info.path); + return file; + } // map in whole file struct stat stat_buf; int fd = ::open(info.path, O_RDONLY, 0); @@ -455,19 +464,19 @@ void InputFiles::logDylib(ld::File* file, bool indirect, bool speculative) if ( _options.dumpDependencyInfo() ) { const ld::dylib::File* dylib = dynamic_cast(file); if ( file == _bundleLoader ) { - _options.dumpDependency(Options::depBundleLoader, file->path()); + _options.addDependency(Options::depBundleLoader, file->path()); } else if ( (dylib != NULL ) && dylib->willBeUpwardDylib() ) { if ( indirect ) - _options.dumpDependency(Options::depUpwardIndirectDylib, file->path()); + _options.addDependency(Options::depUpwardIndirectDylib, file->path()); else - _options.dumpDependency(Options::depUpwardDirectDylib, file->path()); + _options.addDependency(Options::depUpwardDirectDylib, file->path()); } else { if ( indirect ) - _options.dumpDependency(Options::depIndirectDylib, file->path()); + _options.addDependency(Options::depIndirectDylib, file->path()); else - _options.dumpDependency(Options::depDirectDylib, file->path()); + _options.addDependency(Options::depDirectDylib, file->path()); } } } @@ -620,13 +629,14 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan for (const char* frameworkName : newFrameworks) { if ( state.linkerOptionFrameworks.count(frameworkName) ) continue; - Options::FileInfo info = _options.findFramework(frameworkName); - if ( ! this->frameworkAlreadyLoaded(info.path, frameworkName) ) { - _linkerOptionOrdinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); - info.ordinal = _linkerOptionOrdinal; - try { + try { + Options::FileInfo info = _options.findFramework(frameworkName); + if ( ! this->frameworkAlreadyLoaded(info.path, frameworkName) ) { + _linkerOptionOrdinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + info.ordinal = _linkerOptionOrdinal; ld::File* reader = this->makeFile(info, true); ld::dylib::File* dylibReader = dynamic_cast(reader); + ld::archive::File* archiveReader = dynamic_cast(reader); if ( dylibReader != NULL ) { if ( ! dylibReader->installPathVersionSpecific() ) { dylibReader->forEachAtom(handler); @@ -635,14 +645,22 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan this->addDylib(dylibReader, info); } } + else if ( archiveReader != NULL ) { + _searchLibraries.push_back(LibraryInfo(archiveReader)); + _options.addDependency(Options::depArchive, archiveReader->path()); + // -force_load_swift_libs + if (info.options.fForceLoad) { + archiveReader->forEachAtom(handler); + } + } else { - throwf("framework linker option at %s is not a dylib", info.path); + throwf("framework linker option at %s is not a dylib and not an archive", info.path); } } - catch (const char* msg) { - warning("Auto-Linking supplied '%s', %s", info.path, msg); - } } + catch (const char* msg) { + warning("Auto-Linking %s", msg); + } state.linkerOptionFrameworks.insert(frameworkName); } @@ -653,11 +671,11 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan for (const char* libName : newLibraries) { if ( state.linkerOptionLibraries.count(libName) ) continue; - Options::FileInfo info = _options.findLibrary(libName); - if ( ! this->libraryAlreadyLoaded(info.path) ) { - _linkerOptionOrdinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); - info.ordinal = _linkerOptionOrdinal; - try { + try { + Options::FileInfo info = _options.findLibrary(libName); + if ( ! this->libraryAlreadyLoaded(info.path) ) { + _linkerOptionOrdinal = _linkerOptionOrdinal.nextLinkerOptionOrdinal(); + info.ordinal = _linkerOptionOrdinal; // -force_load_swift_libs info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0); ld::File* reader = this->makeFile(info, true); @@ -671,8 +689,7 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan } else if ( archiveReader != NULL ) { _searchLibraries.push_back(LibraryInfo(archiveReader)); - if ( _options.dumpDependencyInfo() ) - _options.dumpDependency(Options::depArchive, archiveReader->path()); + _options.addDependency(Options::depArchive, archiveReader->path()); // -force_load_swift_libs if (info.options.fForceLoad) { archiveReader->forEachAtom(handler); @@ -682,10 +699,10 @@ void InputFiles::addLinkerOptionLibraries(ld::Internal& state, ld::File::AtomHan throwf("linker option dylib at %s is not a dylib", info.path); } } - catch (const char* msg) { - warning("Auto-Linking supplied '%s', %s", info.path, msg); - } } + catch (const char* msg) { + warning("Auto-Linking %s", msg); + } state.linkerOptionLibraries.insert(libName); } } @@ -736,8 +753,7 @@ void InputFiles::createOpaqueFileSections() // extra command line sections always at end for (Options::ExtraSection::const_iterator it=_options.extraSectionsBegin(); it != _options.extraSectionsEnd(); ++it) { _inputFiles.push_back(opaque_section::parse(it->segmentName, it->sectionName, it->path, it->data, it->dataLen)); - if ( _options.dumpDependencyInfo() ) - _options.dumpDependency(Options::depSection, it->path); + _options.addDependency(Options::depSection, it->path); } } @@ -1199,8 +1215,7 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal { ld::relocatable::File* reloc = (ld::relocatable::File*)file; _options.snapshot().recordObjectFile(reloc->path()); - if ( _options.dumpDependencyInfo() ) - _options.dumpDependency(Options::depObjectFile, reloc->path()); + _options.addDependency(Options::depObjectFile, reloc->path()); } break; case ld::File::Dylib: @@ -1220,8 +1235,7 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal state.forceLoadCompilerRT = true; _searchLibraries.push_back(LibraryInfo(archive)); - if ( _options.dumpDependencyInfo() ) - _options.dumpDependency(Options::depArchive, archive->path()); + _options.addDependency(Options::depArchive, archive->path()); } break; case ld::File::Other: diff --git a/src/ld/InputFiles.h b/src/ld/InputFiles.h index 21f878a..ba8ae54 100644 --- a/src/ld/InputFiles.h +++ b/src/ld/InputFiles.h @@ -110,7 +110,7 @@ private: static void parseWorkerThread(InputFiles *inputFiles); void startThread(void (*threadFunc)(InputFiles *)) const; - typedef std::unordered_map InstallNameToDylib; + typedef std::map InstallNameToDylib; const Options& _options; std::vector _inputFiles; diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index b739786..6f115d8 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -45,7 +45,6 @@ #include "MachOFileAbstraction.hpp" #include "Snapshot.h" - // from FunctionNameDemangle.h extern "C" size_t fnd_get_demangled_name(const char *mangledName, char *outputBuffer, size_t length); @@ -117,6 +116,10 @@ void throwf(const char* format, ...) bool Options::FileInfo::checkFileExists(const Options& options, const char *p) { + if (isInlined) { + modTime = 0; + return true; + } struct stat statBuffer; if (p == NULL) p = path; @@ -125,8 +128,7 @@ bool Options::FileInfo::checkFileExists(const Options& options, const char *p) modTime = statBuffer.st_mtime; return true; } - if ( options.dumpDependencyInfo() ) - options.dumpDependency(Options::depNotFound, p); + options.addDependency(Options::depNotFound, p); // fprintf(stderr, "not found: %s\n", p); return false; } @@ -160,7 +162,8 @@ Options::Options(int argc, const char* argv[]) fUsingLazyDylibLinking(false), fEncryptable(true), fEncryptableForceOn(false), fEncryptableForceOff(false), fOrderData(true), fMarkDeadStrippableDylib(false), fMakeCompressedDyldInfo(true), fMakeCompressedDyldInfoForceOff(false), fNoEHLabels(false), - fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false), fUseSimplifiedDylibReExports(false), + fAllowCpuSubtypeMismatches(false), fEnforceDylibSubtypesMatch(false), + fWarnOnSwiftABIVersionMismatches(false), fUseSimplifiedDylibReExports(false), fObjCABIVersion2Override(false), fObjCABIVersion1Override(false), fCanUseUpwardDylib(false), fFullyLoadArchives(false), fLoadAllObjcObjectsFromArchives(false), fFlatNamespace(false), fLinkingMainExecutable(false), fForFinalLinkedImage(false), fForStatic(false), @@ -169,7 +172,7 @@ Options::Options(int argc, const char* argv[]) fWarnCompactUnwind(false), fRemoveDwarfUnwindIfCompactExists(false), fAutoOrderInitializers(true), fOptimizeZeroFill(true), fMergeZeroFill(false), fLogObjectFiles(false), fLogAllFiles(false), fTraceDylibs(false), fTraceIndirectDylibs(false), fTraceArchives(false), fTraceEmitJSON(false), - fOutputSlidable(false), fWarnWeakExports(false), + fOutputSlidable(false), fWarnWeakExports(false), fNoWeakExports(false), fObjcGcCompaction(false), fObjCGc(false), fObjCGcOnly(false), fDemangle(false), fTLVSupport(false), fVersionLoadCommand(false), fVersionLoadCommandForcedOn(false), @@ -192,11 +195,12 @@ Options::Options(int argc, const char* argv[]) fBundleBitcode(false), fHideSymbols(false), fVerifyBitcode(false), fReverseMapUUIDRename(false), fDeDupe(true), fVerboseDeDupe(false), fReverseMapPath(NULL), fLTOCodegenOnly(false), - fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fNoInitializers(false), fBitcodeKind(kBitcodeProcess), + fIgnoreAutoLink(false), fAllowDeadDups(false), fAllowWeakImports(true), fInitializersTreatment(Options::kInvalid), + fZeroModTimeInDebugMap(false), fBitcodeKind(kBitcodeProcess), fPlatform(kPlatformUnknown), fDebugInfoStripping(kDebugInfoMinimal), fTraceOutputFile(NULL), fMacVersionMin(ld::macVersionUnset), fIOSVersionMin(ld::iOSVersionUnset), fWatchOSVersionMin(ld::wOSVersionUnset), fSaveTempFiles(false), fSnapshotRequested(false), fPipelineFifo(NULL), - fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0), + fDependencyInfoPath(NULL), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0), fUnalignedPointerTreatment(kUnalignedPointerIgnore) { this->checkForClassic(argc, argv); @@ -206,18 +210,13 @@ Options::Options(int argc, const char* argv[]) this->reconfigureDefaults(); this->checkIllegalOptionCombinations(); - if ( this->dumpDependencyInfo() ) { - this->dumpDependency(depOutputFile, fOutputFile); - if ( fMapPath != NULL ) - this->dumpDependency(depOutputFile, fMapPath); - } + this->addDependency(depOutputFile, fOutputFile); + if ( fMapPath != NULL ) + this->addDependency(depOutputFile, fMapPath); } Options::~Options() { - if ( fDependencyFileDescriptor != -1 ) - ::close(fDependencyFileDescriptor); - if ( fTraceFileDescriptor != -1 ) ::close(fTraceFileDescriptor); } @@ -827,6 +826,16 @@ static std::string replace_extension(const std::string &path, const std::string return result; } +void Options::addTAPIInterface(tapi::LinkerInterfaceFile *interface, const char *path) const { +#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1)) + if (tapi::APIVersion::isAtLeast(1, 3)) { + for (auto &name : interface->inlinedFrameworkNames()) { + fTAPIFiles.emplace_back(interface, path, name.c_str()); + } + } +#endif +} + bool Options::findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const { FileInfo tbdInfo; @@ -935,6 +944,14 @@ Options::FileInfo Options::findFile(const std::string &path, const ld::dylib::Fi } } + // find inlined TBD file before raw path. + // rdar://problem/35864452 + if (hasInlinedTAPIFile(path)) { + FileInfo inlinedFile(path.c_str()); + inlinedFile.isInlined = true; + return inlinedFile; + } + // try raw path if ( findFile(path, {".tbd"}, result) ) return result; @@ -943,6 +960,58 @@ Options::FileInfo Options::findFile(const std::string &path, const ld::dylib::Fi throwf("file not found: %s", path.c_str()); } +bool Options::hasInlinedTAPIFile(const std::string &path) const { + for (const auto &dylib : fTAPIFiles) { + if (dylib.getInstallName() == path) + return true; + } + return false; +} + +std::unique_ptr Options::findTAPIFile(const std::string &path) const +{ + std::unique_ptr interface; + std::string TBDPath; + + // create parsing options. + tapi::ParsingFlags flags = tapi::ParsingFlags::None; + if (enforceDylibSubtypesMatch()) + flags |= tapi::ParsingFlags::ExactCpuSubType; + + if (!allowWeakImports()) + flags |= tapi::ParsingFlags::DisallowWeakImports; + + // Search through all the inlined framework. + for (const auto &dylib : fTAPIFiles) { + if (dylib.getInstallName() == path) { + // If the install name matches, parse the framework. + std::string errorMessage; + auto file = dylib.getInterfaceFile()->getInlinedFramework(path.c_str(), architecture(), subArchitecture(), + flags, tapi::PackedVersion32(minOSversion()), errorMessage); + if (!file) + throw strdup(errorMessage.c_str()); + + if (!interface) { + // If this is the first inlined framework found, record the information. + interface.reset(file); + TBDPath = dylib.getTAPIFilePath(); + } else { + // If we found other inlined framework already, check to see if their versions are the same. + // If not the same, emit an warning and record the newer one. Otherwise, just use the current one. + if (interface->getCurrentVersion() == file->getCurrentVersion()) + continue; + warning("Inlined framework/dylib mismatch: %s (%s and %s)", path.c_str(), + TBDPath.c_str(), dylib.getTAPIFilePath().c_str()); + if (interface->getCurrentVersion() < file->getCurrentVersion()) { + interface.reset(file); + TBDPath = dylib.getTAPIFilePath(); + } + } + } + } + return interface; +} + // search for indirect dylib first using -F and -L paths first Options::FileInfo Options::findIndirectDylib(const std::string& installName, const ld::dylib::File* fromDylib) const { @@ -1067,16 +1136,14 @@ void Options::loadFileList(const char* fileOfPaths, ld::File::Ordinal baseOrdina file = fopen(realFileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", realFileOfPaths, errno, strerror(errno)); - if ( this->dumpDependencyInfo() ) - this->dumpDependency(Options::depFileList, realFileOfPaths); + this->addDependency(Options::depFileList, realFileOfPaths); } } else { file = fopen(fileOfPaths, "r"); if ( file == NULL ) throwf("-filelist file '%s' could not be opened, errno=%d (%s)\n", fileOfPaths, errno, strerror(errno)); - if ( this->dumpDependencyInfo() ) - this->dumpDependency(Options::depFileList, fileOfPaths); + this->addDependency(Options::depFileList, fileOfPaths); } char path[PATH_MAX]; @@ -1287,8 +1354,7 @@ void Options::loadExportFile(const char* fileOfExports, const char* option, SetW if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) throwf("can't read %s file: %s", option, fileOfExports); - if ( this->dumpDependencyInfo() ) - this->dumpDependency(Options::depMisc, fileOfExports); + this->addDependency(Options::depMisc, fileOfExports); ::close(fd); @@ -1361,8 +1427,7 @@ void Options::parseAliasFile(const char* fileOfAliases) throwf("can't read alias file: %s", fileOfAliases); p[stat_buf.st_size] = '\n'; ::close(fd); - if ( this->dumpDependencyInfo() ) - this->dumpDependency(Options::depMisc, fileOfAliases); + this->addDependency(Options::depMisc, fileOfAliases); // parse into symbols and add to fAliases AliasPair pair; @@ -1790,8 +1855,7 @@ void Options::parseOrderFile(const char* path, bool cstring) throwf("can't read order file: %s", path); ::close(fd); p[stat_buf.st_size] = '\n'; - if ( this->dumpDependencyInfo() ) - this->dumpDependency(Options::depMisc, path); + this->addDependency(Options::depMisc, path); // parse into vector of pairs char * const end = &p[stat_buf.st_size+1]; @@ -3492,6 +3556,9 @@ void Options::parse(int argc, const char* argv[]) else if ( strcmp(arg, "-warn_weak_exports") == 0 ) { fWarnWeakExports = true; } + else if ( strcmp(arg, "-no_weak_exports") == 0 ) { + fNoWeakExports = true; + } else if ( strcmp(arg, "-objc_gc_compaction") == 0 ) { fObjcGcCompaction = true; cannotBeUsedWithBitcode(arg); @@ -3865,7 +3932,10 @@ void Options::parse(int argc, const char* argv[]) fAllowWeakImports = false; } else if ( strcmp(argv[i], "-no_inits") == 0 ) { - fNoInitializers = true; + fInitializersTreatment = Options::kError; + } + else if ( strcmp(argv[i], "-no_warn_inits") == 0 ) { + fInitializersTreatment = Options::kSuppress; } // put this last so that it does not interfer with other options starting with 'i' else if ( strncmp(arg, "-i", 2) == 0 ) { @@ -4192,6 +4262,9 @@ void Options::parsePreCommandLineEnvironmentSettings() if (getenv("LD_DYLIB_CPU_SUBTYPES_MUST_MATCH") != NULL) fEnforceDylibSubtypesMatch = true; + + if (getenv("LD_WARN_ON_SWIFT_ABI_VERSION_MISMATCHES") != NULL) + fWarnOnSwiftABIVersionMismatches = true; sWarningsSideFilePath = getenv("LD_WARN_FILE"); @@ -4211,6 +4284,10 @@ void Options::parsePreCommandLineEnvironmentSettings() if (pipeFdString != NULL) { fPipelineFifo = pipeFdString; } + + // [Reproducible Builds] If env ZERO_AR_DATE is set, zero out timestamp in N_OSO stab + if ( getenv("ZERO_AR_DATE") != NULL ) + fZeroModTimeInDebugMap = true; } @@ -4650,6 +4727,9 @@ void Options::reconfigureDefaults() // only use v2 for Swift dylibs on Mac OS X if ( strncmp(this->installPath(), "/System/Library/PrivateFrameworks/Swift/", 40) == 0 ) fSharedRegionEncodingV2 = true; + // use v2 for ABI stable Swift dylibs on macOS + if ( strncmp(this->installPath(), "/System/Library/Frameworks/Swift/", 33) == 0 ) + fSharedRegionEncodingV2 = true; // an other OS frameworks that use swift need v2 for (const char* searchPath : fLibrarySearchPaths ) { if ( strstr(searchPath, "xctoolchain/usr/lib/swift/macos") != NULL ) { @@ -5246,6 +5326,18 @@ void Options::reconfigureDefaults() fUnalignedPointerTreatment = Options::kUnalignedPointerIgnore; } + // warn by default for OS dylibs + if ( fInitializersTreatment == Options::kInvalid ) { + if ( fSharedRegionEligible && (fOutputKind == Options::kDynamicLibrary) ) { + fInitializersTreatment = Options::kWarning; + // TEMP HACK + if ( (fOutputKind == Options::kDynamicLibrary) && (fDylibInstallName != NULL) && (strstr(fDylibInstallName, "EmbeddedAcousticRecognition.framework") != NULL) && !fNoWeakExports ) + fInitializersTreatment = Options::kSuppress; + } + else + fInitializersTreatment = Options::kSuppress; + } + } void Options::checkIllegalOptionCombinations() @@ -5911,7 +6003,7 @@ const char* Options::demangleSymbol(const char* sym) const static size_t size = 1024; static char* buff = (char*)malloc(size); - + #if DEMANGLE_SWIFT // only try to demangle symbols that look like Swift symbols if ( strncmp(sym, "__T", 3) == 0 ) { @@ -5941,26 +6033,50 @@ const char* Options::demangleSymbol(const char* sym) const } -void Options::dumpDependency(uint8_t opcode, const char* path) const +void Options::writeDependencyInfo() const { - if ( !this->dumpDependencyInfo() ) + // do nothing if -dependency_info not used + if ( !dumpDependencyInfo() ) return; + // sort entries for build reproducibility + std::sort(fDependencies.begin(), fDependencies.end(), [](const DependencyEntry& a, const DependencyEntry& b) -> bool { + if ( a.opcode != b.opcode ) + return (a.opcode < b.opcode); + return (a.path < b.path); + }); + // one time open() of -dependency_info file - if ( fDependencyFileDescriptor == -1 ) { - fDependencyFileDescriptor = open(this->dependencyInfoPath(), O_WRONLY | O_TRUNC | O_CREAT, 0666); - if ( fDependencyFileDescriptor == -1 ) - throwf("Could not open or create -dependency_info file: %s", this->dependencyInfoPath()); - - // write header - uint8_t version = depLinkerVersion; - if ( write(fDependencyFileDescriptor, &version, 1) == -1 ) + int fd = open(this->dependencyInfoPath(), O_WRONLY | O_TRUNC | O_CREAT, 0666); + if ( fd == -1 ) + throwf("Could not open or create -dependency_info file: %s", this->dependencyInfoPath()); + + // write header + uint8_t version = depLinkerVersion; + if ( write(fd, &version, 1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + extern const char ldVersionString[]; + if ( write(fd, ldVersionString, strlen(ldVersionString)+1) == -1 ) + throwf("write() to -dependency_info failed, errno=%d", errno); + + // write each dependency + for (const auto& entry: fDependencies) { + //printf("%d %s\n", entry.opcode, entry.path.c_str()); + if ( write(fd, &entry.opcode, 1) == -1 ) throwf("write() to -dependency_info failed, errno=%d", errno); - extern const char ldVersionString[]; - if ( write(fDependencyFileDescriptor, ldVersionString, strlen(ldVersionString)+1) == -1 ) + if ( write(fd, entry.path.c_str(), entry.path.size()+1) == -1 ) throwf("write() to -dependency_info failed, errno=%d", errno); } + ::close(fd); +} + + +void Options::addDependency(uint8_t opcode, const char* path) const +{ + if ( !this->dumpDependencyInfo() ) + return; + char realPath[PATH_MAX]; if ( path[0] != '/' ) { if ( realpath(path, realPath) != NULL ) { @@ -5968,12 +6084,10 @@ void Options::dumpDependency(uint8_t opcode, const char* path) const } } - if ( write(fDependencyFileDescriptor, &opcode, 1) == -1 ) - throwf("write() to -dependency_info failed, errno=%d", errno); - if ( write(fDependencyFileDescriptor, path, strlen(path)+1) == -1 ) - throwf("write() to -dependency_info failed, errno=%d", errno); - - //fprintf(stderr, "0x%02X %s\n", opcode, path); + DependencyEntry entry; + entry.opcode = opcode; + entry.path = path; + fDependencies.push_back(entry); } diff --git a/src/ld/Options.h b/src/ld/Options.h index 4ff780c..3b0a37c 100644 --- a/src/ld/Options.h +++ b/src/ld/Options.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -37,6 +38,7 @@ #include "Snapshot.h" #include "MachOFileAbstraction.hpp" + extern void throwf (const char* format, ...) __attribute__ ((noreturn,format(printf, 1, 2))); extern void warning(const char* format, ...) __attribute__((format(printf, 1, 2))); @@ -163,6 +165,7 @@ public: LibraryOptions options; ld::File::Ordinal ordinal; bool fromFileList; + bool isInlined; // These are used by the threaded input file parsing engine. mutable int inputFileSlot; // The input file "slot" assigned to this particular file @@ -171,7 +174,7 @@ public: // The use pattern for FileInfo is to create one on the stack in a leaf function and return // it to the calling frame by copy. Therefore the copy constructor steals the path string from // the source, which dies with the stack frame. - FileInfo(FileInfo const &other) : path(other.path), modTime(other.modTime), options(other.options), ordinal(other.ordinal), fromFileList(other.fromFileList), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; }; + FileInfo(FileInfo const &other) : path(other.path), modTime(other.modTime), options(other.options), ordinal(other.ordinal), fromFileList(other.fromFileList), isInlined(other.isInlined), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; }; FileInfo &operator=(FileInfo other) { std::swap(path, other.path); @@ -179,16 +182,17 @@ public: std::swap(options, other.options); std::swap(ordinal, other.ordinal); std::swap(fromFileList, other.fromFileList); + std::swap(isInlined, other.isInlined); std::swap(inputFileSlot, other.inputFileSlot); std::swap(readyToParse, other.readyToParse); return *this; } // Create an empty FileInfo. The path can be set implicitly by checkFileExists(). - FileInfo() : path(NULL), modTime(-1), options(), fromFileList(false) {}; + FileInfo() : path(NULL), modTime(-1), options(), fromFileList(false), isInlined(false) {}; // Create a FileInfo for a specific path, but does not stat the file. - FileInfo(const char *_path) : path(strdup(_path)), modTime(-1), options(), fromFileList(false) {}; + FileInfo(const char *_path) : path(strdup(_path)), modTime(-1), options(), fromFileList(false), isInlined(false) {}; ~FileInfo() { if (path) ::free((void*)path); } @@ -203,6 +207,19 @@ public: bool missing() const { return modTime == -1; } }; + class TAPIInterface { + public: + TAPIInterface(tapi::LinkerInterfaceFile *file, const char* path, const char *installName) : + _file(file), _tbdPath(path), _installName(installName) {} + tapi::LinkerInterfaceFile *getInterfaceFile() const { return _file; } + std::string getTAPIFilePath() const { return _tbdPath; } + const std::string &getInstallName() const { return _installName; } + private: + tapi::LinkerInterfaceFile *_file; + std::string _tbdPath; + std::string _installName; + }; + struct ExtraSection { const char* segmentName; const char* sectionName; @@ -273,7 +290,7 @@ public: depFileList=0x10, depSection=0x10, depBundleLoader=0x10, depMisc=0x10, depNotFound=0x11, depOutputFile = 0x40 }; - void dumpDependency(uint8_t, const char* path) const; + void addDependency(uint8_t, const char* path) const; typedef const char* const* UndefinesIterator; @@ -286,6 +303,7 @@ public: cpu_subtype_t subArchitecture() const { return fSubArchitecture; } bool allowSubArchitectureMismatches() const { return fAllowCpuSubtypeMismatches; } bool enforceDylibSubtypesMatch() const { return fEnforceDylibSubtypesMatch; } + bool warnOnSwiftABIVersionMismatches() const { return fWarnOnSwiftABIVersionMismatches; } bool forceCpuSubtypeAll() const { return fForceSubtypeAll; } const char* architectureName() const { return fArchitectureName; } void setArchitecture(cpu_type_t, cpu_subtype_t subtype, Options::Platform platform); @@ -353,6 +371,8 @@ public: bool keepRelocations(); FileInfo findFile(const std::string &path, const ld::dylib::File* fromDylib=nullptr) const; bool findFile(const std::string &path, const std::vector &tbdExtensions, FileInfo& result) const; + bool hasInlinedTAPIFile(const std::string &path) const; + std::unique_ptr findTAPIFile(const std::string &path) const; UUIDMode UUIDMode() const { return fUUIDMode; } bool warnStabs(); bool pauseAtEnd() { return fPause; } @@ -429,6 +449,7 @@ public: bool makeTentativeDefinitionsReal() const { return fMakeTentativeDefinitionsReal; } const char* dyldInstallPath() const { return fDyldInstallPath; } bool warnWeakExports() const { return fWarnWeakExports; } + bool noWeakExports() const { return fNoWeakExports; } bool objcGcCompaction() const { return fObjcGcCompaction; } bool objcGc() const { return fObjCGc; } bool objcGcOnly() const { return fObjCGcOnly; } @@ -468,7 +489,7 @@ public: bool ignoreAutoLink() const { return fIgnoreAutoLink; } bool allowDeadDuplicates() const { return fAllowDeadDups; } bool allowWeakImports() const { return fAllowWeakImports; } - bool noInitializers() const { return fNoInitializers; } + Treatment initializersTreatment() const { return fInitializersTreatment; } BitcodeMode bitcodeKind() const { return fBitcodeKind; } bool sharedRegionEncodingV2() const { return fSharedRegionEncodingV2; } bool useDataConstSegment() const { return fUseDataConstSegment; } @@ -513,6 +534,10 @@ public: bool hasCodeSymbolMoves() const { return !fSymbolsMovesCode.empty(); } void writeToTraceFile(const char* buffer, size_t len) const; UnalignedPointerTreatment unalignedPointerTreatment() const { return fUnalignedPointerTreatment; } + bool zeroModTimeInDebugMap() const { return fZeroModTimeInDebugMap; } + void writeDependencyInfo() const; + std::vector &TAPIFiles() { return fTAPIFiles; } + void addTAPIInterface(tapi::LinkerInterfaceFile *interface, const char *path) const; static uint32_t parseVersionNumber32(const char*); @@ -549,6 +574,11 @@ private: SetWithWildcards symbols; }; + struct DependencyEntry { + uint8_t opcode; + std::string path; + }; + void parse(int argc, const char* argv[]); void checkIllegalOptionCombinations(); void buildSearchPaths(int argc, const char* argv[]); @@ -701,6 +731,7 @@ private: bool fNoEHLabels; bool fAllowCpuSubtypeMismatches; bool fEnforceDylibSubtypesMatch; + bool fWarnOnSwiftABIVersionMismatches; bool fUseSimplifiedDylibReExports; bool fObjCABIVersion2Override; bool fObjCABIVersion1Override; @@ -731,6 +762,7 @@ private: bool fTraceEmitJSON; bool fOutputSlidable; bool fWarnWeakExports; + bool fNoWeakExports; bool fObjcGcCompaction; bool fObjCGc; bool fObjCGcOnly; @@ -788,7 +820,8 @@ private: bool fIgnoreAutoLink; bool fAllowDeadDups; bool fAllowWeakImports; - bool fNoInitializers; + Treatment fInitializersTreatment; + bool fZeroModTimeInDebugMap; BitcodeMode fBitcodeKind; Platform fPlatform; DebugInfoStripping fDebugInfoStripping; @@ -825,10 +858,12 @@ private: bool fSnapshotRequested; const char* fPipelineFifo; const char* fDependencyInfoPath; - mutable int fDependencyFileDescriptor; mutable int fTraceFileDescriptor; uint8_t fMaxDefaultCommonAlign; UnalignedPointerTreatment fUnalignedPointerTreatment; + mutable std::vector fDependencies; + mutable std::vector fTAPIFiles; + }; diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp index 62e052e..e5059d5 100644 --- a/src/ld/OutputFile.cpp +++ b/src/ld/OutputFile.cpp @@ -5311,16 +5311,23 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(), DebugNoteSorter()); // Add -add_ast_path option to linker which add N_AST stab entry to output + std::set seenAstPaths; const std::vector& astPaths = _options.astFilePaths(); for (std::vector::const_iterator it=astPaths.begin(); it != astPaths.end(); it++) { const char* path = *it; + if ( seenAstPaths.count(path) != 0 ) + continue; + seenAstPaths.insert(path); // emit N_AST ld::relocatable::File::Stab astStab; astStab.atom = NULL; astStab.type = N_AST; astStab.other = 0; astStab.desc = 0; - astStab.value = fileModTime(path); + if ( _options.zeroModTimeInDebugMap() ) + astStab.value = 0; + else + astStab.value = fileModTime(path); astStab.string = path; state.stabs.push_back(astStab); } @@ -5387,11 +5394,17 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) objStab.desc = 1; if ( atomObjFile != NULL ) { objStab.string = assureFullPath(atomObjFile->debugInfoPath()); - objStab.value = atomObjFile->debugInfoModificationTime(); + if ( _options.zeroModTimeInDebugMap() ) + objStab.value = 0; + else + objStab.value = atomObjFile->debugInfoModificationTime(); } else { objStab.string = assureFullPath(atomFile->path()); - objStab.value = atomFile->modificationTime(); + if ( _options.zeroModTimeInDebugMap() ) + objStab.value = 0; + else + objStab.value = atomFile->modificationTime(); } state.stabs.push_back(objStab); wroteStartSO = true; @@ -5401,6 +5414,25 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) asprintf(&fullFilePath, "%s%s", newDirPath, newFilename); // add both leaf path and full path seenFiles.insert(fullFilePath); + + // Add linker support for propagating N_AST debug notes from .o files to linked image + if ( const std::vector* asts = atomObjFile->astFiles() ) { + for (const relocatable::File::AstTimeAndPath& file : *asts) { + const char* cpath = file.path.c_str(); + if ( seenAstPaths.count(cpath) != 0 ) + continue; + seenAstPaths.insert(cpath); + // generate N_AST in output + ld::relocatable::File::Stab astStab; + astStab.atom = NULL; + astStab.type = N_AST; + astStab.other = 0; + astStab.desc = 0; + astStab.value = file.time; + astStab.string = cpath; + state.stabs.push_back(astStab); + } + } } filename = newFilename; dirPath = newDirPath; @@ -5534,6 +5566,7 @@ void OutputFile::synthesizeDebugNotes(ld::Internal& state) } } } + } diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index 831fa7f..51afe8d 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -462,12 +462,22 @@ void Resolver::doFile(const ld::File& file) Options::userReadableSwiftVersion(file.swiftVersion(), fileVersion); Options::userReadableSwiftVersion(_internal.swiftVersion, otherVersion); if ( file.swiftVersion() > _internal.swiftVersion ) { - throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", - file.path(), fileVersion, otherVersion); + if ( _options.warnOnSwiftABIVersionMismatches() ) { + warning("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } else { + throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } } else { - throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", - file.path(), fileVersion, otherVersion); + if ( _options.warnOnSwiftABIVersionMismatches() ) { + warning("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } else { + throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } } } } @@ -479,7 +489,11 @@ void Resolver::doFile(const ld::File& file) // remember if any .o file did not have MH_SUBSECTIONS_VIA_SYMBOLS bit set if ( ! objFile->canScatterAtoms() ) _internal.allObjectFilesScatterable = false; - + + // remember if building for profiling (so we don't warn about initializers) + if ( objFile->hasllvmProfiling() ) + _havellvmProfiling = true; + // update minOSVersion off all .o files uint32_t objMinOS = objFile->minOSVersion(); if ( !objMinOS ) @@ -652,12 +666,22 @@ void Resolver::doFile(const ld::File& file) Options::userReadableSwiftVersion(file.swiftVersion(), fileVersion); Options::userReadableSwiftVersion(_internal.swiftVersion, otherVersion); if ( file.swiftVersion() > _internal.swiftVersion ) { - throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", - file.path(), fileVersion, otherVersion); + if ( _options.warnOnSwiftABIVersionMismatches() ) { + warning("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } else { + throwf("%s compiled with newer version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } } else { - throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", - file.path(), fileVersion, otherVersion); + if ( _options.warnOnSwiftABIVersionMismatches() ) { + warning("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } else { + throwf("%s compiled with older version of Swift language (%s) than previous files (%s)", + file.path(), fileVersion, otherVersion); + } } } } @@ -794,9 +818,17 @@ void Resolver::doAtom(const ld::Atom& atom) if ( atom.section().type() == ld::Section::typeTempAlias ) _haveAliases = true; - // prevent initializers if -no_inits used - if ( (atom.section().type() == ld::Section::typeInitializerPointers) && _options.noInitializers() ) { - throw "-no_inits specified, but initializer found"; + // error or warn about initializers + if ( (atom.section().type() == ld::Section::typeInitializerPointers) && !_havellvmProfiling ) { + switch ( _options.initializersTreatment() ) { + case Options::kError: + throwf("static initializer found in '%s'",atom.safeFilePath()); + case Options::kWarning: + warning("static initializer found in '%s'. Use -no_inits to make this an error. Use -no_warn_inits to suppress warning",atom.safeFilePath()); + break; + default: + break; + } } if ( _options.deadCodeStrip() ) { @@ -1832,7 +1864,7 @@ void Resolver::linkTimeOptimize() // and re-compute dead code this->deadStripOptimize(true); } - + // if -exported_symbols_list on command line, re-force scope if ( _options.hasExportMaskList() ) { for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { @@ -1856,6 +1888,23 @@ void Resolver::linkTimeOptimize() // check new code does not override some dylib this->checkDylibSymbolCollisions(); + + // remove undefs from LTO objects that gets optimized away + std::unordered_set mustPreserve; + if ( _internal.classicBindingHelper != NULL ) + mustPreserve.insert(_internal.classicBindingHelper); + if ( _internal.compressedFastBinderProxy != NULL ) + mustPreserve.insert(_internal.compressedFastBinderProxy); + if ( _internal.lazyBindingHelper != NULL ) + mustPreserve.insert(_internal.lazyBindingHelper); + if ( const ld::Atom* entry = this->entryPoint(true) ) + mustPreserve.insert(entry); + for (Options::UndefinesIterator uit=_options.initialUndefinesBegin(); uit != _options.initialUndefinesEnd(); ++uit) { + SymbolTable::IndirectBindingSlot slot = _symbolTable.findSlotForName(*uit); + if ( _internal.indirectBindingTable[slot] != NULL ) + mustPreserve.insert(_internal.indirectBindingTable[slot]); + } + _symbolTable.removeDeadUndefs(_atoms, mustPreserve); } } diff --git a/src/ld/Resolver.h b/src/ld/Resolver.h index 06b7a8b..811edef 100644 --- a/src/ld/Resolver.h +++ b/src/ld/Resolver.h @@ -64,7 +64,7 @@ public: _haveLLVMObjs(false), _completedInitialObjectFiles(false), _ltoCodeGenFinished(false), - _haveAliases(false) {} + _haveAliases(false), _havellvmProfiling(false) {} virtual void doAtom(const ld::Atom&); @@ -135,6 +135,7 @@ private: bool _completedInitialObjectFiles; bool _ltoCodeGenFinished; bool _haveAliases; + bool _havellvmProfiling; }; diff --git a/src/ld/SymbolTable.cpp b/src/ld/SymbolTable.cpp index 3c12a77..e12c1e4 100644 --- a/src/ld/SymbolTable.cpp +++ b/src/ld/SymbolTable.cpp @@ -860,6 +860,41 @@ const ld::Atom* SymbolTable::indirectAtom(IndirectBindingSlot slot) const return _indirectBindingTable[slot]; } + +void SymbolTable::removeDeadUndefs(std::vector& allAtoms, const std::unordered_set& keep) +{ + // mark the indirect entries in use + std::vector indirectUsed; + for (size_t i=0; i < _indirectBindingTable.size(); ++i) + indirectUsed.push_back(false); + for (const ld::Atom* atom : allAtoms) { + for (auto it = atom->fixupsBegin(); it != atom->fixupsEnd(); ++it) { + switch (it->binding) { + case ld::Fixup::bindingsIndirectlyBound: + indirectUsed[it->u.bindingIndex] = true; + break; + default: + break; + } + } + } + + // any indirect entry not in use which points to an undefined proxy can be removed + for (size_t slot=0; slot < indirectUsed.size(); ++slot) { + if ( !indirectUsed[slot] ) { + const ld::Atom* atom = _indirectBindingTable[slot]; + if ( (atom != nullptr) && (atom->definition() == ld::Atom::definitionProxy) && (keep.count(atom) == 0) ) { + const char* name = atom->name(); + _indirectBindingTable[slot] = NULL; + _byNameReverseTable.erase(slot); + _byNameTable.erase(name); + allAtoms.erase(std::remove(allAtoms.begin(), allAtoms.end(), atom), allAtoms.end()); + } + } + } + +} + void SymbolTable::printStatistics() { // fprintf(stderr, "cstring table size: %lu, bucket count: %lu, hash func called %u times\n", diff --git a/src/ld/SymbolTable.h b/src/ld/SymbolTable.h index 2708f1f..b483304 100644 --- a/src/ld/SymbolTable.h +++ b/src/ld/SymbolTable.h @@ -127,7 +127,8 @@ public: byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); } byNameIterator end() { return byNameIterator(_byNameTable.end(),_indirectBindingTable); } void printStatistics(); - + void removeDeadUndefs(std::vector& allAtoms, const std::unordered_set& keep); + // from ld::IndirectBindingTable virtual const char* indirectName(IndirectBindingSlot slot) const; virtual const ld::Atom* indirectAtom(IndirectBindingSlot slot) const; diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index a8d2276..e0830ab 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -945,6 +945,8 @@ void InternalState::setSectionSizesAndAlignments() this->hasWeakExternalSymbols = true; if ( _options.warnWeakExports() ) warning("weak external symbol: %s", atom->name()); + else if ( _options.noWeakExports() ) + throwf("weak external symbol: %s", atom->name()); } } sect->size = offset; @@ -1351,6 +1353,8 @@ int main(int argc, const char* argv[]) // sort final sections state.sortSections(); + options.writeDependencyInfo(); + // write output file statistics.startOutput = mach_absolute_time(); ld::tool::OutputFile out(options); diff --git a/src/ld/ld.hpp b/src/ld/ld.hpp index 5e9164f..71e5a21 100644 --- a/src/ld/ld.hpp +++ b/src/ld/ld.hpp @@ -214,6 +214,7 @@ namespace relocatable { }; typedef const std::vector< std::vector > LinkerOptionsList; typedef std::vector> ToolVersionList; + struct AstTimeAndPath { uint64_t time; std::string path; }; File(const char* pth, time_t modTime, Ordinal ord) : ld::File(pth, modTime, ord, Reloc) { } @@ -224,10 +225,12 @@ namespace relocatable { virtual const std::vector* stabs() const = 0; virtual bool canScatterAtoms() const = 0; virtual bool hasLongBranchStubs() { return false; } + virtual bool hasllvmProfiling() const { return false; } virtual LinkerOptionsList* linkerOptions() const = 0; virtual const ToolVersionList& toolVersions() const = 0; virtual SourceKind sourceKind() const { return kSourceUnknown; } virtual const uint8_t* fileContent() const { return nullptr; } + virtual const std::vector* astFiles() const { return nullptr; } }; } // namespace relocatable diff --git a/src/ld/parsers/generic_dylib_file.hpp b/src/ld/parsers/generic_dylib_file.hpp index eb9d0ea..0b781be 100644 --- a/src/ld/parsers/generic_dylib_file.hpp +++ b/src/ld/parsers/generic_dylib_file.hpp @@ -138,7 +138,7 @@ public: // overrides of ld::dylib::File - virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override final; + virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override; virtual bool providedExportAtom() const override final { return _providedAtom; } virtual const char* parentUmbrella() const override final { return _parentUmbrella; } virtual const std::vector* allowableClients() const override final { return _allowableClients.empty() ? nullptr : &_allowableClients; } diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index 586516c..811b6b3 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -1459,7 +1459,7 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) // update proxy atoms to point to real atoms and find new atoms const char* name = machoAtom.name(); CStringToAtom::const_iterator pos = _llvmAtoms.find(name); - if ( pos != _llvmAtoms.end() ) { + if ( (pos != _llvmAtoms.end()) && (machoAtom.scope() != ld::Atom::scopeTranslationUnit) ) { // turn Atom into a proxy for this mach-o atom if (pos->second->scope() == ld::Atom::scopeLinkageUnit) { if (log) fprintf(stderr, "demote %s to hidden after LTO\n", name); @@ -1535,6 +1535,7 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) // If mach-o atom is referencing another mach-o atom then // reference is not going through Atom proxy. Fix it here to ensure that all // llvm symbol references always go through Atom proxy. + if ( fit->u.target->scope() != ld::Atom::scopeTranslationUnit ) { const char* targetName = fit->u.target->name(); CStringToAtom::const_iterator post = _llvmAtoms.find(targetName); diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index 47c2ac0..ae3be5f 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -86,6 +86,7 @@ public: _minOSVersion(0), _platform(Options::kPlatformUnknown), _canScatterAtoms(false), + _hasllvmProfiling(false), _objcHasCategoryClassPropertiesField(false), _srcKind(kSourceUnknown) { } virtual ~File(); @@ -105,6 +106,7 @@ public: virtual DebugInfoKind debugInfo() const { return _debugInfoKind; } virtual const std::vector* stabs() const { return &_stabs; } virtual bool canScatterAtoms() const { return _canScatterAtoms; } + virtual bool hasllvmProfiling() const { return _hasllvmProfiling; } virtual const char* translationUnitSource() const; virtual LinkerOptionsList* linkerOptions() const { return &_linkerOptions; } virtual const ToolVersionList& toolVersions() const { return _toolVersions; } @@ -113,12 +115,15 @@ public: virtual SourceKind sourceKind() const { return _srcKind; } virtual const uint8_t* fileContent() const { return _fileContent; } + virtual const std::vector* astFiles() const { return &_astFiles; } + + void setHasllvmProfiling() { _hasllvmProfiling = true; } private: friend class Atom; friend class Section; friend class Parser; friend class CFISection::OAS; - + typedef typename A::P P; const uint8_t* _fileContent; @@ -132,6 +137,7 @@ private: std::vector _unwindInfos; std::vector _lineInfos; std::vector_stabs; + std::vector _astFiles; ld::relocatable::File::DebugInfoKind _debugInfoKind; const char* _dwarfTranslationUnitPath; const macho_section

* _dwarfDebugInfoSect; @@ -144,6 +150,7 @@ private: uint32_t _minOSVersion; Options::Platform _platform; bool _canScatterAtoms; + bool _hasllvmProfiling; bool _objcHasCategoryClassPropertiesField; std::vector > _linkerOptions; std::unique_ptr _bitcode; @@ -1214,6 +1221,7 @@ private: void parseDebugInfo(); void parseStabs(); + void addAstFiles(); void appendAliasAtoms(uint8_t* atomBuffer); static bool isConstFunStabs(const char *stabStr); bool read_comp_unit(const char ** name, const char ** comp_dir, @@ -1554,7 +1562,6 @@ template bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, const Section& sect, uint32_t sectNum, pint_t startAddr, pint_t endAddr, pint_t* addr, pint_t* size, const macho_nlist

** symbol) { - bool cfiApplicable = (sect.machoSection()->flags() & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS)); // may not be a label on start of section, but need atom demarcation there if ( newSection ) { newSection = false; @@ -1614,7 +1621,7 @@ bool Parser::LabelAndCFIBreakIterator::next(Parser& parser, const Section< return true; } // no symbols in section, check CFI - if ( cfiApplicable && (cfiIndex < cfiStartsCount) ) { + if ( cfiIndex < cfiStartsCount ) { pint_t nextCfiAddr = cfiStartsArray[cfiIndex]; if ( nextCfiAddr < endAddr ) { // use cfi @@ -2185,18 +2192,13 @@ bool Parser::parseLoadCommands(Options::Platform platform, uint32_t linkMinOS } - // validate just one segment + // record range of sections if ( segment == NULL ) throw "missing LC_SEGMENT"; - if ( segment->filesize() > _fileLength ) - throw "LC_SEGMENT filesize too large"; - - // record and validate sections _sectionsStart = (macho_section

*)((char*)segment + sizeof(macho_segment_command

)); _machOSectionsCount = segment->nsects(); if ( (sizeof(macho_segment_command

) + _machOSectionsCount * sizeof(macho_section

)) > segment->cmdsize() ) throw "too many sections for size of LC_SEGMENT command"; - return true; } @@ -3630,6 +3632,8 @@ bool Parser::isConstFunStabs(const char *stabStr) template void Parser::parseDebugInfo() { + addAstFiles(); + // check for dwarf __debug_info section if ( _file->_dwarfDebugInfoSect == NULL ) { // if no DWARF debug info, look for stabs @@ -4000,6 +4004,22 @@ void Parser::parseStabs() } +template +void Parser::addAstFiles() +{ + // scan symbol table for N_AST entries + for (uint32_t symbolIndex = 0; symbolIndex < _symbolCount; ++symbolIndex ) { + const macho_nlist

& sym = this->symbolFromIndex(symbolIndex); + if ( (sym.n_type() == N_AST) && (sym.n_strx() != 0) ) { + const char* symString = this->nameFromSymbol(sym); + ld::relocatable::File::AstTimeAndPath entry; + entry.time = sym.n_value(); + entry.path = symString; + _file->_astFiles.push_back(entry); + } + } +} + // Look at the compilation unit DIE and determine // its NAME, compilation directory (in COMP_DIR) and its @@ -4445,7 +4465,7 @@ bool CFISection::needsRelocating() template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + libunwind::CFI_Atom_Info::OAS> cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { const uint32_t sectionSize = this->_machOSection->size(); @@ -4510,7 +4530,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + libunwind::CFI_Atom_Info::OAS> cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // create ObjectAddressSpace object for use by libunwind @@ -4531,7 +4551,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + libunwind::CFI_Atom_Info::OAS> cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { if ( !parser.armUsesZeroCostExceptions() ) { @@ -4557,7 +4577,7 @@ void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, template <> void CFISection::cfiParse(class Parser& parser, uint8_t* buffer, - libunwind::CFI_Atom_Info::OAS>::CFI_Atom_Info cfiArray[], + libunwind::CFI_Atom_Info::OAS> cfiArray[], uint32_t& count, const pint_t cuStarts[], uint32_t cuCount) { // copy __eh_frame data to buffer @@ -5268,6 +5288,9 @@ SymboledSection::SymboledSection(Parser& parser, File& f, const macho_s _type = ld::Atom::typeLSDA; else if ( this->type() == ld::Section::typeInitializerPointers ) _type = ld::Atom::typeInitializerPointers; + // don't warn about static initializers in dylibs built for profiling + if ( strncmp(s->sectname(), "__llvm_prf_", 11) == 0 ) + this->_file.setHasllvmProfiling(); break; } } @@ -6285,8 +6308,6 @@ bool Section::addRelocFixup(class Parser& parser, const macho_re Parser::TargetDesc target; Parser::TargetDesc toTarget; src.atom = this->findAtomByAddress(srcAddr); - if ( src.atom == NULL ) - throwf("malformed mach-o, reloc addr 0x%llX not in any atom", srcAddr); src.offsetInAtom = srcAddr - src.atom->_objAddress; const uint8_t* fixUpPtr = file().fileContent() + sect->offset() + reloc->r_address(); uint64_t contentValue = 0; @@ -7469,8 +7490,12 @@ bool Section::addRelocFixup(class Parser& parser, const macho_relo parser.findTargetFromAddressAndSectionNum(contentValue, nextReloc->r_symbolnum(), toTarget); useDirectBinding = (toTarget.atom->scope() == ld::Atom::scopeTranslationUnit); } - if ( useDirectBinding ) - parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom); + if ( useDirectBinding ) { + if ( (toTarget.atom->combine() == ld::Atom::combineByNameAndContent) || (toTarget.atom->combine() == ld::Atom::combineByNameAndReferences) ) + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, ld::Fixup::bindingByContentBound, toTarget.atom); + else + parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.atom); + } else parser.addFixup(src, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, toTarget.weakImport, toTarget.name); parser.addFixup(src, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, toTarget.addend); diff --git a/src/ld/parsers/textstub_dylib_file.cpp b/src/ld/parsers/textstub_dylib_file.cpp index 076736e..27c4560 100644 --- a/src/ld/parsers/textstub_dylib_file.cpp +++ b/src/ld/parsers/textstub_dylib_file.cpp @@ -29,7 +29,7 @@ #include #include "Architectures.hpp" -#include "bitcode.hpp" +#include "Bitcode.hpp" #include "MachOFileAbstraction.hpp" #include "MachOTrie.hpp" #include "generic_dylib_file.hpp" @@ -50,7 +50,14 @@ class File final : public generic::dylib::File using Base = generic::dylib::File; public: - File(const char* path, const uint8_t* fileContent, uint64_t fileLength, + File(const char* path, const uint8_t* fileContent, uint64_t fileLength, const Options *opts, + time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, + bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, + cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool enforceDylibSubtypesMatch, + bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, + bool logAllFiles, const char* installPath, bool indirectDylib); + File(std::unique_ptr &&file, const char *path, const Options *opts, time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, @@ -58,9 +65,18 @@ public: bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, bool logAllFiles, const char* installPath, bool indirectDylib); virtual ~File() noexcept {} + + // overrides of generic::dylib::File + virtual void processIndirectLibraries(ld::dylib::File::DylibHandler*, bool addImplicitDylibs) override final; private: + void init(tapi::LinkerInterfaceFile *file, const Options *opts, bool buildingForSimulator, + bool indirectDylib, bool linkingFlatNamespace, bool linkingMainExecutable, + const char *path, Options::Platform platform, const char *targetInstallPath); void buildExportHashTable(const tapi::LinkerInterfaceFile* file); + + const Options *_opts; + std::unique_ptr _interface; }; static ld::File::ObjcConstraint mapObjCConstraint(tapi::ObjCConstraint constraint) { @@ -101,8 +117,8 @@ static Options::Platform mapPlatform(tapi::Platform platform) { return Options::kPlatformUnknown; } -template - File::File(const char* path, const uint8_t* fileContent, uint64_t fileLength, + template + File::File(const char* path, const uint8_t* fileContent, uint64_t fileLength, const Options *opts, time_t mTime, ld::File::Ordinal ord, bool linkingFlatNamespace, bool linkingMainExecutable, bool hoistImplicitPublicDylibs, Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, cpu_type_t cpuType, cpu_subtype_t cpuSubType, @@ -112,12 +128,25 @@ template : Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace, hoistImplicitPublicDylibs, allowSimToMacOSX, addVers) { - std::unique_ptr file; std::string errorMessage; // Support $ld$weak symbols in .tbd files -#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 1) || (TAPI_API_VERSION_MAJOR > 1)) +#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 3) || (TAPI_API_VERSION_MAJOR > 1)) // Check if the library supports the new create API. + if (tapi::APIVersion::isAtLeast(1, 3)) { + tapi::ParsingFlags flags = tapi::ParsingFlags::None; + if (enforceDylibSubtypesMatch) + flags |= tapi::ParsingFlags::ExactCpuSubType; + + if (!allowWeakImports) + flags |= tapi::ParsingFlags::DisallowWeakImports; + + _interface.reset(tapi::LinkerInterfaceFile::create( + path, cpuType, cpuSubType, flags, + tapi::PackedVersion32(linkMinOSVersion), errorMessage)); + } else +#endif +#if ((TAPI_API_VERSION_MAJOR == 1 && TAPI_API_VERSION_MINOR >= 1) || (TAPI_API_VERSION_MAJOR > 1)) if (tapi::APIVersion::isAtLeast(1, 1)) { tapi::ParsingFlags flags = tapi::ParsingFlags::None; if (enforceDylibSubtypesMatch) @@ -126,27 +155,23 @@ template if (!allowWeakImports) flags |= tapi::ParsingFlags::DisallowWeakImports; - file.reset(tapi::LinkerInterfaceFile::create( + _interface.reset(tapi::LinkerInterfaceFile::create( path, fileContent, fileLength, cpuType, cpuSubType, flags, tapi::PackedVersion32(linkMinOSVersion), errorMessage)); - } else { + } else +#endif +#if (TAPI_API_VERSION_MAJOR >= 1) + { auto matchingType = enforceDylibSubtypesMatch ? - tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible; + tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible; - file.reset(tapi::LinkerInterfaceFile::create( + _interface.reset(tapi::LinkerInterfaceFile::create( path, fileContent, fileLength, cpuType, cpuSubType, matchingType, tapi::PackedVersion32(linkMinOSVersion), errorMessage)); } -#else - auto matchingType = enforceDylibSubtypesMatch ? - tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible; - - file.reset(tapi::LinkerInterfaceFile::create( - path, fileContent, fileLength, cpuType, cpuSubType, matchingType, - tapi::PackedVersion32(linkMinOSVersion), errorMessage)); #endif - if (file == nullptr) + if (!_interface) throw strdup(errorMessage.c_str()); // unmap file - it is no longer needed. @@ -156,6 +181,30 @@ template if ( logAllFiles ) printf("%s\n", path); + init(_interface.get(), opts, buildingForSimulator, indirectDylib, linkingFlatNamespace, + linkingMainExecutable, path, platform, targetInstallPath); +} + + template + File::File(std::unique_ptr &&file, const char* path, const Options *opts, + time_t mTime, ld::File::Ordinal ordinal, bool linkingFlatNamespace, + bool linkingMainExecutable, bool hoistImplicitPublicDylibs, + Options::Platform platform, uint32_t linkMinOSVersion, bool allowWeakImports, + cpu_type_t cpuType, cpu_subtype_t cpuSubType, bool enforceDylibSubtypesMatch, + bool allowSimToMacOSX, bool addVers, bool buildingForSimulator, + bool logAllFiles, const char* installPath, bool indirectDylib) + : Base(strdup(path), mTime, ordinal, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace, + hoistImplicitPublicDylibs, allowSimToMacOSX, addVers), _interface(std::move(file)) +{ + init(_interface.get(), opts, buildingForSimulator, indirectDylib, linkingFlatNamespace, + linkingMainExecutable, path, platform, installPath); +} + +template +void File::init(tapi::LinkerInterfaceFile *file, const Options *opts, bool buildingForSimulator, + bool indirectDylib, bool linkingFlatNamespace, bool linkingMainExecutable, + const char *path, Options::Platform platform, const char *targetInstallPath) { + _opts = opts; this->_bitcode = std::unique_ptr(new ld::Bitcode(nullptr, 0)); this->_noRexports = !file->hasReexportedLibraries(); this->_hasWeakExports = file->hasWeakDefinedExports(); @@ -179,16 +228,16 @@ template if ( strstr(this->_dylibInstallPath, frname) != NULL ) this->_frameworkName = leafName; } - + for (auto &client : file->allowableClients()) this->_allowableClients.push_back(strdup(client.c_str())); - + // [TAPI] Don't hoist "public" (in /usr/lib/) dylibs that should not be directly linked this->_hasPublicInstallName = file->hasAllowableClients() ? false : this->isPublicLocation(file->getInstallName().c_str()); - + for (const auto &client : file->allowableClients()) this->_allowableClients.emplace_back(strdup(client.c_str())); - + auto dylibPlatform = mapPlatform(file->getPlatform()); if ( (dylibPlatform != platform) && (platform != Options::kPlatformUnknown) ) { this->_wrongOS = true; @@ -196,23 +245,23 @@ template if ( buildingForSimulator ) { if ( !this->_allowSimToMacOSXLinking ) throwf("building for %s simulator, but linking against dylib built for %s (%s).", - Options::platformName(platform), Options::platformName(dylibPlatform), path); + Options::platformName(platform), Options::platformName(dylibPlatform), path); } else { throwf("building for %s, but linking against dylib built for %s (%s).", - Options::platformName(platform), Options::platformName(dylibPlatform), path); + Options::platformName(platform), Options::platformName(dylibPlatform), path); } } } - + for (const auto& reexport : file->reexportedLibraries()) { const char *path = strdup(reexport.c_str()); if ( (targetInstallPath == nullptr) || (strcmp(targetInstallPath, path) != 0) ) this->_dependentDylibs.emplace_back(path, true); } - + for (const auto& symbol : file->ignoreExports()) this->_ignoreExports.insert(strdup(symbol.c_str())); - + // if linking flat and this is a flat dylib, create one atom that references all imported symbols. if ( linkingFlatNamespace && linkingMainExecutable && (file->hasTwoLevelNamespace() == false) ) { std::vector importNames; @@ -223,9 +272,9 @@ template importNames.emplace_back(sym.getName().c_str()); this->_importAtom = new generic::dylib::ImportAtom(*this, importNames); } - + // build hash table - buildExportHashTable(file.get()); + buildExportHashTable(file); } template @@ -245,6 +294,13 @@ void File::buildExportHashTable(const tapi::LinkerInterfaceFile* file) { } } +template +void File::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) { + if (_interface) + _opts->addTAPIInterface(_interface.get(), this->path()); + Base::processIndirectLibraries(handler, addImplicitDylibs); +} + template class Parser { @@ -256,7 +312,7 @@ public: ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { - return new File(path, fileContent, fileLength,mTime, ordinal, + return new File(path, fileContent, fileLength, &opts, mTime, ordinal, opts.flatNamespace(), opts.linkingMainExecutable(), opts.implicitlyLinkIndirectPublicDylibs(), @@ -273,6 +329,29 @@ public: opts.installPath(), indirectDylib); } + + static ld::dylib::File* parse(const char* path, std::unique_ptr &&file, time_t mTime, + ld::File::Ordinal ordinal, const Options& opts, + bool indirectDylib) + { + return new File(std::move(file), path, &opts, mTime, ordinal, + opts.flatNamespace(), + opts.linkingMainExecutable(), + opts.implicitlyLinkIndirectPublicDylibs(), + opts.platform(), + opts.minOSversion(), + opts.allowWeakImports(), + opts.architecture(), + opts.subArchitecture(), + opts.enforceDylibSubtypesMatch(), + opts.allowSimulatorToLinkWithMacOSX(), + opts.addVersionLoadCommand(), + opts.targetIOSSimulator(), + opts.logAllFiles(), + opts.installPath(), + indirectDylib); + } + }; // @@ -307,7 +386,31 @@ ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, const ch } return nullptr; } - - + +ld::dylib::File *parse(const char *path, std::unique_ptr &&file, time_t modTime, + ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib) { + switch ( opts.architecture() ) { +#if SUPPORT_ARCH_x86_64 + case CPU_TYPE_X86_64: + return Parser::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib); +#endif +#if SUPPORT_ARCH_i386 + case CPU_TYPE_I386: + return Parser::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib); +#endif +#if SUPPORT_ARCH_arm_any + case CPU_TYPE_ARM: + return Parser::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib); +#endif +#if SUPPORT_ARCH_arm64 + case CPU_TYPE_ARM64: + return Parser::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib); +#endif + } + return nullptr; + +} + + } // namespace dylib } // namespace textstub diff --git a/src/ld/parsers/textstub_dylib_file.hpp b/src/ld/parsers/textstub_dylib_file.hpp index e0a75f6..9931908 100644 --- a/src/ld/parsers/textstub_dylib_file.hpp +++ b/src/ld/parsers/textstub_dylib_file.hpp @@ -36,6 +36,9 @@ extern ld::dylib::File* parse(const uint8_t* fileContent, uint64_t fileLength, c time_t modTime, const Options& opts, ld::File::Ordinal ordinal, bool bundleLoader, bool indirectDylib); +extern ld::dylib::File *parse(const char *path, std::unique_ptr &&file, time_t modTime, + ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib); + } // namespace dylib } // namespace textstub diff --git a/src/ld/passes/branch_shim.cpp b/src/ld/passes/branch_shim.cpp index 7be33d2..d5f81eb 100644 --- a/src/ld/passes/branch_shim.cpp +++ b/src/ld/passes/branch_shim.cpp @@ -348,7 +348,7 @@ void doPass(const Options& opts, ld::Internal& state) break; fixUpLocation += fit->offsetInAtom; uint32_t instruction = *((uint32_t*)fixUpLocation); - bool is_b = ((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000); + bool is_b = ((instruction & 0x0E000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000); // need shim for branch from arm to thumb, or for call to function outside kext if ( is_b || (targetIsProxy && makingKextBundle) ) { if ( _s_log ) fprintf(stderr, "need to add arm->thumb instr=0x%08X shim to %s for %s\n", instruction, target->name(), atom->name()); diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index eddcbc9..ef6ba48 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -393,7 +393,7 @@ void MachOChecker::checkMachHeader() throw "sizeofcmds in mach_header is larger than file"; uint32_t flags = fHeader->flags(); - const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xFC000000; + const uint32_t invalidBits = MH_INCRLINK | MH_LAZY_INIT | 0xF8000000; if ( flags & invalidBits ) throw "invalid bits in mach_header flags"; if ( (flags & MH_NO_REEXPORTED_DYLIBS) && (fHeader->filetype() != MH_DYLIB) ) diff --git a/unit-tests/test-cases/linker_options-framework-static-chain/Makefile b/unit-tests/test-cases/linker_options-framework-static-chain/Makefile new file mode 100644 index 0000000..fdeed4c --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework-static-chain/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check linker options work for -framework containing static archives +# in a recursive manner, chaining linkage from one archive to another +# + +run: all + +all: + mkdir -p Baz.framework + ${CC} ${CCFLAGS} baz.c -c -o baz.o + libtool -static baz.o -o Baz.framework/Baz + mkdir -p Bar.framework + ${CC} ${CCFLAGS} bar.c -c -o bar.o + libtool -static bar.o -o Bar.framework/Bar + mkdir -p Foo.framework + ${CC} ${CCFLAGS} foo.c -c -o foo.o + ${LD} -r foo.o -add_linker_option '-framework Bar' -add_linker_option '-framework Baz' -o foo.o + libtool -static foo.o -o Foo.framework/Foo + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} -r main.o -add_linker_option '-framework Foo' -o main.o + ${CC} ${CCFLAGS} main.o -o main -F. + ${DYLDINFO} -export main | grep _baz | ${FAIL_IF_EMPTY} + ${DYLDINFO} -export main | grep _bar | ${FAIL_IF_EMPTY} + ${DYLDINFO} -export main | grep _foo | ${FAIL_IF_EMPTY} + ${DYLDINFO} -export main | grep _main | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main main.o foo.o bar.o baz.o Foo.framework Bar.framework Baz.framework diff --git a/unit-tests/test-cases/linker_options-framework-static-chain/bar.c b/unit-tests/test-cases/linker_options-framework-static-chain/bar.c new file mode 100644 index 0000000..981110f --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework-static-chain/bar.c @@ -0,0 +1 @@ +void bar() { } diff --git a/unit-tests/test-cases/linker_options-framework-static-chain/baz.c b/unit-tests/test-cases/linker_options-framework-static-chain/baz.c new file mode 100644 index 0000000..a6dd5ab --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework-static-chain/baz.c @@ -0,0 +1 @@ +void baz() { } diff --git a/unit-tests/test-cases/linker_options-framework-static-chain/foo.c b/unit-tests/test-cases/linker_options-framework-static-chain/foo.c new file mode 100644 index 0000000..ab39b78 --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework-static-chain/foo.c @@ -0,0 +1,8 @@ + +extern void bar(); +extern void baz(); + +void foo() { + bar(); + baz(); +} diff --git a/unit-tests/test-cases/linker_options-framework-static-chain/main.c b/unit-tests/test-cases/linker_options-framework-static-chain/main.c new file mode 100644 index 0000000..4f56fe0 --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework-static-chain/main.c @@ -0,0 +1,8 @@ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/unit-tests/test-cases/linker_options-framework-static/Makefile b/unit-tests/test-cases/linker_options-framework-static/Makefile new file mode 100644 index 0000000..9e01431 --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework-static/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check linker options work for -framework containing static archive +# + +run: all + +all: + mkdir -p Foo.framework + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o Foo.framework/Foo + ${CC} ${CCFLAGS} main.c -c -o main.o + ${LD} -r main.o -add_linker_option '-framework Foo' -o main2.o + ${CC} ${CCFLAGS} main2.o -o main -F. + ${DYLDINFO} -export main | grep _foo | ${FAIL_IF_EMPTY} + ${DYLDINFO} -export main | grep _main | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} main + +clean: + rm -rf main foo.o main.o main2.o Foo.framework + diff --git a/unit-tests/test-cases/linker_options-framework-static/foo.c b/unit-tests/test-cases/linker_options-framework-static/foo.c new file mode 100644 index 0000000..e704870 --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework-static/foo.c @@ -0,0 +1,3 @@ + + +void foo() { } diff --git a/unit-tests/test-cases/linker_options-framework-static/main.c b/unit-tests/test-cases/linker_options-framework-static/main.c new file mode 100644 index 0000000..4f56fe0 --- /dev/null +++ b/unit-tests/test-cases/linker_options-framework-static/main.c @@ -0,0 +1,8 @@ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/unit-tests/test-cases/lto-duplicate_symbols/Makefile b/unit-tests/test-cases/lto-duplicate_symbols/Makefile new file mode 100644 index 0000000..d8d3135 --- /dev/null +++ b/unit-tests/test-cases/lto-duplicate_symbols/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2017 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# rdar://problem/35099885 +# Check that with ThinLTO linker can still distinguish between static and +# non-static functions of the same name, and doesn't mix them up. +# + +run: all + +all: + ${CC} ${CCFLAGS} -flto=thin static.c -c -o static.o + ${CC} ${CCFLAGS} -flto=thin non-static.c -c -o non-static.o + ${CC} ${CCFLAGS} -flto=thin main.c -c -o main.o + # Option -flto-codegen-only is used to avoid LTO optimizations because they + # can rename a static function and thus prevent from testing how linker + # handles duplicate names. + ${CC} ${CCFLAGS} -flto=thin -Wl,-flto-codegen-only static.o non-static.o main.o -o main + ${FAIL_IF_BAD_MACHO} main + nm -j main | grep --count "^_same_name$$" | grep "^2$$" | ${PASS_IFF_STDIN} + +clean: + rm -rf main static.o non-static.o main.o diff --git a/unit-tests/test-cases/lto-duplicate_symbols/main.c b/unit-tests/test-cases/lto-duplicate_symbols/main.c new file mode 100644 index 0000000..699e024 --- /dev/null +++ b/unit-tests/test-cases/lto-duplicate_symbols/main.c @@ -0,0 +1,7 @@ +int same_name(); +int other(); + +int main(void) +{ + return same_name() + other(); +} diff --git a/unit-tests/test-cases/lto-duplicate_symbols/non-static.c b/unit-tests/test-cases/lto-duplicate_symbols/non-static.c new file mode 100644 index 0000000..00039d7 --- /dev/null +++ b/unit-tests/test-cases/lto-duplicate_symbols/non-static.c @@ -0,0 +1,4 @@ +int same_name(void) +{ + return 32; +} diff --git a/unit-tests/test-cases/lto-duplicate_symbols/static.c b/unit-tests/test-cases/lto-duplicate_symbols/static.c new file mode 100644 index 0000000..d40d247 --- /dev/null +++ b/unit-tests/test-cases/lto-duplicate_symbols/static.c @@ -0,0 +1,9 @@ +static int same_name(int i) +{ + return i + 10; +} + +int other() +{ + return same_name(100); +} -- 2.45.2