From 82b4b32b1851b4bba2473356c2208bd34561ee02 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 18 Sep 2018 20:50:58 +0000 Subject: [PATCH] ld64-278.4.tar.gz --- src/ld/HeaderAndLoadCommands.hpp | 19 ++- src/ld/InputFiles.cpp | 57 ++++----- src/ld/LinkEdit.hpp | 2 +- src/ld/LinkEditClassic.hpp | 21 ++++ src/ld/Options.cpp | 73 +++++++----- src/ld/Options.h | 31 ++++- src/ld/OutputFile.cpp | 88 ++++++++++---- src/ld/Resolver.cpp | 63 +++++----- src/ld/SymbolTable.cpp | 24 ++-- src/ld/ld.hpp | 17 ++- .../parsers/libunwind/DwarfInstructions.hpp | 4 + src/ld/parsers/lto_file.cpp | 110 +++++++++++++++--- src/ld/parsers/lto_file.h | 1 + src/ld/parsers/macho_relocatable_file.cpp | 15 ++- src/ld/parsers/textstub_dylib_file.cpp | 37 +++++- src/ld/passes/bitcode_bundle.cpp | 47 ++++++++ src/ld/passes/code_dedup.cpp | 53 ++++++--- src/ld/passes/objc.cpp | 8 +- src/ld/passes/order.cpp | 22 +++- src/other/ObjectDump.cpp | 21 ++-- src/other/dyldinfo.cpp | 3 + src/other/machochecker.cpp | 2 + .../lto-live_support_section/Makefile | 45 +++++++ .../test-cases/lto-live_support_section/foo.c | 22 ++++ .../test-cases/lto-unexport-sym/Makefile | 42 +++++++ unit-tests/test-cases/lto-unexport-sym/a.c | 7 ++ unit-tests/test-cases/lto-unexport-sym/b.c | 3 + .../test-cases/lto-unexport-sym/unexp.list | 2 + 28 files changed, 627 insertions(+), 212 deletions(-) create mode 100644 unit-tests/test-cases/lto-live_support_section/Makefile create mode 100644 unit-tests/test-cases/lto-live_support_section/foo.c create mode 100644 unit-tests/test-cases/lto-unexport-sym/Makefile create mode 100644 unit-tests/test-cases/lto-unexport-sym/a.c create mode 100644 unit-tests/test-cases/lto-unexport-sym/b.c create mode 100644 unit-tests/test-cases/lto-unexport-sym/unexp.list diff --git a/src/ld/HeaderAndLoadCommands.hpp b/src/ld/HeaderAndLoadCommands.hpp index 454eb1f..a7d6620 100644 --- a/src/ld/HeaderAndLoadCommands.hpp +++ b/src/ld/HeaderAndLoadCommands.hpp @@ -176,7 +176,7 @@ HeaderAndLoadCommandsAtom::HeaderAndLoadCommandsAtom(const Options& opts, ld: ld::Atom::definitionRegular, ld::Atom::combineNever, ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, ld::Atom::symbolTableNotIn, false, false, false, - (opts.outputKind() == Options::kPreload) ? ld::Atom::Alignment(0) : ld::Atom::Alignment(12) ), + (opts.outputKind() == Options::kPreload) ? ld::Atom::Alignment(0) : ld::Atom::Alignment(log2(opts.segmentAlignment())) ), _options(opts), _state(state), _writer(writer), _address(0), _uuidCmdInOutputBuffer(NULL), _linkeditCmdOffset(0), _symboltableCmdOffset(0) { bzero(_uuid, 16); @@ -724,7 +724,7 @@ uint8_t* HeaderAndLoadCommandsAtom::copySingleSegmentLoadCommand(uint8_t* p) if ( cmd->fileoff() == 0 ) cmd->set_fileoff(fsect->fileOffset); cmd->set_vmsize(fsect->address + fsect->size - cmd->vmaddr()); - if ( (fsect->type() != ld::Section::typeZeroFill) && (fsect->type() != ld::Section::typeTentativeDefs) ) + if ( !sectionTakesNoDiskSpace(fsect) ) cmd->set_filesize(fsect->fileOffset + fsect->size - cmd->fileoff()); ++msect; } @@ -1348,14 +1348,21 @@ uint8_t* HeaderAndLoadCommandsAtom::copyDylibLoadCommand(uint8_t* p, const ld { uint32_t sz = alignedSize(sizeof(macho_dylib_command

) + strlen(dylib->installPath()) + 1); macho_dylib_command

* cmd = (macho_dylib_command

*)p; + bool weakLink = dylib->forcedWeakLinked() || dylib->allSymbolsAreWeakImported(); + bool upward = dylib->willBeUpwardDylib() && _options.useUpwardDylibs(); + bool reExport = dylib->willBeReExported() && _options.useSimplifiedDylibReExports(); + if ( weakLink && upward ) + warning("cannot weak upward link. Dropping weak for %s", dylib->installPath()); + if ( weakLink && reExport ) + warning("cannot weak re-export a dylib. Dropping weak for %s", dylib->installPath()); if ( dylib->willBeLazyLoadedDylib() ) cmd->set_cmd(LC_LAZY_LOAD_DYLIB); - else if ( dylib->forcedWeakLinked() || dylib->allSymbolsAreWeakImported() ) - cmd->set_cmd(LC_LOAD_WEAK_DYLIB); - else if ( dylib->willBeReExported() && _options.useSimplifiedDylibReExports() ) + else if ( reExport ) cmd->set_cmd(LC_REEXPORT_DYLIB); - else if ( dylib->willBeUpwardDylib() && _options.useUpwardDylibs() ) + else if ( upward ) cmd->set_cmd(LC_LOAD_UPWARD_DYLIB); + else if ( weakLink ) + cmd->set_cmd(LC_LOAD_WEAK_DYLIB); else cmd->set_cmd(LC_LOAD_DYLIB); cmd->set_cmdsize(sz); diff --git a/src/ld/InputFiles.cpp b/src/ld/InputFiles.cpp index e8e6bcc..79401f2 100644 --- a/src/ld/InputFiles.cpp +++ b/src/ld/InputFiles.cpp @@ -177,6 +177,11 @@ private: ld::Section CustomStackAtom::_s_section("__UNIXSTACK", "__stack", ld::Section::typeStack, true); +static bool isCompilerSupportLib(const char* path) { + const char* libName = strrchr(path, '/'); + return ( (libName != NULL) && (strncmp(libName, "/libclang_rt", 12) == 0) ); +} + const char* InputFiles::fileArch(const uint8_t* p, unsigned len) { @@ -210,14 +215,16 @@ const char* InputFiles::fileArch(const uint8_t* p, unsigned len) ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib) { // map in whole file - uint64_t len = info.fileLen; + struct stat stat_buf; int fd = ::open(info.path, O_RDONLY, 0); if ( fd == -1 ) throwf("can't open file, errno=%d", errno); - if ( info.fileLen < 20 ) - throwf("file too small (length=%llu)", info.fileLen); - - uint8_t* p = (uint8_t*)::mmap(NULL, info.fileLen, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( ::fstat(fd, &stat_buf) != 0 ) + throwf("fstat(%s) failed, errno=%d\n", info.path, errno); + if ( stat_buf.st_size < 20 ) + throwf("file too small (length=%llu)", stat_buf.st_size); + int64_t len = stat_buf.st_size; + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); if ( p == (uint8_t*)(-1) ) throwf("can't map file, errno=%d", errno); @@ -255,23 +262,23 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib if ( sliceFound ) { uint32_t fileOffset = OSSwapBigToHostInt32(archs[sliceToUse].offset); len = OSSwapBigToHostInt32(archs[sliceToUse].size); - if ( fileOffset+len > info.fileLen ) { + if ( fileOffset+len > stat_buf.st_size ) { // file size was read awhile ago. If file is being written, wait a second to see if big enough now sleep(1); - uint64_t newFileLen = info.fileLen; + int64_t newFileLen = stat_buf.st_size; struct stat statBuffer; if ( stat(info.path, &statBuffer) == 0 ) { newFileLen = statBuffer.st_size; } if ( fileOffset+len > newFileLen ) { throwf("truncated fat file. Slice from %u to %llu is past end of file with length %llu", - fileOffset, fileOffset+len, info.fileLen); + fileOffset, fileOffset+len, stat_buf.st_size); } } // if requested architecture is page aligned within fat file, then remap just that portion of file if ( (fileOffset & 0x00000FFF) == 0 ) { // unmap whole file - munmap((caddr_t)p, info.fileLen); + munmap((caddr_t)p, stat_buf.st_size); // re-map just part we need p = (uint8_t*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, fileOffset); if ( p == (uint8_t*)(-1) ) @@ -355,8 +362,7 @@ ld::File* InputFiles::makeFile(const Options::FileInfo& info, bool indirectDylib archOpts.verboseLoad = _options.whyLoad(); archOpts.logAllFiles = _options.logAllFiles(); // Set ObjSource Kind, libclang_rt is compiler static library - const char* libName = strrchr(info.path, '/'); - if ( (libName != NULL) && (strncmp(libName, "/libclang_rt", 12) == 0) ) + if ( isCompilerSupportLib(info.path) ) archOpts.objOpts.srcKind = ld::relocatable::File::kSourceCompilerArchive; else archOpts.objOpts.srcKind = ld::relocatable::File::kSourceArchive; @@ -485,35 +491,12 @@ void InputFiles::logArchive(ld::File* file) const void InputFiles::logTraceInfo(const char* format, ...) const { - // one time open() of custom LD_TRACE_FILE - static int trace_file = -1; - if ( trace_file == -1 ) { - const char *trace_file_path = _options.traceOutputFile(); - if ( trace_file_path != NULL ) { - trace_file = open(trace_file_path, O_WRONLY | O_APPEND | O_CREAT, 0666); - if ( trace_file == -1 ) - throwf("Could not open or create trace file (errno=%d): %s", errno, trace_file_path); - } - else { - trace_file = fileno(stderr); - } - } - char trace_buffer[MAXPATHLEN * 2]; va_list ap; va_start(ap, format); int length = vsnprintf(trace_buffer, sizeof(trace_buffer), format, ap); va_end(ap); - char* buffer_ptr = trace_buffer; - - while (length > 0) { - ssize_t amount_written = write(trace_file, buffer_ptr, length); - if(amount_written == -1) - /* Failure to write shouldn't fail the build. */ - return; - buffer_ptr += amount_written; - length -= amount_written; - } + _options.writeToTraceFile(trace_buffer, length); } @@ -1232,6 +1215,10 @@ void InputFiles::forEachInitialAtom(ld::File::AtomHandler& handler, ld::Internal // force loaded archives should be in LD_TRACE if ( (info.options.fForceLoad || _options.fullyLoadArchives()) && (_options.traceArchives() || _options.traceEmitJSON()) ) logArchive(archive); + + if ( isCompilerSupportLib(info.path) && (info.options.fForceLoad || _options.fullyLoadArchives()) ) + state.forceLoadCompilerRT = true; + _searchLibraries.push_back(LibraryInfo(archive)); if ( _options.dumpDependencyInfo() ) _options.dumpDependency(Options::depArchive, archive->path()); diff --git a/src/ld/LinkEdit.hpp b/src/ld/LinkEdit.hpp index c4421ef..bde0473 100644 --- a/src/ld/LinkEdit.hpp +++ b/src/ld/LinkEdit.hpp @@ -998,7 +998,7 @@ void ExportInfoAtom::encode() const entry.flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; entry.other = this->_writer.compressedOrdinalForAtom(atom); if ( entry.other == BIND_SPECIAL_DYLIB_SELF ) { - warning("not adding explict export for symbol %s because it is already re-exported from dylib %s", entry.name, atom->file()->path()); + warning("not adding explict export for symbol %s because it is already re-exported from dylib %s", entry.name, atom->safeFilePath()); continue; } if ( atom->isAlias() ) { diff --git a/src/ld/LinkEditClassic.hpp b/src/ld/LinkEditClassic.hpp index ce7c820..fd9098f 100644 --- a/src/ld/LinkEditClassic.hpp +++ b/src/ld/LinkEditClassic.hpp @@ -1824,6 +1824,27 @@ void SectionRelocationsAtom::encodeSectionReloc(ld::Internal::FinalSectio relocs.push_back(reloc1); break; + case ld::Fixup::kindStoreARM64TLVPLoadPageOff12: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPageOff12: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(false); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_TLVP_LOAD_PAGEOFF12); + relocs.push_back(reloc1); + break; + + case ld::Fixup::kindStoreARM64TLVPLoadPage21: + case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadPage21: + reloc1.set_r_address(address); + reloc1.set_r_symbolnum(symbolNum); + reloc1.set_r_pcrel(true); + reloc1.set_r_length(2); + reloc1.set_r_extern(external); + reloc1.set_r_type(ARM64_RELOC_TLVP_LOAD_PAGE21); + relocs.push_back(reloc1); + break; case ld::Fixup::kindStoreLittleEndian64: case ld::Fixup::kindStoreTargetAddressLittleEndian64: diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index faf9c16..d36bc02 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -120,7 +120,6 @@ bool Options::FileInfo::checkFileExists(const Options& options, const char *p) p = path; if ( stat(p, &statBuffer) == 0 ) { if (p != path) path = strdup(p); - fileLen = statBuffer.st_size; modTime = statBuffer.st_mtime; return true; } @@ -145,7 +144,7 @@ Options::Options(int argc, const char* argv[]) fClientName(NULL), fUmbrellaName(NULL), fInitFunctionName(NULL), fDotOutputFile(NULL), fExecutablePath(NULL), fBundleLoader(NULL), fDtraceScriptName(NULL), fSegAddrTablePath(NULL), fMapPath(NULL), - fDyldInstallPath("/usr/lib/dyld"), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), + fDyldInstallPath("/usr/lib/dyld"), fLtoCachePath(NULL), fTempLtoObjectPath(NULL), fOverridePathlibLTO(NULL), fLtoCpu(NULL), fZeroPageSize(ULLONG_MAX), fStackSize(0), fStackAddr(0), fSourceVersion(0), fSDKVersion(0), fExecutableStack(false), fNonExecutableHeap(false), fDisableNonExecutableHeap(false), fMinimumHeaderPad(32), fSegmentAlignment(4096), @@ -195,7 +194,7 @@ Options::Options(int argc, const char* argv[]) 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), fMaxDefaultCommonAlign(0) + fDependencyInfoPath(NULL), fDependencyFileDescriptor(-1), fTraceFileDescriptor(-1), fMaxDefaultCommonAlign(0) { this->checkForClassic(argc, argv); this->parsePreCommandLineEnvironmentSettings(); @@ -213,8 +212,11 @@ Options::Options(int argc, const char* argv[]) Options::~Options() { - if ( fDependencyFileDescriptor != -1 ) - ::close(fDependencyFileDescriptor); + if ( fDependencyFileDescriptor != -1 ) + ::close(fDependencyFileDescriptor); + + if ( fTraceFileDescriptor != -1 ) + ::close(fTraceFileDescriptor); } bool Options::errorBecauseOfWarnings() const @@ -831,13 +833,6 @@ bool Options::findFile(const std::string &path, const std::vector & break; } - // If we found a text-based stub file, check if it should be used. - if ( !tbdInfo.missing() ) { - if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) { - result = tbdInfo; - return true; - } - } FileInfo dylibInfo; { bool found = dylibInfo.checkFileExists(*this, path.c_str()); @@ -845,32 +840,30 @@ bool Options::findFile(const std::string &path, const std::vector & printf("[Logging for XBS]%sfound library: '%s'\n", (found ? " " : " not "), path.c_str()); } - // There is only a text-based stub file. - if ( !tbdInfo.missing() && dylibInfo.missing() ) { - result = tbdInfo; - return true; - } - // There is only a dynamic library file. - else if ( tbdInfo.missing() && !dylibInfo.missing() ) { - result = dylibInfo; - return true; + // There is only a text-based stub file or a dynamic library file. + if ( tbdInfo.missing() != dylibInfo.missing() ) { + result = tbdInfo.missing() ? dylibInfo : tbdInfo; } // There are both - a text-based stub file and a dynamic library file. else if ( !tbdInfo.missing() && !dylibInfo.missing() ) { - // If the files are still in synv we can use and should use the text-based stub file. - if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) { + // Check if we should prefer the text-based stub file (installapi). + if (tapi::LinkerInterfaceFile::shouldPreferTextBasedStubFile(tbdInfo.path)) { + result = tbdInfo; + } + // If the files are still in sync we can use and should use the text-based stub file. + else if (tapi::LinkerInterfaceFile::areEquivalent(tbdInfo.path, dylibInfo.path)) { result = tbdInfo; } // Otherwise issue a warning and fall-back to the dynamic library file. else { warning("text-based stub file %s and library file %s are out of sync. Falling back to library file for linking.", tbdInfo.path, dylibInfo.path); result = dylibInfo; - } - return true; + } else { + return false; } - return false; + return true; } static bool startsWith(const std::string& str, const std::string& prefix) @@ -5661,10 +5654,6 @@ void Options::checkIllegalOptionCombinations() if ( fReverseMapPath != NULL && !fHideSymbols ) { throw "-bitcode_symbol_map can only be used with -bitcode_hide_symbols"; } - if ( fBitcodeKind != kBitcodeProcess && - fOutputKind != Options::kObjectFile ) { - throw "-bitcode_process_mode can only be used together with -r"; - } // auto fix up the process type for strip -S. // when there is only one input and output type is object file, downgrade kBitcodeProcess to kBitcodeAsData. if ( fOutputKind == Options::kObjectFile && fInputFiles.size() == 1 && fBitcodeKind == Options::kBitcodeProcess ) @@ -5885,4 +5874,28 @@ void Options::dumpDependency(uint8_t opcode, const char* path) const } +void Options::writeToTraceFile(const char* buffer, size_t len) const +{ + // one time open() of custom LD_TRACE_FILE + if ( fTraceFileDescriptor == -1 ) { + if ( fTraceOutputFile != NULL ) { + fTraceFileDescriptor = open(fTraceOutputFile, O_WRONLY | O_APPEND | O_CREAT, 0666); + if ( fTraceFileDescriptor == -1 ) + throwf("Could not open or create trace file (errno=%d): %s", errno, fTraceOutputFile); + } + else { + fTraceFileDescriptor = fileno(stderr); + } + } + + while (len > 0) { + ssize_t amountWritten = write(fTraceFileDescriptor, buffer, len); + if ( amountWritten == -1 ) + /* Failure to write shouldn't fail the build. */ + return; + buffer += amountWritten; + len -= amountWritten; + } +} + diff --git a/src/ld/Options.h b/src/ld/Options.h index e257e30..d489fd6 100644 --- a/src/ld/Options.h +++ b/src/ld/Options.h @@ -129,10 +129,30 @@ public: } } + static void userReadableSwiftVersion(uint8_t value, char versionString[64]) + { + switch (value) { + case 1: + strcpy(versionString, "1.0"); + break; + case 2: + strcpy(versionString, "1.1"); + break; + case 3: + strcpy(versionString, "2.0"); + break; + case 4: + strcpy(versionString, "3.0"); + break; + default: + sprintf(versionString, "unknown ABI version 0x%02X", value); + } + } + + class FileInfo { public: const char* path; - uint64_t fileLen; time_t modTime; LibraryOptions options; ld::File::Ordinal ordinal; @@ -145,11 +165,10 @@ 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), fileLen(other.fileLen), 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), inputFileSlot(-1) { ((FileInfo&)other).path = NULL; }; FileInfo &operator=(FileInfo other) { std::swap(path, other.path); - std::swap(fileLen, other.fileLen); std::swap(modTime, other.modTime); std::swap(options, other.options); std::swap(ordinal, other.ordinal); @@ -160,10 +179,10 @@ public: } // Create an empty FileInfo. The path can be set implicitly by checkFileExists(). - FileInfo() : path(NULL), fileLen(0), modTime(-1), options(), fromFileList(false) {}; + FileInfo() : path(NULL), modTime(-1), options(), fromFileList(false) {}; // Create a FileInfo for a specific path, but does not stat the file. - FileInfo(const char *_path) : path(strdup(_path)), fileLen(0), modTime(-1), options(), fromFileList(false) {}; + FileInfo(const char *_path) : path(strdup(_path)), modTime(-1), options(), fromFileList(false) {}; ~FileInfo() { if (path) ::free((void*)path); } @@ -484,6 +503,7 @@ public: uint8_t maxDefaultCommonAlign() const { return fMaxDefaultCommonAlign; } bool hasDataSymbolMoves() const { return !fSymbolsMovesData.empty(); } bool hasCodeSymbolMoves() const { return !fSymbolsMovesCode.empty(); } + void writeToTraceFile(const char* buffer, size_t len) const; static uint32_t parseVersionNumber32(const char*); @@ -795,6 +815,7 @@ private: const char* fPipelineFifo; const char* fDependencyInfoPath; mutable int fDependencyFileDescriptor; + mutable int fTraceFileDescriptor; uint8_t fMaxDefaultCommonAlign; }; diff --git a/src/ld/OutputFile.cpp b/src/ld/OutputFile.cpp index 732eb54..3b775c9 100644 --- a/src/ld/OutputFile.cpp +++ b/src/ld/OutputFile.cpp @@ -2223,6 +2223,7 @@ void OutputFile::applyFixUps(ld::Internal& state, uint64_t mhAddress, const ld:: // Leave ADRP as-is set32LE(infoB.instructionContent, makeNOP()); ldrInfoC.offset += addInfoB.addend; + ldrInfoC.baseReg = adrpInfoA.destReg; set32LE(infoC.instructionContent, makeLoadOrStore(ldrInfoC)); if ( _options.verboseOptimizationHints() ) fprintf(stderr, "adrp-add-ldr at 0x%08llX T2 transformed to ADRP/LDR \n", infoC.instructionAddress); @@ -2627,7 +2628,7 @@ void OutputFile::writeAtoms(ld::Internal& state, uint8_t* wholeBuffer) } catch (const char* msg) { if ( atom->file() != NULL ) - throwf("%s in '%s' from %s", msg, atom->name(), atom->file()->path()); + throwf("%s in '%s' from %s", msg, atom->name(), atom->safeFilePath()); else throwf("%s in '%s'", msg, atom->name()); } @@ -2778,7 +2779,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) // Don't use mmap on non-hfs volumes struct statfs fsInfo; if ( statfs(_options.outputFilePath(), &fsInfo) != -1 ) { - if ( strcmp(fsInfo.f_fstypename, "hfs") == 0) { + if ( (strcmp(fsInfo.f_fstypename, "hfs") == 0) || (strcmp(fsInfo.f_fstypename, "apfs") == 0) ) { (void)unlink(_options.outputFilePath()); outputIsMappableFile = true; } @@ -2802,7 +2803,7 @@ void OutputFile::writeOutputFile(ld::Internal& state) end[1] = '\0'; struct statfs fsInfo; if ( statfs(dirPath, &fsInfo) != -1 ) { - if ( strcmp(fsInfo.f_fstypename, "hfs") == 0) { + if ( (strcmp(fsInfo.f_fstypename, "hfs") == 0) || (strcmp(fsInfo.f_fstypename, "apfs") == 0) ) { outputIsMappableFile = true; } } @@ -4037,17 +4038,17 @@ void OutputFile::noteTextReloc(const ld::Atom* atom, const ld::Atom* target) warning("PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, " "but used in %s from %s. " "To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie", - atom->name(), atom->file()->path()); + atom->name(), atom->safeFilePath()); } } this->pieDisabled = true; } else if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) ) { - throwf("illegal text-relocoation (direct reference) to (global,weak) %s in %s from %s in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); + throwf("illegal text-relocoation (direct reference) to (global,weak) %s in %s from %s in %s", target->name(), target->safeFilePath(), atom->name(), atom->safeFilePath()); } else { if ( (target->file() != NULL) && (atom->file() != NULL) ) - throwf("illegal text-relocation to '%s' in %s from '%s' in %s", target->name(), target->file()->path(), atom->name(), atom->file()->path()); + throwf("illegal text-relocation to '%s' in %s from '%s' in %s", target->name(), target->safeFilePath(), atom->name(), atom->safeFilePath()); else throwf("illegal text reloc in '%s' to '%s'", atom->name(), target->name()); } @@ -4082,7 +4083,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s const char* demangledName = strdup(_options.demangleSymbol(atom->name())); warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. " "This was likely caused by different translation units being compiled with different visibility settings.", - demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path()); + demangledName, atom->safeFilePath(), _options.demangleSymbol(target->name()), target->safeFilePath()); } return; } @@ -4111,7 +4112,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s const char* demangledName = strdup(_options.demangleSymbol(atom->name())); warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. " "This was likely caused by different translation units being compiled with different visibility settings.", - demangledName, atom->file()->path(), _options.demangleSymbol(target->name()), target->file()->path()); + demangledName, atom->safeFilePath(), _options.demangleSymbol(target->name()), target->safeFilePath()); } return; } @@ -4124,6 +4125,7 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s if ( target == NULL ) return; + const uint64_t pointerSize = (_options.architecture() & CPU_ARCH_ABI64) ? 8 : 4; bool inReadOnlySeg = ((_options.initialSegProtection(sect->segmentName()) & VM_PROT_WRITE) == 0); bool needsRebase = false; bool needsBinding = false; @@ -4266,12 +4268,21 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s if ( (targetAddress+checkAddend) > sctEnd ) { warning("data symbol %s from %s has pointer to %s + 0x%08llX. " "That large of an addend may disable %s from being put in the dyld shared cache.", - atom->name(), atom->file()->path(), target->name(), addend, _options.installPath() ); + atom->name(), atom->safeFilePath(), target->name(), addend, _options.installPath() ); } } } } } + if ( ((address & (pointerSize-1)) != 0) && (rebaseType == REBASE_TYPE_POINTER) ) { + if ( (pointerSize == 8) && ((address & 7) == 4) ) { + // for now, don't warning about 8-byte pointers only 4-byte aligned + } + else { + warning("pointer not aligned at address 0x%llX (%s + %lld from %s)", + address, atom->name(), (address - atom->finalAddress()), atom->safeFilePath()); + } + } _rebaseInfo.push_back(RebaseInfo(rebaseType, address)); } if ( needsBinding ) { @@ -4279,6 +4290,15 @@ void OutputFile::addDyldInfo(ld::Internal& state, ld::Internal::FinalSection* s noteTextReloc(atom, target); sect->hasExternalRelocs = true; // so dyld knows to change permissions on __TEXT segment } + if ( ((address & (pointerSize-1)) != 0) && (type == BIND_TYPE_POINTER) ) { + if ( (pointerSize == 8) && ((address & 7) == 4) ) { + // for now, don't warning about 8-byte pointers only 4-byte aligned + } + else { + warning("pointer not aligned at address 0x%llX (%s + %lld from %s)", + address, atom->name(), (address - atom->finalAddress()), atom->safeFilePath()); + } + } _bindingInfo.push_back(BindingInfo(type, this->compressedOrdinalForAtom(target), target->name(), weak_import, address, addend)); } if ( needsLazyBinding ) { @@ -4330,14 +4350,19 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio assert(minusTarget->definition() != ld::Atom::definitionProxy); assert(target != NULL); assert(target->definition() != ld::Atom::definitionProxy); - // make sure target is not global and weak - if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) - && (atom->section().type() != ld::Section::typeCFI) - && (atom->section().type() != ld::Section::typeDtraceDOF) - && (atom->section().type() != ld::Section::typeUnwindInfo) - && (minusTarget != target) ) { - // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols - throwf("bad codegen, pointer diff in %s to global weak symbol %s", atom->name(), target->name()); + // check if target of pointer-diff is global and weak + if ( (target->scope() == ld::Atom::scopeGlobal) && (target->combine() == ld::Atom::combineByName) && (target->definition() == ld::Atom::definitionRegular) ) { + if ( (atom->section().type() == ld::Section::typeCFI) + || (atom->section().type() == ld::Section::typeDtraceDOF) + || (atom->section().type() == ld::Section::typeUnwindInfo) ) { + // ok for __eh_frame and __uwind_info to use pointer diffs to global weak symbols + return; + } + // Have direct reference to weak-global. This should be an indrect reference + const char* demangledName = strdup(_options.demangleSymbol(atom->name())); + warning("direct access in function '%s' from file '%s' to global weak symbol '%s' from file '%s' means the weak symbol cannot be overridden at runtime. " + "This was likely caused by different translation units being compiled with different visibility settings.", + demangledName, atom->safeFilePath(), _options.demangleSymbol(target->name()), target->safeFilePath()); } return; } @@ -4443,14 +4468,14 @@ void OutputFile::addClassicRelocs(ld::Internal& state, ld::Internal::FinalSectio case ld::Fixup::kindStoreThumbLow16: // no way to encode rebasing of binding for these instructions if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) ) - throwf("no supported runtime lo16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name()); + throwf("no supported runtime lo16 relocation in %s from %s to %s", atom->name(), atom->safeFilePath(), target->name()); break; case ld::Fixup::kindStoreARMHigh16: case ld::Fixup::kindStoreThumbHigh16: // no way to encode rebasing of binding for these instructions if ( _options.outputSlidable() || (target->definition() == ld::Atom::definitionProxy) ) - throwf("no supported runtime hi16 relocation in %s from %s to %s", atom->name(), atom->file()->path(), target->name()); + throwf("no supported runtime hi16 relocation in %s from %s to %s", atom->name(), atom->safeFilePath(), target->name()); break; default: @@ -5052,6 +5077,14 @@ void OutputFile::writeMapFile(ld::Internal& state) } } +static std::string realPathString(const char* path) +{ + char realName[MAXPATHLEN]; + if ( realpath(path, realName) != NULL ) + return realName; + return path; +} + void OutputFile::writeJSONEntry(ld::Internal& state) { if ( _options.traceEmitJSON() && (_options.UUIDMode() != Options::kUUIDNone) && (_options.traceOutputFile() != NULL) ) { @@ -5099,7 +5132,7 @@ void OutputFile::writeJSONEntry(ld::Internal& state) if (dynamicList.size() > 0) { jsonEntry += ",\"dynamic\":["; for (const ld::dylib::File* dylib : dynamicList) { - jsonEntry += "\"" + std::string(dylib->path()) + "\""; + jsonEntry += "\"" + realPathString(dylib->path()) + "\""; if ((dylib != dynamicList.back())) { jsonEntry += ","; } @@ -5110,7 +5143,7 @@ void OutputFile::writeJSONEntry(ld::Internal& state) if (upwardList.size() > 0) { jsonEntry += ",\"upward-dynamic\":["; for (const ld::dylib::File* dylib : upwardList) { - jsonEntry += "\"" + std::string(dylib->path()) + "\""; + jsonEntry += "\"" + realPathString(dylib->path()) + "\""; if ((dylib != upwardList.back())) { jsonEntry += ","; } @@ -5121,7 +5154,7 @@ void OutputFile::writeJSONEntry(ld::Internal& state) if (reexportList.size() > 0) { jsonEntry += ",\"re-exports\":["; for (const ld::dylib::File* dylib : reexportList) { - jsonEntry += "\"" + std::string(dylib->path()) + "\""; + jsonEntry += "\"" + realPathString(dylib->path()) + "\""; if ((dylib != reexportList.back())) { jsonEntry += ","; } @@ -5132,18 +5165,23 @@ void OutputFile::writeJSONEntry(ld::Internal& state) if (state.archivePaths.size() > 0) { jsonEntry += ",\"archives\":["; for (const std::string& archivePath : state.archivePaths) { - jsonEntry += "\"" + std::string(archivePath) + "\""; + jsonEntry += "\"" + realPathString(archivePath.c_str()) + "\""; if ((archivePath != state.archivePaths.back())) { jsonEntry += ","; } } jsonEntry += "]"; } + + if (state.bundleLoader != NULL) { + jsonEntry += ",\"bundle-loader\":"; + jsonEntry += "\"" + realPathString(state.bundleLoader->path()) + "\""; + } + jsonEntry += "}\n"; // Write the JSON entry to the trace file. - std::ofstream out(_options.traceOutputFile(), ios::app); - out << jsonEntry; + _options.writeToTraceFile(jsonEntry.c_str(), jsonEntry.size()); } } diff --git a/src/ld/Resolver.cpp b/src/ld/Resolver.cpp index 1c8f16e..24af5f8 100644 --- a/src/ld/Resolver.cpp +++ b/src/ld/Resolver.cpp @@ -337,25 +337,6 @@ void Resolver::doLinkerOption(const std::vector& linkerOption, cons } } -static void userReadableSwiftVersion(uint8_t value, char versionString[64]) -{ - switch (value) { - case 1: - strcpy(versionString, "1.0"); - break; - case 2: - strcpy(versionString, "1.1"); - break; - case 3: - strcpy(versionString, "2.0"); - break; - case 4: - strcpy(versionString, "3.0"); - break; - default: - sprintf(versionString, "unknown ABI version 0x%02X", value); - } -} void Resolver::doFile(const ld::File& file) { @@ -378,9 +359,11 @@ void Resolver::doFile(const ld::File& file) // Resolve bitcode section in the object file if ( _options.bundleBitcode() ) { if ( objFile->getBitcode() == NULL ) { - // No bitcode section, figure out if the object file comes from LTO/compiler static library - if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO && - objFile->sourceKind() != ld::relocatable::File::kSourceCompilerArchive ) { + // Handle the special case for compiler_rt objects. Add the file to the list to be process. + if ( objFile->sourceKind() == ld::relocatable::File::kSourceCompilerArchive ) { + _internal.filesFromCompilerRT.push_back(objFile); + } else if (objFile->sourceKind() != ld::relocatable::File::kSourceLTO ) { + // No bitcode section, figure out if the object file comes from LTO/compiler static library switch ( _options.platform() ) { case Options::kPlatformOSX: case Options::kPlatformUnknown: @@ -467,8 +450,8 @@ void Resolver::doFile(const ld::File& file) else if ( file.swiftVersion() != _internal.swiftVersion ) { char fileVersion[64]; char otherVersion[64]; - userReadableSwiftVersion(file.swiftVersion(), fileVersion); - userReadableSwiftVersion(_internal.swiftVersion, otherVersion); + 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); @@ -638,8 +621,8 @@ void Resolver::doFile(const ld::File& file) else if ( file.swiftVersion() != _internal.swiftVersion ) { char fileVersion[64]; char otherVersion[64]; - userReadableSwiftVersion(file.swiftVersion(), fileVersion); - userReadableSwiftVersion(_internal.swiftVersion, otherVersion); + 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); @@ -707,14 +690,14 @@ void Resolver::doAtom(const ld::Atom& atom) } else if ( _options.outputKind() == Options::kDynamicLibrary ) { if ( atom.file() != NULL ) - warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.file()->path()); + warning("target OS does not support re-exporting symbol %s from %s\n", _options.demangleSymbol(name), atom.safeFilePath()); else warning("target OS does not support re-exporting symbol %s\n", _options.demangleSymbol(name)); } } else { if ( atom.file() != NULL ) - warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.file()->path()); + warning("cannot export hidden symbol %s from %s", _options.demangleSymbol(name), atom.safeFilePath()); else warning("cannot export hidden symbol %s", _options.demangleSymbol(name)); } @@ -726,7 +709,7 @@ void Resolver::doAtom(const ld::Atom& atom) (const_cast(&atom))->setScope(ld::Atom::scopeGlobal); } else { - throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path()); + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.safeFilePath()); } } break; @@ -737,7 +720,7 @@ void Resolver::doAtom(const ld::Atom& atom) //fprintf(stderr, "demote %s to hidden\n", name); } if ( _options.canReExportSymbols() && _options.shouldReExport(name) ) { - throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.file()->path()); + throwf("requested re-export symbol %s is not from a dylib, but from %s\n", _options.demangleSymbol(name), atom.safeFilePath()); } break; } @@ -995,12 +978,12 @@ void Resolver::markLive(const ld::Atom& atom, WhyLiveBackChain* previous) //fprintf(stderr, "markLive(%p) %s\n", &atom, atom.name()); // if -why_live cares about this symbol, then dump chain if ( (previous->referer != NULL) && _options.printWhyLive(atom.name()) ) { - fprintf(stderr, "%s from %s\n", atom.name(), atom.file()->path()); + fprintf(stderr, "%s from %s\n", atom.name(), atom.safeFilePath()); int depth = 1; for(WhyLiveBackChain* p = previous; p != NULL; p = p->previous, ++depth) { for(int i=depth; i > 0; --i) fprintf(stderr, " "); - fprintf(stderr, "%s from %s\n", p->referer->name(), p->referer->file()->path()); + fprintf(stderr, "%s from %s\n", p->referer->name(), p->referer->safeFilePath()); } } @@ -1395,11 +1378,11 @@ bool Resolver::printReferencedBy(const char* name, SymbolTable::IndirectBindingS ++foundReferenceCount; } else if ( atom->contentType() == ld::Atom::typeCFI ) { - fprintf(stderr, " Dwarf Exception Unwind Info (__eh_frame) in %s\n", pathLeafName(atom->file()->path())); + fprintf(stderr, " Dwarf Exception Unwind Info (__eh_frame) in %s\n", pathLeafName(atom->safeFilePath())); ++foundReferenceCount; } else { - fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->file()->path())); + fprintf(stderr, " %s in %s\n", _options.demangleSymbol(atom->name()), pathLeafName(atom->safeFilePath())); ++foundReferenceCount; break; // if undefined used twice in a function, only show first } @@ -1744,6 +1727,7 @@ void Resolver::linkTimeOptimize() optOpt.pie = _options.positionIndependentExecutable(); optOpt.mainExecutable = _options.linkingMainExecutable();; optOpt.staticExecutable = (_options.outputKind() == Options::kStaticExecutable); + optOpt.preload = (_options.outputKind() == Options::kPreload); optOpt.relocatable = (_options.outputKind() == Options::kObjectFile); optOpt.allowTextRelocs = _options.allowTextRelocs(); optOpt.linkerDeadStripping = _options.deadCodeStrip(); @@ -1753,7 +1737,7 @@ void Resolver::linkTimeOptimize() optOpt.armUsesZeroCostExceptions = _options.armUsesZeroCostExceptions(); optOpt.simulator = _options.targetIOSSimulator(); optOpt.ignoreMismatchPlatform = ((_options.outputKind() == Options::kPreload) || (_options.outputKind() == Options::kStaticExecutable)); - optOpt.bitcodeBundle = _options.bundleBitcode(); + optOpt.bitcodeBundle = (_options.bundleBitcode() && (_options.bitcodeKind() != Options::kBitcodeMarker)); optOpt.maxDefaultCommonAlignment = _options.maxDefaultCommonAlign(); optOpt.arch = _options.architecture(); optOpt.mcpu = _options.mcpuLTO(); @@ -1801,8 +1785,15 @@ void Resolver::linkTimeOptimize() // if -dead_strip on command line if ( _options.deadCodeStrip() ) { - // clear liveness bit + // run through all atoms again and make live_section LTO atoms are preserved from dead_stripping if needed + _dontDeadStripIfReferencesLive.clear(); for (std::vector::const_iterator it=_atoms.begin(); it != _atoms.end(); ++it) { + const ld::Atom* atom = *it; + if ( atom->dontDeadStripIfReferencesLive() ) { + _dontDeadStripIfReferencesLive.push_back(atom); + } + + // clear liveness bit (const_cast(*it))->setLive((*it)->dontDeadStrip()); } // and re-compute dead code diff --git a/src/ld/SymbolTable.cpp b/src/ld/SymbolTable.cpp index 4be0c5e..3c12a77 100644 --- a/src/ld/SymbolTable.cpp +++ b/src/ld/SymbolTable.cpp @@ -132,7 +132,7 @@ void SymbolTable::addDuplicateSymbol(const char *name, const ld::Atom *atom) // check if file is already in the list, add it if not bool found = false; for (DuplicatedSymbolAtomList::iterator it = atoms->begin(); !found && it != atoms->end(); it++) - if (strcmp((*it)->file()->path(), atom->file()->path()) == 0) + if (strcmp((*it)->safeFilePath(), atom->safeFilePath()) == 0) found = true; if (!found) atoms->push_back(atom); @@ -158,7 +158,7 @@ void SymbolTable::checkDuplicateSymbols() const foundDuplicate = true; fprintf(stderr, "duplicate symbol %s in:\n", symbolIt->first); for (DuplicatedSymbolAtomList::iterator atomIt = atoms->begin(); atomIt != atoms->end(); atomIt++) { - fprintf(stderr, " %s\n", (*atomIt)->file()->path()); + fprintf(stderr, " %s\n", (*atomIt)->safeFilePath()); } } } @@ -284,18 +284,18 @@ private: case Options::kCommonsIgnoreDylibs: if ( _options.warnCommons() ) warning("using common symbol %s from %s and ignoring defintion from dylib %s", - proxy.name(), proxy.file()->path(), dylib.file()->path()); + proxy.name(), proxy.safeFilePath(), dylib.safeFilePath()); pickAtom(dylib); break; case Options::kCommonsOverriddenByDylibs: if ( _options.warnCommons() ) warning("replacing common symbol %s from %s with true definition from dylib %s", - proxy.name(), proxy.file()->path(), dylib.file()->path()); + proxy.name(), proxy.safeFilePath(), dylib.safeFilePath()); pickAtom(proxy); break; case Options::kCommonsConflictsDylibsError: throwf("common symbol %s from %s conflicts with defintion from dylib %s", - proxy.name(), proxy.file()->path(), dylib.file()->path()); + proxy.name(), proxy.safeFilePath(), dylib.safeFilePath()); } } @@ -307,7 +307,7 @@ private: } else if ( _atomB.combine() == ld::Atom::combineByName ) { pickAtomA(); } else { - throwf("symbol %s exported from both %s and %s\n", _atomA.name(), _atomA.file()->path(), _atomB.file()->path()); + throwf("symbol %s exported from both %s and %s\n", _atomA.name(), _atomA.safeFilePath(), _atomB.safeFilePath()); } } @@ -322,10 +322,8 @@ private: break; case ld::Atom::definitionTentative: if ( _atomB.size() > _atomA.size() ) { - const char* atomApath = (_atomA.file() != NULL) ? _atomA.file()->path() : ""; - const char* atomBpath = (_atomB.file() != NULL) ? _atomB.file()->path() : ""; warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'", - _atomA.name(), _atomB.size(), atomBpath, _atomA.size(), atomApath); + _atomA.name(), _atomB.size(), _atomB.safeFilePath(), _atomA.size(), _atomA.safeFilePath()); } pickAtomA(); break; @@ -342,10 +340,8 @@ private: switch (_atomB.definition()) { case ld::Atom::definitionRegular: if ( _atomA.size() > _atomB.size() ) { - const char* atomApath = (_atomA.file() != NULL) ? _atomA.file()->path() : ""; - const char* atomBpath = (_atomB.file() != NULL) ? _atomB.file()->path() : ""; warning("tentative definition of '%s' with size %llu from '%s' is being replaced by real definition of smaller size %llu from '%s'", - _atomA.name(), _atomA.size(),atomApath, _atomB.size(), atomBpath); + _atomA.name(), _atomA.size(),_atomA.safeFilePath(), _atomB.size(), _atomB.safeFilePath()); } pickAtomB(); break; @@ -505,7 +501,7 @@ bool SymbolTable::add(const ld::Atom& atom, bool ignoreDuplicates) void SymbolTable::markCoalescedAway(const ld::Atom* atom) { // remove this from list of all atoms used - //fprintf(stderr, "markCoalescedAway(%p) from %s\n", atom, atom->file()->path()); + //fprintf(stderr, "markCoalescedAway(%p) from %s\n", atom, atom->safeFilePath()); (const_cast(atom))->setCoalescedAway(); // @@ -893,7 +889,7 @@ void SymbolTable::printStatistics() //ReferencesHash obj; //for(ReferencesHashToSlot::iterator it=_byReferencesTable.begin(); it != _byReferencesTable.end(); ++it) { // if ( obj.operator()(it->first) == 0x2F3AC0EAC744EA70 ) { - // fprintf(stderr, "hash=0x2F3AC0EAC744EA70 for %p %s from %s\n", it->first, it->first->name(), it->first->file()->path()); + // fprintf(stderr, "hash=0x2F3AC0EAC744EA70 for %p %s from %s\n", it->first, it->first->name(), it->first->safeFilePath()); // // } //} diff --git a/src/ld/ld.hpp b/src/ld/ld.hpp index 0db1b7e..8351e3f 100644 --- a/src/ld/ld.hpp +++ b/src/ld/ld.hpp @@ -225,6 +225,7 @@ namespace relocatable { virtual bool hasLongBranchStubs() { return false; } virtual LinkerOptionsList* linkerOptions() const = 0; virtual SourceKind sourceKind() const { return kSourceUnknown; } + virtual const uint8_t* fileContent() const { return nullptr; } }; } // namespace relocatable @@ -786,6 +787,7 @@ public: _definition = a._definition; _combine = a._combine; _dontDeadStrip = a._dontDeadStrip; + _dontDeadStripIfRefLive = a._dontDeadStripIfRefLive; _thumb = a._thumb; _autoHide = a._autoHide; _contentType = a._contentType; @@ -797,6 +799,14 @@ public: _weakImportState = a._weakImportState; } + const char* safeFilePath() const { + const File* f = this->file(); + if ( f != NULL ) + return f->path(); + else + return ""; + } + protected: enum AddressMode { modeSectionOffset, modeFinalAddress }; @@ -831,7 +841,7 @@ public: }; - + // utility classes for using std::unordered_map with c-strings struct CStringHash { size_t operator()(const char* __s) const { @@ -892,7 +902,8 @@ public: hasThreadLocalVariableDefinitions(false), hasWeakExternalSymbols(false), someObjectHasOptimizationHints(false), - dropAllBitcode(false), embedMarkerOnly(false) { } + dropAllBitcode(false), embedMarkerOnly(false), + forceLoadCompilerRT(false) { } std::vector sections; std::vector dylibs; @@ -905,6 +916,7 @@ public: CStringSet linkerOptionFrameworks; std::vector indirectBindingTable; std::vector filesWithBitcode; + std::vector filesFromCompilerRT; std::vector deadAtoms; std::unordered_set allUndefProxies; const ld::dylib::File* bundleLoader; @@ -927,6 +939,7 @@ public: bool someObjectHasOptimizationHints; bool dropAllBitcode; bool embedMarkerOnly; + bool forceLoadCompilerRT; std::vector ltoBitcodePath; }; diff --git a/src/ld/parsers/libunwind/DwarfInstructions.hpp b/src/ld/parsers/libunwind/DwarfInstructions.hpp index c14c38f..711def8 100644 --- a/src/ld/parsers/libunwind/DwarfInstructions.hpp +++ b/src/ld/parsers/libunwind/DwarfInstructions.hpp @@ -275,6 +275,10 @@ const char* DwarfInstructions::parseCFIs(A& addressSpace, pint_t ehSectionS break; } } + if ( pcRange == 0 ) { + warn(ref, pcStart, "FDE found for zero size function"); + break; + } //fprintf(stderr, "FDE for func at 0x%08X, alreadyHaveCU=%d\n", (uint32_t)entry->u.fdeInfo.function.targetAddress, alreadyHaveCU); if ( alreadyHaveCU && !forceDwarfConversion ) { if ( keepDwarfWhichHasCU ) diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index 650fd4e..6915ae6 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -125,7 +126,7 @@ public: static bool sHasTriedLocalContext; bool mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule); #if LTO_API_VERSION >= 18 - void addToThinGenerator(thinlto_code_gen_t generator); + void addToThinGenerator(thinlto_code_gen_t generator, int id); #endif private: friend class Atom; @@ -291,12 +292,14 @@ private: std::vector& newAtoms, std::vector& additionalUndefines); +#if LTO_API_VERSION >= 18 static thinlto_code_gen_t init_thinlto_codegen(const std::vector& files, const std::vector& allAtoms, ld::Internal& state, const OptimizeOptions& options, CStringToAtom& deadllvmAtoms, CStringToAtom& llvmAtoms); +#endif static std::vector _s_files; static bool _s_llvmOptionsProcessed; @@ -543,9 +546,11 @@ bool File::mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule) { } #if LTO_API_VERSION >= 18 -void File::addToThinGenerator(thinlto_code_gen_t generator) { +void File::addToThinGenerator(thinlto_code_gen_t generator, int id) { assert(!_module && "Expected module to be disposed"); - ::thinlto_codegen_add_module(generator, _path, (const char *)_content, _contentLength); + std::string pathWithID = _path; + pathWithID += std::to_string(id); + ::thinlto_codegen_add_module(generator, pathWithID.c_str(), (const char *)_content, _contentLength); } #endif @@ -761,6 +766,12 @@ static lto_codegen_model getCodeModel(const OptimizeOptions& options) { else return LTO_CODEGEN_PIC_MODEL_STATIC; } + else if ( options.preload ) { + if ( options.pie ) + return LTO_CODEGEN_PIC_MODEL_DYNAMIC; + else + return LTO_CODEGEN_PIC_MODEL_STATIC; + } else { if ( options.pie ) return LTO_CODEGEN_PIC_MODEL_DYNAMIC; @@ -1028,6 +1039,7 @@ bool Parser::optimizeLTO(const std::vector files, return true; } +#if LTO_API_VERSION >= 18 // Create the ThinLTO codegenerator thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector& files, const std::vector& allAtoms, @@ -1178,6 +1190,7 @@ thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector& return thingenerator; } +#endif // Full LTO processing bool Parser::optimizeThinLTO(const std::vector& files, @@ -1210,9 +1223,10 @@ bool Parser::optimizeThinLTO(const std::vector& files, ld::File::Ordinal lastOrdinal; + int FileId = 0; for (auto *f : files) { if ( logBitcodeFiles) fprintf(stderr, "thinlto_codegen_add_module(%s)\n", f->path()); - f->addToThinGenerator(thingenerator); + f->addToThinGenerator(thingenerator, FileId++); lastOrdinal = f->ordinal(); } @@ -1259,10 +1273,26 @@ bool Parser::optimizeThinLTO(const std::vector& files, thinlto_codegen_set_codegen_only(thingenerator, true); #endif + // If object_path_lto is used, we switch to a file-based API: libLTO will + // generate the files on disk and we'll map them on-demand. + +#if LTO_API_VERSION >= 21 + bool useFileBasedAPI = (options.tmpObjectFilePath && ::lto_api_version() >= 21); + if ( useFileBasedAPI ) + thinlto_set_generated_objects_dir(thingenerator, options.tmpObjectFilePath); +#endif + // run code generator thinlto_codegen_process(thingenerator); - auto numObjects = thinlto_module_get_num_objects(thingenerator); - if (!numObjects) + + unsigned numObjects; +#if LTO_API_VERSION >= 21 + if ( useFileBasedAPI ) + numObjects = thinlto_module_get_num_object_files(thingenerator); + else +#endif + numObjects = thinlto_module_get_num_objects(thingenerator); + if ( numObjects == 0 ) throwf("could not do ThinLTO codegen (thinlto_codegen_process didn't produce any object): '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); // if requested, save off objects files @@ -1277,38 +1307,69 @@ bool Parser::optimizeThinLTO(const std::vector& files, if ( fd != -1 ) { ::write(fd, machOFile.Buffer, machOFile.Size); ::close(fd); - } else { + } + else { warning("unable to write temporary ThinLTO output: %s", tempMachoPath.c_str()); } } } - // mach-o parsing is done in-memory, but need path for debug notes std::string macho_dirpath = "/tmp/thinlto.o"; if ( options.tmpObjectFilePath != NULL ) { macho_dirpath = options.tmpObjectFilePath; struct stat statBuffer; if( stat(macho_dirpath.c_str(), &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) { + unlink(macho_dirpath.c_str()); if ( mkdir(macho_dirpath.c_str(), 0700) !=0 ) { warning("unable to create ThinLTO output directory for temporary object files: %s", macho_dirpath.c_str()); } } } + auto get_thinlto_buffer_or_load_file = [&] (unsigned ID) { +#if LTO_API_VERSION >= 21 + if ( useFileBasedAPI ) { + const char* path = thinlto_module_get_object_file(thingenerator, ID); + // map in whole file + struct stat stat_buf; + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open thinlto file '%s', errno=%d", path, errno); + if ( ::fstat(fd, &stat_buf) != 0 ) + throwf("fstat thinlto file '%s' failed, errno=%d\n", path, errno); + size_t len = stat_buf.st_size; + if ( len < 20 ) + throwf("ThinLTO file '%s' too small (length=%zu)", path, len); + const char* p = (const char*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == (const char*)(-1) ) + throwf("can't map file, errno=%d", errno); + ::close(fd); + return LTOObjectBuffer{ p, len }; + } +#endif + return thinlto_module_get_object(thingenerator, ID); + }; + auto ordinal = ld::File::Ordinal::LTOOrdinal().nextFileListOrdinal(); for (unsigned bufID = 0; bufID < numObjects; ++bufID) { - auto machOFile = thinlto_module_get_object(thingenerator, bufID); + auto machOFile = get_thinlto_buffer_or_load_file(bufID); if (!machOFile.Size) { warning("Ignoring empty buffer generated by ThinLTO"); continue; } // mach-o parsing is done in-memory, but need path for debug notes - std::string tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o"; - - // if needed, save temp mach-o file to specific location - if ( options.tmpObjectFilePath != NULL ) { + std::string tmp_path; +#if LTO_API_VERSION >= 21 + if ( useFileBasedAPI ) { + tmp_path = thinlto_module_get_object_file(thingenerator, bufID); + } + else +#endif + if ( options.tmpObjectFilePath != NULL) { + tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o"; + // if needed, save temp mach-o file to specific location int fd = ::open(tmp_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666); if ( fd != -1) { ::write(fd, (const uint8_t *)machOFile.Buffer, machOFile.Size); @@ -1401,6 +1462,10 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) CStringToAtom::const_iterator pos = _llvmAtoms.find(name); if ( pos != _llvmAtoms.end() ) { // 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); + (const_cast(&machoAtom))->setScope(ld::Atom::scopeLinkageUnit); + } pos->second->setCompiledAtom(machoAtom); _lastProxiedAtom = &machoAtom; _lastProxiedFile = pos->second->file(); @@ -1420,6 +1485,25 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) else { // Don't pass it back as a new atom if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p (name=%s)\n", &machoAtom, llvmAtom->second, machoAtom.name()); + if ( llvmAtom->second->coalescedAway() ) { + if (log) fprintf(stderr, "AtomSyncer: dead coalesced atom %s\n", machoAtom.name()); + // + // We told libLTO to keep a weak atom that will replaced by an native mach-o atom. + // We also need to remove any atoms directly dependent on this (FDE, LSDA). + for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(), fend=machoAtom.fixupsEnd(); fit != fend; ++fit) { + switch ( fit->kind ) { + case ld::Fixup::kindNoneGroupSubordinate: + case ld::Fixup::kindNoneGroupSubordinateFDE: + case ld::Fixup::kindNoneGroupSubordinateLSDA: + assert(fit->binding == ld::Fixup::bindingDirectlyBound); + (const_cast(fit->u.target))->setCoalescedAway(); + if (log) fprintf(stderr, "AtomSyncer: mark coalesced-away subordinate atom %s\n", fit->u.target->name()); + break; + default: + break; + } + } + } } } else diff --git a/src/ld/parsers/lto_file.h b/src/ld/parsers/lto_file.h index b760177..b582a8b 100644 --- a/src/ld/parsers/lto_file.h +++ b/src/ld/parsers/lto_file.h @@ -58,6 +58,7 @@ struct OptimizeOptions { bool pie; bool mainExecutable; bool staticExecutable; + bool preload; bool relocatable; bool allowTextRelocs; bool linkerDeadStripping; diff --git a/src/ld/parsers/macho_relocatable_file.cpp b/src/ld/parsers/macho_relocatable_file.cpp index 776e695..c4ecd36 100644 --- a/src/ld/parsers/macho_relocatable_file.cpp +++ b/src/ld/parsers/macho_relocatable_file.cpp @@ -111,7 +111,7 @@ public: virtual ld::Bitcode* getBitcode() const { return _bitcode.get(); } virtual SourceKind sourceKind() const { return _srcKind; } - const uint8_t* fileContent() { return _fileContent; } + virtual const uint8_t* fileContent() const { return _fileContent; } private: friend class Atom; friend class Section; @@ -1715,11 +1715,14 @@ typename A::P::uint_t Parser::realAddr(typename A::P::uint_t addr) #define STACK_ALLOC_IF_SMALL(_type, _name, _actual_count, _maxCount) \ _type* _name = NULL; \ uint32_t _name##_count = 1; \ - if ( _actual_count > _maxCount ) \ + uint32_t _name##_stack_count = _actual_count; \ + if ( _actual_count > _maxCount ) { \ _name = (_type*)malloc(sizeof(_type) * _actual_count); \ + _name##_stack_count = 1; \ + } \ else \ _name##_count = _actual_count; \ - _type _name##_buffer[_name##_count]; \ + _type _name##_buffer[_name##_stack_count]; \ if ( _name == NULL ) \ _name = _name##_buffer; @@ -1770,7 +1773,7 @@ ld::relocatable::File* Parser::parse(const ParserOptions& opts) // create lists of address that already have compact unwind and thus don't need the dwarf parsed unsigned cuLsdaCount = 0; - pint_t cuStarts[countOfCUs]; + STACK_ALLOC_IF_SMALL(pint_t, cuStarts, countOfCUs, 1024); for (uint32_t i=0; i < countOfCUs; ++i) { if ( CUSection::encodingMeansUseDwarf(cuInfoArray[i].compactUnwindInfo) ) cuStarts[i] = -1; @@ -6241,6 +6244,10 @@ template <> bool Section::addRelocFixup(class Parser& parser, const macho_relocation_info

* reloc) { const macho_section

* sect = this->machoSection(); + if ( sect == NULL ) { + warning("malformed mach-o, relocations not supported on section %s", this->sectionName()); + return false; + } uint64_t srcAddr = sect->addr() + reloc->r_address(); Parser::SourceLocation src; Parser::TargetDesc target; diff --git a/src/ld/parsers/textstub_dylib_file.cpp b/src/ld/parsers/textstub_dylib_file.cpp index 5854462..0b83d41 100644 --- a/src/ld/parsers/textstub_dylib_file.cpp +++ b/src/ld/parsers/textstub_dylib_file.cpp @@ -108,14 +108,39 @@ 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)) + // Check if the library supports the new create API. + if (tapi::APIVersion::isAtLeast(1, 1)) { + tapi::ParsingFlags flags = tapi::ParsingFlags::None; + if (enforceDylibSubtypesMatch) + flags |= tapi::ParsingFlags::ExactCpuSubType; + + if (!allowWeakImports) + flags |= tapi::ParsingFlags::DisallowWeakImports; + + file.reset(tapi::LinkerInterfaceFile::create( + path, fileContent, fileLength, cpuType, cpuSubType, flags, + 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)); + } +#else auto matchingType = enforceDylibSubtypesMatch ? - tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible; + tapi::CpuSubTypeMatching::Exact : tapi::CpuSubTypeMatching::ABI_Compatible; - std::string errorMessage; - auto file = std::unique_ptr( - tapi::LinkerInterfaceFile::create(path, fileContent, fileLength, cpuType, - cpuSubType, matchingType, - tapi::PackedVersion32(linkMinOSVersion), errorMessage)); + file.reset(tapi::LinkerInterfaceFile::create( + path, fileContent, fileLength, cpuType, cpuSubType, matchingType, + tapi::PackedVersion32(linkMinOSVersion), errorMessage)); +#endif if (file == nullptr) throw strdup(errorMessage.c_str()); diff --git a/src/ld/passes/bitcode_bundle.cpp b/src/ld/passes/bitcode_bundle.cpp index 527869a..7165051 100644 --- a/src/ld/passes/bitcode_bundle.cpp +++ b/src/ld/passes/bitcode_bundle.cpp @@ -218,6 +218,25 @@ private: ld::Internal& _state; }; +#if LTO_API_VERSION >= 7 +static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, const char* message, void*) +{ + switch ( severity ) { +#if LTO_API_VERSION >= 10 + case LTO_DS_REMARK: +#endif + case LTO_DS_NOTE: + break; // ignore remarks and notes + case LTO_DS_WARNING: + warning("%s", message); + break; + case LTO_DS_ERROR: + throwf("%s", message); + } +} +#endif + + BitcodeAtom::BitcodeAtom() : ld::Atom(bitcodeBundleSection, ld::Atom::definitionRegular, ld::Atom::combineNever, @@ -285,6 +304,11 @@ BitcodeObfuscator::BitcodeObfuscator() _lto_get_asm_symbol_num == NULL || _lto_get_asm_symbol_name == NULL || ::lto_api_version() < 14 ) throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version()); _obfuscator = ::lto_codegen_create_in_local_context(); + +#if LTO_API_VERSION >= 7 + lto_codegen_set_diagnostic_handler(_obfuscator, ltoDiagnosticHandler, NULL); +#endif + #if LTO_API_VERSION >= 14 lto_codegen_set_should_internalize(_obfuscator, false); #endif @@ -455,6 +479,7 @@ void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst) strcmp(key, "hide-symbols") == 0 || strcmp(key, "platform") == 0 || strcmp(key, "sdkversion") == 0 || + strcmp(key, "swift-version") == 0 || strcmp(key, "dylibs/lib") == 0 || strcmp(key, "link-options/option") == 0 ) { xar_prop_create(dst, key, val); @@ -667,6 +692,14 @@ void BitcodeBundle::doPass() BitcodeHandler bitcodeHandler((char*)ltoTemp.getContent(), ltoTemp.getSize()); bitcodeHandler.populateMustPreserveSymbols(obfuscator); } + // add must preserve symbols from compiler_rt input. + for ( auto &f : _state.filesFromCompilerRT ) { + std::vector symbols; + if ( f->fileContent() && mach_o::relocatable::getNonLocalSymbols(f->fileContent(), symbols) ) { + for ( auto &sym : symbols) + obfuscator->addMustPreserveSymbols(sym); + } + } // special symbols supplied by linker obfuscator->addMustPreserveSymbols("___dso_handle"); @@ -958,6 +991,14 @@ void BitcodeBundle::doPass() if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 ) throwf("could not add SDK version to bitcode bundle"); + // Write swift version into header + if (_state.swiftVersion != 0) { + char swiftVersion[64]; + Options::userReadableSwiftVersion(_state.swiftVersion, swiftVersion); + if ( xar_prop_create((xar_file_t)linkXML, "swift-version", swiftVersion) ) + throwf("could not add swift version to bitcode bundle"); + } + // Write dylibs char sdkRoot[PATH_MAX]; if ( _options.sdkPaths().empty() || (realpath(_options.sdkPaths().front(), sdkRoot) == NULL) ) @@ -993,6 +1034,12 @@ void BitcodeBundle::doPass() } } + // Write if compiler_rt is force loaded + if (_state.forceLoadCompilerRT) { + if ( xar_prop_create((xar_file_t)linkXML, "rt-forceload", "1") != 0 ) + throwf("could not add compiler_rt force_load info to bitcode bundle"); + } + // Write link-line into archive for ( auto &it : linkCmd ) { if (xar_prop_create((xar_file_t)linkXML, "link-options/option", it.c_str()) != 0) diff --git a/src/ld/passes/code_dedup.cpp b/src/ld/passes/code_dedup.cpp index e067acf..c6bc8fb 100644 --- a/src/ld/passes/code_dedup.cpp +++ b/src/ld/passes/code_dedup.cpp @@ -49,7 +49,7 @@ public: DeDupAliasAtom(const ld::Atom* dupOf, const ld::Atom* replacement) : ld::Atom(dupOf->section(), ld::Atom::definitionRegular, ld::Atom::combineNever, dupOf->scope(), dupOf->contentType(), ld::Atom::symbolTableIn, - false, false, true, 0), + false, false, true, dupOf->alignment()), _dedupOf(dupOf), _fixup(0, ld::Fixup::k1of1, ld::Fixup::kindNoneFollowOn, ld::Fixup::bindingDirectlyBound, replacement) { if ( dupOf->autoHide() ) @@ -142,12 +142,21 @@ struct atom_hashing { // A helper for std::unordered_map<> that compares functions struct atom_equal { - struct VisitedSet { - std::unordered_set atoms1; - std::unordered_set atoms2; + struct BackChain { + BackChain* prev; + const Atom* inCallChain1; + const Atom* inCallChain2; + + bool inCallChain(const Atom* target) { + for (BackChain* p = this; p->prev != NULL; p = p->prev) { + if ( (p->inCallChain1 == target) || (p->inCallChain2 == target) ) + return true; + } + return false; + } }; - static bool sameFixups(const ld::Atom* atom1, const ld::Atom* atom2, VisitedSet& visited) { + static bool sameFixups(const ld::Atom* atom1, const ld::Atom* atom2, BackChain& backChain) { ++sFixupCompareCount; //fprintf(stderr, "sameFixups(%s,%s)\n", atom1->name(), atom2->name()); Fixup::iterator f1 = atom1->fixupsBegin(); @@ -198,10 +207,16 @@ struct atom_equal { return false; if ( target1->section().type() != ld::Section::typeCode ) return false; - // to support co-recursive functions, don't call equals() on targets we've already visited - if ( ((visited.atoms1.count(target1) == 0) || (visited.atoms2.count(target2) == 0)) && !equal(target1, target2, visited) ) - return false; - } + // to support co-recursive functions, don't recurse into equals() for targets already in the back chain + if ( !backChain.inCallChain(target1) || !backChain.inCallChain(target2) ) { + BackChain nextBackChain; + nextBackChain.prev = &backChain; + nextBackChain.inCallChain1 = target1; + nextBackChain.inCallChain2 = target2; + if ( !equal(target1, target2, nextBackChain) ) + return false; + } + } ++f1; ++f2; @@ -210,17 +225,21 @@ struct atom_equal { return true; } - static bool equal(const ld::Atom* atom1, const ld::Atom* atom2, VisitedSet& visited) { + static bool equal(const ld::Atom* atom1, const ld::Atom* atom2, BackChain& backChain) { + if ( atom1->size() != atom2->size() ) + return false; if ( atom_hashing::hash(atom1) != atom_hashing::hash(atom2) ) return false; - visited.atoms1.insert(atom1); - visited.atoms2.insert(atom2); - return sameFixups(atom1, atom2, visited); + if ( memcmp(atom1->rawContentPointer(), atom2->rawContentPointer(), atom1->size()) != 0 ) + return false; + bool result = sameFixups(atom1, atom2, backChain); + //fprintf(stderr, "sameFixups(%s,%s) = %d\n", atom1->name(), atom2->name(), result); + return result; } bool operator()(const ld::Atom* atom1, const ld::Atom* atom2) const { - VisitedSet visited; - return equal(atom1, atom2, visited); + BackChain backChain = { NULL, atom1, atom2 }; + return equal(atom1, atom2, backChain); } }; @@ -348,7 +367,7 @@ void doPass(const Options& opts, ld::Internal& state) if ( log ) { fprintf(stderr, "atoms before pruning:\n"); for (const ld::Atom* atom : textSection->atoms) - fprintf(stderr, " %p (size=%llu) %sp\n", atom, atom->size(), atom->name()); + fprintf(stderr, " %p (size=%llu) %s\n", atom, atom->size(), atom->name()); } // remove replaced atoms from section textSection->atoms.erase(std::remove_if(textSection->atoms.begin(), textSection->atoms.end(), @@ -363,7 +382,7 @@ void doPass(const Options& opts, ld::Internal& state) if ( log ) { fprintf(stderr, "atoms after pruning:\n"); for (const ld::Atom* atom : textSection->atoms) - fprintf(stderr, " %p (size=%llu) %sp\n", atom, atom->size(), atom->name()); + fprintf(stderr, " %p (size=%llu) %s\n", atom, atom->size(), atom->name()); } //fprintf(stderr, "hash-count=%lu, fixup-compares=%lu, atom-count=%u\n", sHashCount, sFixupCompareCount, atomsBeingComparedCount); diff --git a/src/ld/passes/objc.cpp b/src/ld/passes/objc.cpp index e2afca1..a9a8c6a 100644 --- a/src/ld/passes/objc.cpp +++ b/src/ld/passes/objc.cpp @@ -1110,12 +1110,12 @@ MethodListAtom::MethodListAtom(ld::Internal& state, const ld::Atom* baseMetho if ( baseMethodListMethodNameAtoms.count(target) != 0 ) { warning("%s method '%s' in category from %s overrides method from class in %s", (meta ? "meta" : "instance"), target->rawContentPointer(), - categoryMethodListAtom->file()->path(), baseMethodList->file()->path() ); + categoryMethodListAtom->safeFilePath(), baseMethodList->safeFilePath() ); } if ( categoryMethodNameAtoms.count(target) != 0 ) { warning("%s method '%s' in category from %s conflicts with same method from another category", (meta ? "meta" : "instance"), target->rawContentPointer(), - categoryMethodListAtom->file()->path()); + categoryMethodListAtom->safeFilePath()); } categoryMethodNameAtoms.insert(target); } @@ -1270,12 +1270,12 @@ void scanCategories(ld::Internal& state, if (Category::getClassProperties(state, categoryAtom)) { haveCategoriesWithNonNullClassProperties = true; - // fprintf(stderr, "category in file %s has non-null class properties\n", categoryAtom->file()->path()); + // fprintf(stderr, "category in file %s has non-null class properties\n", categoryAtom->safeFilePath()); } if (!categoryAtom->file()->objcHasCategoryClassPropertiesField()) { haveCategoriesWithoutClassPropertyStorage = true; - // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->file()->path()); + // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->safeFilePath()); } } } diff --git a/src/ld/passes/order.cpp b/src/ld/passes/order.cpp index a7d31f3..0528876 100644 --- a/src/ld/passes/order.cpp +++ b/src/ld/passes/order.cpp @@ -530,7 +530,7 @@ void Layout::buildOrdinalOverrideMap() AtomToOrdinal::iterator pos = _ordinalOverrideMap.find(nextAtom); if ( pos == _ordinalOverrideMap.end() ) { _ordinalOverrideMap[nextAtom] = index++; - if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->name(), nextAtom->file()->path()); + if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s in cluster from %s\n", index, nextAtom->name(), nextAtom->safeFilePath()); } else { if (_s_log ) fprintf(stderr, "could not order %s as %u because it was already laid out earlier by %s as %u\n", @@ -540,7 +540,7 @@ void Layout::buildOrdinalOverrideMap() } else { _ordinalOverrideMap[atom] = index; - if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->name(), atom->file()->path()); + if (_s_log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->name(), atom->safeFilePath()); } ++matchCount; } @@ -617,10 +617,20 @@ void Layout::doPass() // sort atoms in each section for (std::vector::iterator sit=_state.sections.begin(); sit != _state.sections.end(); ++sit) { ld::Internal::FinalSection* sect = *sit; - if ( sect->type() == ld::Section::typeTempAlias ) - continue; - if ( log ) fprintf(stderr, "sorting section %s\n", sect->sectionName()); - std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); + switch ( sect->type() ) { + case ld::Section::typeTempAlias: + case ld::Section::typeStub: + case ld::Section::typeLazyPointer: + case ld::Section::typeLazyPointerClose: + case ld::Section::typeStubClose: + case ld::Section::typeNonLazyPointer: + // these sections are already sorted by pass that created them + break; + default: + if ( log ) fprintf(stderr, "sorting section %s\n", sect->sectionName()); + std::sort(sect->atoms.begin(), sect->atoms.end(), _comparer); + break; + } } if ( log ) { diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index 7739986..6763e1a 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -396,11 +396,13 @@ struct AtomSorter rightString = (char*)cstringAtom->rawContentPointer(); } } - assert(leftString != NULL); - assert(rightString != NULL); - diff = strcmp(leftString, rightString); - if ( diff != 0 ) - return (diff < 0); + if ( leftString != rightString ) { + assert(leftString != NULL); + assert(rightString != NULL); + diff = strcmp(leftString, rightString); + if ( diff != 0 ) + return (diff < 0); + } } else if ( left->section().type() == ld::Section::typeLiteral4 ) { // if literal sort by content @@ -804,6 +806,7 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreThumbHigh16: printf(", then store high-16 in Thumb movt"); break; +#if SUPPORT_ARCH_arm64 case ld::Fixup::kindStoreARM64Branch26: printf(", then store as ARM64 26-bit pcrel branch"); break; @@ -843,9 +846,7 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreARM64PCRelToGOT: printf(", then store as 32-bit delta to GOT entry"); break; - case ld::Fixup::kindStoreARM64PointerToGOT32: - printf(", then store as 32-bit pointer to GOT entry"); - break; +#endif case ld::Fixup::kindDtraceExtra: printf("dtrace static probe extra info"); break; @@ -867,12 +868,14 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: printf("Thumb dtrace static is-enabled site"); break; +#if SUPPORT_ARCH_arm64 case ld::Fixup::kindStoreARM64DtraceCallSiteNop: printf("ARM64 dtrace static probe site"); break; case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: printf("ARM64 dtrace static is-enabled site"); break; +#endif case ld::Fixup::kindLazyTarget: printf("lazy reference to external symbol %s", referenceTargetAtomName(ref)); break; @@ -990,6 +993,7 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindSetTargetTLVTemplateOffsetLittleEndian64: printf("tlv template offset of %s", referenceTargetAtomName(ref)); break; +#if SUPPORT_ARCH_arm64 case ld::Fixup::kindStoreTargetAddressARM64Branch26: printf("ARM64 store 26-bit pcrel branch to %s", referenceTargetAtomName(ref)); break; @@ -1023,6 +1027,7 @@ void dumper::dumpFixup(const ld::Fixup* ref) case ld::Fixup::kindStoreTargetAddressARM64TLVPLoadNowLeaPageOff12: printf("ARM64 store 12-bit page offset of lea for TLV of %s", referenceTargetAtomName(ref)); break; +#endif //default: // printf("unknown fixup"); // break; diff --git a/src/other/dyldinfo.cpp b/src/other/dyldinfo.cpp index 83d601e..e271978 100644 --- a/src/other/dyldinfo.cpp +++ b/src/other/dyldinfo.cpp @@ -276,10 +276,13 @@ bool DyldInfoPrinter::validFile(const uint8_t* fileContent) switch (header->filetype()) { case MH_EXECUTE: case MH_DYLIB: + case MH_DYLIB_STUB: case MH_BUNDLE: case MH_DYLINKER: case MH_KEXT_BUNDLE: return true; + default: + return false; } return false; } diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index 97138e8..0b08844 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -455,6 +455,8 @@ void MachOChecker::checkLoadCommands() case LC_LOAD_UPWARD_DYLIB: case LC_VERSION_MIN_MACOSX: case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: case LC_FUNCTION_STARTS: case LC_DYLD_ENVIRONMENT: case LC_DATA_IN_CODE: diff --git a/unit-tests/test-cases/lto-live_support_section/Makefile b/unit-tests/test-cases/lto-live_support_section/Makefile new file mode 100644 index 0000000..01b417f --- /dev/null +++ b/unit-tests/test-cases/lto-live_support_section/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2009 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 + +# +# __asan_globals section is stripped out when building -flto +# + + +run: all + +all: + ${CC} ${CCFLAGS} -flto -o foo foo.c -Wl,-dead_strip,-exported_symbol,_my_global -v + # Check symbols that are expected to be preserved + nm -m foo | grep _global_metadata | ${FAIL_IF_EMPTY} + nm -m foo | grep _liveness_binder | ${FAIL_IF_EMPTY} + nm -m foo | grep _main | ${FAIL_IF_EMPTY} + nm -m foo | grep _my_global | ${FAIL_IF_EMPTY} + + # Check symbols that are expected to be stripped + nm -m foo | grep unused | ${FAIL_IF_STDIN} + +clean: + rm foo diff --git a/unit-tests/test-cases/lto-live_support_section/foo.c b/unit-tests/test-cases/lto-live_support_section/foo.c new file mode 100644 index 0000000..f49c23a --- /dev/null +++ b/unit-tests/test-cases/lto-live_support_section/foo.c @@ -0,0 +1,22 @@ + +int my_global = 0; + +__attribute__ ((section ("__DATA,__asan_globals,regular"))) +struct { void *ptr; } global_metadata = { .ptr = &my_global }; + +__attribute__ ((used, section ("__DATA,__asan_liveness,regular,live_support"))) +struct { void *a, *b; } liveness_binder = { .a = &global_metadata, .b = &my_global }; + +int unused_global = 0; + +__attribute__ ((section ("__DATA,__asan_globals,regular"))) +struct { void *ptr; } unused_global_metadata = { .ptr = &unused_global }; + +__attribute__ ((used, section ("__DATA,__asan_liveness,regular,live_support"))) +struct { void *a, *b; } unused_liveness_binder = { .a = &unused_global_metadata, .b = &unused_global }; + + +int main(int argc, char *argv[]) +{ + return my_global; +} diff --git a/unit-tests/test-cases/lto-unexport-sym/Makefile b/unit-tests/test-cases/lto-unexport-sym/Makefile new file mode 100644 index 0000000..de0808a --- /dev/null +++ b/unit-tests/test-cases/lto-unexport-sym/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2009 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 + +# +# __asan_globals section is stripped out when building -flto +# + + +run: all + +all: + ${CC} ${CCFLAGS} -c a.c + ${CC} ${CCFLAGS} -c b.c -flto + ${CC} ${CCFLAGS} a.o b.o -unexported_symbols_list unexp.list + # Check that unexported symbols are hidden + nm a.out | grep "t _bar" | ${FAIL_IF_EMPTY} + nm a.out | grep "t _foo" | ${FAIL_IF_EMPTY} + +clean: + rm a.out b.o a.o diff --git a/unit-tests/test-cases/lto-unexport-sym/a.c b/unit-tests/test-cases/lto-unexport-sym/a.c new file mode 100644 index 0000000..89cea52 --- /dev/null +++ b/unit-tests/test-cases/lto-unexport-sym/a.c @@ -0,0 +1,7 @@ +void foo(); +void bar() { + foo(); +} + +int main() { +} diff --git a/unit-tests/test-cases/lto-unexport-sym/b.c b/unit-tests/test-cases/lto-unexport-sym/b.c new file mode 100644 index 0000000..d0d3aa5 --- /dev/null +++ b/unit-tests/test-cases/lto-unexport-sym/b.c @@ -0,0 +1,3 @@ +void foo() { + +} \ No newline at end of file diff --git a/unit-tests/test-cases/lto-unexport-sym/unexp.list b/unit-tests/test-cases/lto-unexport-sym/unexp.list new file mode 100644 index 0000000..b9e50b8 --- /dev/null +++ b/unit-tests/test-cases/lto-unexport-sym/unexp.list @@ -0,0 +1,2 @@ +_foo +_bar -- 2.45.2