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
.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.
);
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 */ = {
"-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;
"-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;
"-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;
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
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);
if ( _options.dumpDependencyInfo() ) {
const ld::dylib::File* dylib = dynamic_cast<const ld::dylib::File*>(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());
}
}
}
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<ld::dylib::File*>(reader);
+ ld::archive::File* archiveReader = dynamic_cast<ld::archive::File*>(reader);
if ( dylibReader != NULL ) {
if ( ! dylibReader->installPathVersionSpecific() ) {
dylibReader->forEachAtom(handler);
this->addDylib(dylibReader, info);
}
}
+ else if ( archiveReader != NULL ) {
+ _searchLibraries.push_back(LibraryInfo(archiveReader));
+ _options.addDependency(Options::depArchive, archiveReader->path());
+ //<rdar://problem/17787306> -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);
}
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;
//<rdar://problem/17787306> -force_load_swift_libs
info.options.fForceLoad = _options.forceLoadSwiftLibs() && (strncmp(libName, "swift", 5) == 0);
ld::File* reader = this->makeFile(info, true);
}
else if ( archiveReader != NULL ) {
_searchLibraries.push_back(LibraryInfo(archiveReader));
- if ( _options.dumpDependencyInfo() )
- _options.dumpDependency(Options::depArchive, archiveReader->path());
+ _options.addDependency(Options::depArchive, archiveReader->path());
//<rdar://problem/17787306> -force_load_swift_libs
if (info.options.fForceLoad) {
archiveReader->forEachAtom(handler);
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);
}
}
// 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);
}
}
{
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:
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:
static void parseWorkerThread(InputFiles *inputFiles);
void startThread(void (*threadFunc)(InputFiles *)) const;
- typedef std::unordered_map<const char*, ld::dylib::File*, CStringHash, CStringEquals> InstallNameToDylib;
+ typedef std::map<std::string, ld::dylib::File*> InstallNameToDylib;
const Options& _options;
std::vector<ld::File*> _inputFiles;
#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);
bool Options::FileInfo::checkFileExists(const Options& options, const char *p)
{
+ if (isInlined) {
+ modTime = 0;
+ return true;
+ }
struct stat statBuffer;
if (p == NULL)
p = path;
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;
}
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),
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),
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);
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);
}
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<std::string> &tbdExtensions, FileInfo& result) const
{
FileInfo tbdInfo;
}
}
+ // 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;
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<tapi::LinkerInterfaceFile> Options::findTAPIFile(const std::string &path) const
+{
+ std::unique_ptr<tapi::LinkerInterfaceFile> 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
{
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];
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);
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;
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];
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);
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 ) {
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");
if (pipeFdString != NULL) {
fPipelineFifo = pipeFdString;
}
+
+ // <rdar://problem/30746905> [Reproducible Builds] If env ZERO_AR_DATE is set, zero out timestamp in N_OSO stab
+ if ( getenv("ZERO_AR_DATE") != NULL )
+ fZeroModTimeInDebugMap = true;
}
// <rdar://problem/24772435> only use v2 for Swift dylibs on Mac OS X
if ( strncmp(this->installPath(), "/System/Library/PrivateFrameworks/Swift/", 40) == 0 )
fSharedRegionEncodingV2 = true;
+ // <rdar://problem/32525720> use v2 for ABI stable Swift dylibs on macOS
+ if ( strncmp(this->installPath(), "/System/Library/Frameworks/Swift/", 33) == 0 )
+ fSharedRegionEncodingV2 = true;
// <rdar://problem/31428120> an other OS frameworks that use swift need v2
for (const char* searchPath : fLibrarySearchPaths ) {
if ( strstr(searchPath, "xctoolchain/usr/lib/swift/macos") != NULL ) {
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()
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 ) {
}
-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;
+ // <rdar://problem/30750137> 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 ) {
}
}
- 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);
}
#include <stdint.h>
#include <mach/machine.h>
+#include <tapi/tapi.h>
#include <vector>
#include <unordered_set>
#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)));
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
// 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);
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); }
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;
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;
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);
bool keepRelocations();
FileInfo findFile(const std::string &path, const ld::dylib::File* fromDylib=nullptr) const;
bool findFile(const std::string &path, const std::vector<std::string> &tbdExtensions, FileInfo& result) const;
+ bool hasInlinedTAPIFile(const std::string &path) const;
+ std::unique_ptr<tapi::LinkerInterfaceFile> findTAPIFile(const std::string &path) const;
UUIDMode UUIDMode() const { return fUUIDMode; }
bool warnStabs();
bool pauseAtEnd() { return fPause; }
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; }
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; }
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<TAPIInterface> &TAPIFiles() { return fTAPIFiles; }
+ void addTAPIInterface(tapi::LinkerInterfaceFile *interface, const char *path) const;
static uint32_t parseVersionNumber32(const char*);
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[]);
bool fNoEHLabels;
bool fAllowCpuSubtypeMismatches;
bool fEnforceDylibSubtypesMatch;
+ bool fWarnOnSwiftABIVersionMismatches;
bool fUseSimplifiedDylibReExports;
bool fObjCABIVersion2Override;
bool fObjCABIVersion1Override;
bool fTraceEmitJSON;
bool fOutputSlidable;
bool fWarnWeakExports;
+ bool fNoWeakExports;
bool fObjcGcCompaction;
bool fObjCGc;
bool fObjCGcOnly;
bool fIgnoreAutoLink;
bool fAllowDeadDups;
bool fAllowWeakImports;
- bool fNoInitializers;
+ Treatment fInitializersTreatment;
+ bool fZeroModTimeInDebugMap;
BitcodeMode fBitcodeKind;
Platform fPlatform;
DebugInfoStripping fDebugInfoStripping;
bool fSnapshotRequested;
const char* fPipelineFifo;
const char* fDependencyInfoPath;
- mutable int fDependencyFileDescriptor;
mutable int fTraceFileDescriptor;
uint8_t fMaxDefaultCommonAlign;
UnalignedPointerTreatment fUnalignedPointerTreatment;
+ mutable std::vector<DependencyEntry> fDependencies;
+ mutable std::vector<Options::TAPIInterface> fTAPIFiles;
+
};
std::sort(atomsNeedingDebugNotes.begin(), atomsNeedingDebugNotes.end(), DebugNoteSorter());
// <rdar://problem/17689030> Add -add_ast_path option to linker which add N_AST stab entry to output
+ std::set<std::string> seenAstPaths;
const std::vector<const char*>& astPaths = _options.astFilePaths();
for (std::vector<const char*>::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);
}
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;
asprintf(&fullFilePath, "%s%s", newDirPath, newFilename);
// add both leaf path and full path
seenFiles.insert(fullFilePath);
+
+ // <rdar://problem/34121435> Add linker support for propagating N_AST debug notes from .o files to linked image
+ if ( const std::vector<relocatable::File::AstTimeAndPath>* 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;
}
}
}
+
}
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);
+ }
}
}
}
// 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 )
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);
+ }
}
}
}
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() ) {
// and re-compute dead code
this->deadStripOptimize(true);
}
-
+
// <rdar://problem/12386559> if -exported_symbols_list on command line, re-force scope
if ( _options.hasExportMaskList() ) {
for (std::vector<const ld::Atom*>::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) {
// check new code does not override some dylib
this->checkDylibSymbolCollisions();
+
+ // <rdar://problem/33853815> remove undefs from LTO objects that gets optimized away
+ std::unordered_set<const ld::Atom*> 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);
}
}
_haveLLVMObjs(false),
_completedInitialObjectFiles(false),
_ltoCodeGenFinished(false),
- _haveAliases(false) {}
+ _haveAliases(false), _havellvmProfiling(false) {}
virtual void doAtom(const ld::Atom&);
bool _completedInitialObjectFiles;
bool _ltoCodeGenFinished;
bool _haveAliases;
+ bool _havellvmProfiling;
};
return _indirectBindingTable[slot];
}
+
+void SymbolTable::removeDeadUndefs(std::vector<const ld::Atom*>& allAtoms, const std::unordered_set<const ld::Atom*>& keep)
+{
+ // mark the indirect entries in use
+ std::vector<bool> 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",
byNameIterator begin() { return byNameIterator(_byNameTable.begin(),_indirectBindingTable); }
byNameIterator end() { return byNameIterator(_byNameTable.end(),_indirectBindingTable); }
void printStatistics();
-
+ void removeDeadUndefs(std::vector<const ld::Atom *>& allAtoms, const std::unordered_set<const ld::Atom*>& keep);
+
// from ld::IndirectBindingTable
virtual const char* indirectName(IndirectBindingSlot slot) const;
virtual const ld::Atom* indirectAtom(IndirectBindingSlot slot) const;
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;
// sort final sections
state.sortSections();
+ options.writeDependencyInfo();
+
// write output file
statistics.startOutput = mach_absolute_time();
ld::tool::OutputFile out(options);
};
typedef const std::vector< std::vector<const char*> > LinkerOptionsList;
typedef std::vector<std::pair<uint32_t,uint32_t>> ToolVersionList;
+ struct AstTimeAndPath { uint64_t time; std::string path; };
File(const char* pth, time_t modTime, Ordinal ord)
: ld::File(pth, modTime, ord, Reloc) { }
virtual const std::vector<Stab>* 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<AstTimeAndPath>* astFiles() const { return nullptr; }
};
} // namespace relocatable
// 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<const char*>* allowableClients() const override final { return _allowableClients.empty() ? nullptr : &_allowableClients; }
// 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);
// 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);
_minOSVersion(0),
_platform(Options::kPlatformUnknown),
_canScatterAtoms(false),
+ _hasllvmProfiling(false),
_objcHasCategoryClassPropertiesField(false),
_srcKind(kSourceUnknown) { }
virtual ~File();
virtual DebugInfoKind debugInfo() const { return _debugInfoKind; }
virtual const std::vector<ld::relocatable::File::Stab>* 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; }
virtual SourceKind sourceKind() const { return _srcKind; }
virtual const uint8_t* fileContent() const { return _fileContent; }
+ virtual const std::vector<AstTimeAndPath>* astFiles() const { return &_astFiles; }
+
+ void setHasllvmProfiling() { _hasllvmProfiling = true; }
private:
friend class Atom<A>;
friend class Section<A>;
friend class Parser<A>;
friend class CFISection<A>::OAS;
-
+
typedef typename A::P P;
const uint8_t* _fileContent;
std::vector<ld::Atom::UnwindInfo> _unwindInfos;
std::vector<ld::Atom::LineInfo> _lineInfos;
std::vector<ld::relocatable::File::Stab>_stabs;
+ std::vector<AstTimeAndPath> _astFiles;
ld::relocatable::File::DebugInfoKind _debugInfoKind;
const char* _dwarfTranslationUnitPath;
const macho_section<P>* _dwarfDebugInfoSect;
uint32_t _minOSVersion;
Options::Platform _platform;
bool _canScatterAtoms;
+ bool _hasllvmProfiling;
bool _objcHasCategoryClassPropertiesField;
std::vector<std::vector<const char*> > _linkerOptions;
std::unique_ptr<ld::Bitcode> _bitcode;
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,
bool Parser<A>::LabelAndCFIBreakIterator::next(Parser<A>& parser, const Section<A>& sect, uint32_t sectNum, pint_t startAddr, pint_t endAddr,
pint_t* addr, pint_t* size, const macho_nlist<P>** 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;
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
}
- // 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<P>*)((char*)segment + sizeof(macho_segment_command<P>));
_machOSectionsCount = segment->nsects();
if ( (sizeof(macho_segment_command<P>) + _machOSectionsCount * sizeof(macho_section<P>)) > segment->cmdsize() )
throw "too many sections for size of LC_SEGMENT command";
-
return true;
}
template <typename A>
void Parser<A>::parseDebugInfo()
{
+ addAstFiles();
+
// check for dwarf __debug_info section
if ( _file->_dwarfDebugInfoSect == NULL ) {
// if no DWARF debug info, look for stabs
}
+template <typename A>
+void Parser<A>::addAstFiles()
+{
+ // scan symbol table for N_AST entries
+ for (uint32_t symbolIndex = 0; symbolIndex < _symbolCount; ++symbolIndex ) {
+ const macho_nlist<P>& 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
template <>
void CFISection<x86_64>::cfiParse(class Parser<x86_64>& parser, uint8_t* buffer,
- libunwind::CFI_Atom_Info<CFISection<x86_64>::OAS>::CFI_Atom_Info cfiArray[],
+ libunwind::CFI_Atom_Info<CFISection<x86_64>::OAS> cfiArray[],
uint32_t& count, const pint_t cuStarts[], uint32_t cuCount)
{
const uint32_t sectionSize = this->_machOSection->size();
template <>
void CFISection<x86>::cfiParse(class Parser<x86>& parser, uint8_t* buffer,
- libunwind::CFI_Atom_Info<CFISection<x86>::OAS>::CFI_Atom_Info cfiArray[],
+ libunwind::CFI_Atom_Info<CFISection<x86>::OAS> cfiArray[],
uint32_t& count, const pint_t cuStarts[], uint32_t cuCount)
{
// create ObjectAddressSpace object for use by libunwind
template <>
void CFISection<arm>::cfiParse(class Parser<arm>& parser, uint8_t* buffer,
- libunwind::CFI_Atom_Info<CFISection<arm>::OAS>::CFI_Atom_Info cfiArray[],
+ libunwind::CFI_Atom_Info<CFISection<arm>::OAS> cfiArray[],
uint32_t& count, const pint_t cuStarts[], uint32_t cuCount)
{
if ( !parser.armUsesZeroCostExceptions() ) {
template <>
void CFISection<arm64>::cfiParse(class Parser<arm64>& parser, uint8_t* buffer,
- libunwind::CFI_Atom_Info<CFISection<arm64>::OAS>::CFI_Atom_Info cfiArray[],
+ libunwind::CFI_Atom_Info<CFISection<arm64>::OAS> cfiArray[],
uint32_t& count, const pint_t cuStarts[], uint32_t cuCount)
{
// copy __eh_frame data to buffer
_type = ld::Atom::typeLSDA;
else if ( this->type() == ld::Section::typeInitializerPointers )
_type = ld::Atom::typeInitializerPointers;
+ // <rdar://problem/34716321> don't warn about static initializers in dylibs built for profiling
+ if ( strncmp(s->sectname(), "__llvm_prf_", 11) == 0 )
+ this->_file.setHasllvmProfiling();
break;
}
}
Parser<x86_64>::TargetDesc target;
Parser<x86_64>::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;
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);
#include <vector>
#include "Architectures.hpp"
-#include "bitcode.hpp"
+#include "Bitcode.hpp"
#include "MachOFileAbstraction.hpp"
#include "MachOTrie.hpp"
#include "generic_dylib_file.hpp"
using Base = generic::dylib::File<A>;
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<tapi::LinkerInterfaceFile> &&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,
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<tapi::LinkerInterfaceFile> _interface;
};
static ld::File::ObjcConstraint mapObjCConstraint(tapi::ObjCConstraint constraint) {
return Options::kPlatformUnknown;
}
-template <typename A>
- File<A>::File(const char* path, const uint8_t* fileContent, uint64_t fileLength,
+ template <typename A>
+ File<A>::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,
: Base(strdup(path), mTime, ord, platform, linkMinOSVersion, allowWeakImports, linkingFlatNamespace,
hoistImplicitPublicDylibs, allowSimToMacOSX, addVers)
{
- std::unique_ptr<tapi::LinkerInterfaceFile> file;
std::string errorMessage;
// <rdar://problem/29038544> 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)
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.
if ( logAllFiles )
printf("%s\n", path);
+ init(_interface.get(), opts, buildingForSimulator, indirectDylib, linkingFlatNamespace,
+ linkingMainExecutable, path, platform, targetInstallPath);
+}
+
+ template<typename A>
+ File<A>::File(std::unique_ptr<tapi::LinkerInterfaceFile> &&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<typename A>
+void File<A>::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<ld::Bitcode>(new ld::Bitcode(nullptr, 0));
this->_noRexports = !file->hasReexportedLibraries();
this->_hasWeakExports = file->hasWeakDefinedExports();
if ( strstr(this->_dylibInstallPath, frname) != NULL )
this->_frameworkName = leafName;
}
-
+
for (auto &client : file->allowableClients())
this->_allowableClients.push_back(strdup(client.c_str()));
-
+
// <rdar://problem/20659505> [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;
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<const char*> importNames;
importNames.emplace_back(sym.getName().c_str());
this->_importAtom = new generic::dylib::ImportAtom<A>(*this, importNames);
}
-
+
// build hash table
- buildExportHashTable(file.get());
+ buildExportHashTable(file);
}
template <typename A>
}
}
+template <typename A>
+void File<A>::processIndirectLibraries(ld::dylib::File::DylibHandler* handler, bool addImplicitDylibs) {
+ if (_interface)
+ _opts->addTAPIInterface(_interface.get(), this->path());
+ Base::processIndirectLibraries(handler, addImplicitDylibs);
+}
+
template <typename A>
class Parser
{
ld::File::Ordinal ordinal, const Options& opts,
bool indirectDylib)
{
- return new File<A>(path, fileContent, fileLength,mTime, ordinal,
+ return new File<A>(path, fileContent, fileLength, &opts, mTime, ordinal,
opts.flatNamespace(),
opts.linkingMainExecutable(),
opts.implicitlyLinkIndirectPublicDylibs(),
opts.installPath(),
indirectDylib);
}
+
+ static ld::dylib::File* parse(const char* path, std::unique_ptr<tapi::LinkerInterfaceFile> &&file, time_t mTime,
+ ld::File::Ordinal ordinal, const Options& opts,
+ bool indirectDylib)
+ {
+ return new File<A>(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);
+ }
+
};
//
}
return nullptr;
}
-
-
+
+ld::dylib::File *parse(const char *path, std::unique_ptr<tapi::LinkerInterfaceFile> &&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<x86_64>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
+#endif
+#if SUPPORT_ARCH_i386
+ case CPU_TYPE_I386:
+ return Parser<x86>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
+#endif
+#if SUPPORT_ARCH_arm_any
+ case CPU_TYPE_ARM:
+ return Parser<arm>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
+#endif
+#if SUPPORT_ARCH_arm64
+ case CPU_TYPE_ARM64:
+ return Parser<arm64>::parse(path, std::move(file), modTime, ordinal, opts, indirectDylib);
+#endif
+ }
+ return nullptr;
+
+}
+
+
} // namespace dylib
} // namespace textstub
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<tapi::LinkerInterfaceFile> &&file, time_t modTime,
+ ld::File::Ordinal ordinal, const Options& opts, bool indirectDylib);
+
} // namespace dylib
} // namespace textstub
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());
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) )
--- /dev/null
+##
+# 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
--- /dev/null
+void bar() { }
--- /dev/null
+void baz() { }
--- /dev/null
+
+extern void bar();
+extern void baz();
+
+void foo() {
+ bar();
+ baz();
+}
--- /dev/null
+
+extern void foo();
+
+int main()
+{
+ foo();
+ return 0;
+}
--- /dev/null
+##
+# 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
+
--- /dev/null
+
+
+void foo() { }
--- /dev/null
+
+extern void foo();
+
+int main()
+{
+ foo();
+ return 0;
+}
--- /dev/null
+##
+# 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
--- /dev/null
+int same_name();
+int other();
+
+int main(void)
+{
+ return same_name() + other();
+}
--- /dev/null
+int same_name(void)
+{
+ return 32;
+}
--- /dev/null
+static int same_name(int i)
+{
+ return i + 10;
+}
+
+int other()
+{
+ return same_name(100);
+}