Index: lld/MachO/DriverUtils.cpp =================================================================== --- lld/MachO/DriverUtils.cpp +++ lld/MachO/DriverUtils.cpp @@ -201,10 +201,11 @@ DylibFile *umbrella, bool isBundleLoader) { CachedHashStringRef path(mbref.getBufferIdentifier()); - DylibFile *file = loadedDylibs[path]; + DylibFile *&file = loadedDylibs[path]; if (file) return file; + DylibFile *newFile; file_magic magic = identify_magic(mbref.getBuffer()); if (magic == file_magic::tapi_file) { Expected> result = TextAPIReader::get(mbref); @@ -214,19 +215,27 @@ return {}; } file = make(**result, umbrella, isBundleLoader); + + // parseReexports() can recursively call loadDylib(). That's fine since + // we wrote DylibFile we just loaded to the loadDylib cache via the `file` + // reference. But the recursive load can grow loadDylibs, so the `file` + // reference might become invalid after parseReexports() -- so copy the + // pointer it refers to before going on. + newFile = file; + newFile->parseReexports(**result, umbrella); } else { assert(magic == file_magic::macho_dynamically_linked_shared_lib || magic == file_magic::macho_dynamically_linked_shared_lib_stub || magic == file_magic::macho_executable || magic == file_magic::macho_bundle); file = make(mbref, umbrella, isBundleLoader); + + // parseLoadCommands() can also recursively call loadDylib(). See comment + // in previous block for why this means we must copy `file` here. + newFile = file; + newFile->parseLoadCommands(mbref, umbrella); } - // Note that DylibFile's ctor may recursively invoke loadDylib(), which can - // cause loadedDylibs to get resized and its iterators invalidated. As such, - // we redo the key lookup here instead of caching an iterator from our earlier - // lookup at the start of the function. - loadedDylibs[path] = file; - return file; + return newFile; } Optional Index: lld/MachO/InputFiles.h =================================================================== --- lld/MachO/InputFiles.h +++ lld/MachO/InputFiles.h @@ -130,6 +130,13 @@ // .dylib file class DylibFile : public InputFile { public: + explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella, + bool isBundleLoader = false); + + explicit DylibFile(const llvm::MachO::InterfaceFile &interface, + DylibFile *umbrella = nullptr, + bool isBundleLoader = false); + // Mach-O dylibs can re-export other dylibs as sub-libraries, meaning that the // symbols in those sub-libraries will be available under the umbrella // library's namespace. Those sub-libraries can also have their own @@ -137,16 +144,14 @@ // the root dylib to ensure symbols in the child library are correctly bound // to the root. On the other hand, if a dylib is being directly loaded // (through an -lfoo flag), then `umbrella` should be a nullptr. - explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella, - bool isBundleLoader = false); - - explicit DylibFile(const llvm::MachO::InterfaceFile &interface, - DylibFile *umbrella = nullptr, - bool isBundleLoader = false); + void parseLoadCommands(MemoryBufferRef mb, DylibFile *umbrella); + void parseReexports(const llvm::MachO::InterfaceFile &interface, + DylibFile *umbrella); static bool classof(const InputFile *f) { return f->kind() == DylibKind; } StringRef dylibName; + DylibFile *exportingFile; uint32_t compatibilityVersion = 0; uint32_t currentVersion = 0; int64_t ordinal = 0; // Ordinal numbering starts from 1, so 0 is a sentinel Index: lld/MachO/InputFiles.cpp =================================================================== --- lld/MachO/InputFiles.cpp +++ lld/MachO/InputFiles.cpp @@ -832,7 +832,7 @@ return; // Initialize symbols. - DylibFile *exportingFile = isImplicitlyLinked(dylibName) ? this : umbrella; + exportingFile = isImplicitlyLinked(dylibName) ? this : umbrella; if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) { auto *c = reinterpret_cast(cmd); parseTrie(buf + c->export_off, c->export_size, @@ -847,8 +847,12 @@ return; } - const uint8_t *p = - reinterpret_cast(hdr) + target->headerSize; +} + +void DylibFile::parseLoadCommands(MemoryBufferRef mb, DylibFile *umbrella) { + auto *hdr = reinterpret_cast(mb.getBufferStart()); + const uint8_t *p = reinterpret_cast(mb.getBufferStart()) + + target->headerSize; for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) { auto *cmd = reinterpret_cast(p); p += cmd->cmdsize; @@ -877,6 +881,13 @@ } } +// Some versions of XCode ship with .tbd files that don't have the right +// platform settings. +static constexpr std::array skipPlatformChecks{ + "/usr/lib/system/libsystem_kernel.dylib", + "/usr/lib/system/libsystem_platform.dylib", + "/usr/lib/system/libsystem_pthread.dylib"}; + DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella, bool isBundleLoader) : InputFile(DylibKind, interface), refState(RefState::Unreferenced), @@ -890,13 +901,6 @@ compatibilityVersion = interface.getCompatibilityVersion().rawValue(); currentVersion = interface.getCurrentVersion().rawValue(); - // Some versions of XCode ship with .tbd files that don't have the right - // platform settings. - static constexpr std::array skipPlatformChecks{ - "/usr/lib/system/libsystem_kernel.dylib", - "/usr/lib/system/libsystem_platform.dylib", - "/usr/lib/system/libsystem_pthread.dylib"}; - if (!is_contained(skipPlatformChecks, dylibName) && !is_contained(interface.targets(), config->platformInfo.target)) { error(toString(this) + " is incompatible with " + @@ -904,7 +908,7 @@ return; } - DylibFile *exportingFile = isImplicitlyLinked(dylibName) ? this : umbrella; + exportingFile = isImplicitlyLinked(dylibName) ? this : umbrella; auto addSymbol = [&](const Twine &name) -> void { symbols.push_back(symtab->addDylib(saver.save(name), exportingFile, /*isWeakDef=*/false, @@ -934,10 +938,12 @@ break; } } +} +void DylibFile::parseReexports(const llvm::MachO::InterfaceFile &interface, + DylibFile *umbrella) { const InterfaceFile *topLevel = interface.getParent() == nullptr ? &interface : interface.getParent(); - for (InterfaceFileRef intfRef : interface.reexportedLibraries()) { InterfaceFile::const_target_range targets = intfRef.targets(); if (is_contained(skipPlatformChecks, intfRef.getInstallName()) ||