Skip to content

Commit 9a2e2de

Browse files
committed
[lld-macho] Change loadReexport to handle the case where a TAPI re-exports to reference documents nested within other TBD.
Currently, it was delibrately impleneted to not handle this case, but as it has turnt out, we need this feature. The concrete use case is `System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa` reexports /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit , which then rexports /System/Library/PrivateFrameworks/UIFoundation.framework/Versions/A/UIFoundation The current implemention uses a global currentTopLevelTapi, which is not reset until it finishes loading the whole tree. This is a problem because if the top-level is set to Cocoa, then when we get to UIFoundation, it will try to find UIFoundation in the current top level, which is Cocoa and will not find it. The right thing should be: - When loading a library from a TBD file, re-exports need to be looked up in the auxiliary documents within the same TBD. - When loading from an actual dylib, no additional TBD documents need to be examined. - In no case does a re-export mentioned in one TBD file need to be looked up in a document in an auxiliary document from a different TBD file Differential Revision: https://reviews.llvm.org/D97438
1 parent d96b5e6 commit 9a2e2de

File tree

7 files changed

+67
-25
lines changed

7 files changed

+67
-25
lines changed

lld/MachO/Driver.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
#include "llvm/Option/OptTable.h"
1616
#include "llvm/Support/MemoryBuffer.h"
1717

18+
namespace llvm {
19+
namespace MachO {
20+
class InterfaceFile;
21+
} // namespace MachO
22+
} // namespace llvm
23+
1824
namespace lld {
1925
namespace macho {
2026

lld/MachO/InputFiles.cpp

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -575,19 +575,16 @@ static Optional<DylibFile *> loadDylib(StringRef path, DylibFile *umbrella) {
575575

576576
// TBD files are parsed into a series of TAPI documents (InterfaceFiles), with
577577
// the first document storing child pointers to the rest of them. When we are
578-
// processing a given TBD file, we store that top-level document here. When
579-
// processing re-exports, we search its children for potentially matching
580-
// documents in the same TBD file. Note that the children themselves don't
581-
// point to further documents, i.e. this is a two-level tree.
578+
// processing a given TBD file, we store that top-level document in
579+
// currentTopLevelTapi. When processing re-exports, we search its children for
580+
// potentially matching documents in the same TBD file. Note that the children
581+
// themselves don't point to further documents, i.e. this is a two-level tree.
582582
//
583-
// ld64 allows a TAPI re-export to reference documents nested within other TBD
584-
// files, but that seems like a strange design, so this is an intentional
585-
// deviation.
586-
const InterfaceFile *currentTopLevelTapi = nullptr;
587-
588583
// Re-exports can either refer to on-disk files, or to documents within .tbd
589584
// files.
590-
static Optional<DylibFile *> findDylib(StringRef path, DylibFile *umbrella) {
585+
static Optional<DylibFile *>
586+
findDylib(StringRef path, DylibFile *umbrella,
587+
const InterfaceFile *currentTopLevelTapi) {
591588
if (path::is_absolute(path, path::Style::posix))
592589
for (StringRef root : config->systemLibraryRoots)
593590
if (Optional<std::string> dylibPath =
@@ -631,8 +628,10 @@ static bool isImplicitlyLinked(StringRef path) {
631628
return false;
632629
}
633630

634-
void loadReexport(StringRef path, DylibFile *umbrella) {
635-
Optional<DylibFile *> reexport = findDylib(path, umbrella);
631+
void loadReexport(StringRef path, DylibFile *umbrella,
632+
const InterfaceFile *currentTopLevelTapi) {
633+
Optional<DylibFile *> reexport =
634+
findDylib(path, umbrella, currentTopLevelTapi);
636635
if (!reexport)
637636
error("unable to locate re-export with install name " + path);
638637
else if (isImplicitlyLinked(path))
@@ -690,7 +689,7 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
690689
const auto *c = reinterpret_cast<const dylib_command *>(cmd);
691690
StringRef reexportPath =
692691
reinterpret_cast<const char *>(c) + read32le(&c->dylib.name);
693-
loadReexport(reexportPath, umbrella);
692+
loadReexport(reexportPath, umbrella, nullptr);
694693
}
695694

696695
// FIXME: What about LC_LOAD_UPWARD_DYLIB, LC_LAZY_LOAD_DYLIB,
@@ -701,7 +700,7 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
701700
const auto *c = reinterpret_cast<const dylib_command *>(cmd);
702701
StringRef dylibPath =
703702
reinterpret_cast<const char *>(c) + read32le(&c->dylib.name);
704-
Optional<DylibFile *> dylib = findDylib(dylibPath, umbrella);
703+
Optional<DylibFile *> dylib = findDylib(dylibPath, umbrella, nullptr);
705704
if (!dylib)
706705
error(Twine("unable to locate library '") + dylibPath +
707706
"' loaded from '" + toString(this) + "' for -flat_namespace");
@@ -758,17 +757,11 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella,
758757
}
759758
}
760759

761-
bool isTopLevelTapi = false;
762-
if (currentTopLevelTapi == nullptr) {
763-
currentTopLevelTapi = &interface;
764-
isTopLevelTapi = true;
765-
}
760+
const InterfaceFile *topLevel =
761+
interface.getParent() == nullptr ? &interface : interface.getParent();
766762

767763
for (InterfaceFileRef intfRef : interface.reexportedLibraries())
768-
loadReexport(intfRef.getInstallName(), umbrella);
769-
770-
if (isTopLevelTapi)
771-
currentTopLevelTapi = nullptr;
764+
loadReexport(intfRef.getInstallName(), umbrella, topLevel);
772765
}
773766

774767
ArchiveFile::ArchiveFile(std::unique_ptr<object::Archive> &&f)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--- !tapi-tbd-v3
2+
archs: [ i386, x86_64 ]
3+
uuids: [ 'i386: 00000000-0000-0000-0000-000000000000', 'x86_64: 00000000-0000-0000-0000-000000000001' ]
4+
platform: ios
5+
install-name: '/usr/lib/libReexportSystem.dylib'
6+
exports:
7+
- archs: [ i386, x86_64 ]
8+
re-exports: [ '/usr/lib/libSystem' ]
9+
symbols: [ __crashreporter_info__, _cache_create ]
10+

lld/test/MachO/Inputs/iPhoneSimulator.sdk/usr/lib/libSystem.tbd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ install-name: '/usr/lib/libSystem.dylib'
66
current-version: 1281
77
exports:
88
- archs: [ i386, x86_64 ]
9-
re-exports: [ '/usr/lib/system/libcache.dylib' ]
10-
symbols: [ __crashreporter_info__ ]
9+
re-exports: [ '/usr/lib/system/libcache.dylib', ]
10+
symbols: [ __crashreporter_info__, _cache_create ]
1111
--- !tapi-tbd-v3
1212
archs: [ i386, x86_64 ]
1313
uuids: [ 'i386: 00000000-0000-0000-0000-000000000002', 'x86_64: 00000000-0000-0000-0000-000000000003' ]

lld/test/MachO/reexport-nested-lib.s

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# REQUIRES: x86
2+
#
3+
# This tests that we can reference symbols from a dylib,
4+
# re-exported by a top-level tapi document, which itself is
5+
# re-exported by another top-level tapi document.
6+
#
7+
# RUN: rm -rf %t; mkdir -p %t
8+
# RUN: llvm-mc -filetype obj -triple x86_64-apple-darwin %s -o %t/test.o
9+
# RUN: %lld -o %t/test -syslibroot %S/Inputs/iPhoneSimulator.sdk -lReexportSystem %t/test.o
10+
# RUN: llvm-objdump %t/test --macho --bind %t/test | FileCheck %s
11+
12+
# CHECK: segment section address type addend dylib symbol
13+
# CHECK: __DATA __data 0x{{[0-9a-f]*}} pointer 0 libReexportSystem __crashreporter_info__
14+
# CHECK: __DATA __data 0x{{[0-9a-f]*}} pointer 0 libReexportSystem _cache_create
15+
16+
.text
17+
.globl _main
18+
19+
_main:
20+
ret
21+
22+
.data
23+
// This symbol is from libSystem, which is re-exported by libReexportSystem.
24+
// Reference it here to verify that it is visible.
25+
.quad __crashreporter_info__
26+
27+
// This symbol is from /usr/lib/system/libcache.dylib, which is re-exported in libSystem.
28+
.quad _cache_create

llvm/include/llvm/TextAPI/MachO/InterfaceFile.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,9 @@ class InterfaceFile {
338338
///\param Document The library to inline with top level library.
339339
void addDocument(std::shared_ptr<InterfaceFile> &&Document);
340340

341+
/// Returns the pointer to parent document if exists or nullptr otherwise.
342+
InterfaceFile *getParent() const { return Parent; }
343+
341344
/// Get the list of inlined libraries.
342345
///
343346
/// \return Returns a list of the inlined frameworks.
@@ -431,6 +434,7 @@ class InterfaceFile {
431434
std::vector<std::shared_ptr<InterfaceFile>> Documents;
432435
std::vector<std::pair<Target, std::string>> UUIDs;
433436
SymbolMapType Symbols;
437+
InterfaceFile *Parent = nullptr;
434438
};
435439

436440
template <typename DerivedT, typename KeyInfoT, typename BucketT>

llvm/lib/TextAPI/MachO/InterfaceFile.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ void InterfaceFile::addDocument(std::shared_ptr<InterfaceFile> &&Document) {
124124
const std::shared_ptr<InterfaceFile> &RHS) {
125125
return LHS->InstallName < RHS->InstallName;
126126
});
127+
Document->Parent = this;
127128
Documents.insert(Pos, Document);
128129
}
129130

0 commit comments

Comments
 (0)