The re-exports list in a TAPI document can either refer to other inlined
TAPI documents, or to on-disk files (which may themselves be TBD or
regular files.) Similarly, the re-exports of a regular dylib can refer
to a TBD file.
Details
- Reviewers
smeenai - Group Reviewers
Restricted Project - Commits
- rG7394460d8759: [lld-macho] Handle TAPI and regular re-exports uniformly
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
lld/test/MachO/Inputs/iPhoneSimulator.sdk/usr/lib/libSystem.tbd | ||
---|---|---|
24 | is not re-exported* |
lld/MachO/Driver.cpp | ||
---|---|---|
133 | IIUC, the previous version would prefer the tbd to the dylib, and resolveDylibPath will prefer the dylib to the TBD. IIRC, if ld64 finds both a TBD and a dylib for the same file, it verifies the TBD against the dylib and falls back to the dylib in case of a mismatch. I don't think we need to implement that (and I don't think LLVM's version of TextAPI has the equivalence checking logic yet anyway), so perhaps always preferring the dylib in that case (as you're doing now) is reasonable. | |
182 | This matches ld64's behavior as far as I can tell, so it looks good, but can you add a test? Both for this and for the standard search paths only being searched for under the syslibroots? (This and the tests should also probably be their own diff.) | |
387–389 | Can we get a test for this fix as well? (It should also probably be its own diff.) | |
lld/MachO/InputFiles.cpp | ||
375 | If I'm understanding correctly, this will only be able to find any inline documents we've seen in our input files thus far. Does this match ld64, or is it able to find inline documents from later files? | |
448–449 | Nit: we're inconsistent about reexport vs reExport | |
lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libc++abi.tbd | ||
5 | Should this be libc++abi? | |
lld/test/MachO/reexport-stub.s | ||
16 | I thought that for sub-libraries, the bind information reflected the actual sub-library the symbol was coming from, and that seems to be the case in a basic test I did with ld64. Can you confirm how this is supposed to behave? (IIRC there's options to re-export specific symbols, and those bind to the re-exporting library.) |
address some comments
lld/MachO/Driver.cpp | ||
---|---|---|
133 | yeah, I did inadvertently change things here. From a quick glance at the code, I think your description of ld64's behavior is correct. ld64 can also take an environment variable that makes it always prefer the TAPI file. Do you know if having both the TAPI and the dylib has a real use case? Given the amount of logic ld64 has devoted to it, I guess there might be, but it seems odd. Maybe I could just raise an error for now if both are present? | |
182 | yeah, sneaking this in here was sloppy, my bad :p D85992: [lld-macho] Fall back to raw path if we don't find anything under syslibroot | |
387–389 | whoops, I did not mean to leave this change in at all. It isn't a fix, it's wrong :p | |
lld/MachO/InputFiles.cpp | ||
375 | ld64 is indeed able to find documents from later files. It seems like pretty bizarre behavior to me, though -- what would be the use case? (In my initial implementation, I actually tried to limit re-exports in tbds to reference just those in the same file because I thought that made the most sense, but it was more awkward to implement that...) | |
lld/test/MachO/reexport-stub.s | ||
16 | Swapping ld64 for lld in this particular test gives the same result. (sub-library.s tests something similar, and ld64 binds to the parent library there too.) Can you share the test you did that showed a binding to the sub-library? |
lld/MachO/Driver.cpp | ||
---|---|---|
133 | The Xcode 12 betas have both dylibs and TBDs present (for at least some of the SDKs), so erroring out in that case wouldn't be ideal. (I don't think the final Xcode releases every have dylibs.) I think it's fine to just use the dylib if you have both, and perhaps add a TODO to consider adding verification like ld64 has. | |
lld/MachO/InputFiles.cpp | ||
375 | Yeah, I'd also find it weird to reference sub-documents from other files, and I don't know if it's actually used in the SDKs. | |
lld/test/MachO/reexport-stub.s | ||
16 | I'm using ld64 530 from Xcode 11.3.1. If I try to run this test as-is (albeit with clang instead of llvm-mc), when I try to link test, I get an error: ld: malformed mach-o, symbol table not in __LINKEDIT file './libreexporter.dylib' I can just add a dummy symbol to work around that, and then $ cat reexporter.s .globl foo foo: ret $ cat test.s .globl _main _main: ret .data .quad ___gxx_personality_v0 $ clang -c reexporter.s test.s $ ld -dylib -lc++ -sub_library libc++ reexporter.o -o libreexporter.dylib $ ld -o test -lSystem -L. -lreexporter test.o $ objdump --bind test Bind table: segment section address type addend dylib symbol __DATA __data 0x100001000 pointer 0 libc++ ___gxx_personality_v0 |
lld/test/MachO/reexport-stub.s | ||
---|---|---|
16 | hm, I must have messed up my first attempt, because I was able to see the binding to libc++ on my second try. But I figured out why sub-library.s doesn't repro the issue: bindings only get routed directly to re-exports if those re-exports have "public install names", i.e. if they live under /usr/lib/ or if they are a top-level framework under /System/Library/Frameworks/. I couldn't find any code comments explaining why, but I'm guessing it's an optimization -- I suppose the libraries with "public install names" form a stable ABI, and the only reason they get re-exported by other libraries is to provide some convenience to the user (who doesn't need to pass in half a dozen linker flags). Avoiding unnecessary re-export processing at runtime is probably a performance win. Addressing this seems somewhat orthogonal to this diff, so a TODO comment should suffice for now? |
lld/test/MachO/reexport-stub.s | ||
---|---|---|
16 | Ah, interesting. And yup, a TODO sounds good. |
add TODOs
lld/MachO/InputFiles.cpp | ||
---|---|---|
375 | For a quick sanity check, I got lld to load all the non-Swift TBDs under the iPhone simulator's /usr/lib folder, and it worked fine. I did it twice, the second time with all the command-line -l arguments in reverse, and that worked too. So it seems like we don't have any cross-file references to inline documents. |
lld/test/MachO/reexport-stub.s | ||
---|---|---|
16 | I've added the TODO to the DylibFile ctor. |
lld/test/MachO/reexport-stub.s | ||
---|---|---|
16 | The manpage entry for -no_implicit_dylibs confirms my guess: -no_implicit_dylibs When creating a two-level namespace final linked image, normally the linker will hoist up public dylibs that are implicitly linked to make the two-level namespace encoding more efficient for dyld. For example, Cocoa re-exports AppKit and AppKit re-exports Foundation. If you link with -framework Cocoa and use a symbol from Foundation, the linker will implicitly add a load command to load Foundation and encode the symbol as coming from Foundation. If you use this option, the linker will not add a load command for Foundation and encode the symbol as coming from Cocoa. Then at runtime dyld will have to search Cocoa and AppKit before finding the symbol in Foundation. |
Hi! I believe our mac builders are failing after this change with
FAIL: lld :: MachO/invalid/stub-link.s (2106 of 2478) ******************** TEST 'lld :: MachO/invalid/stub-link.s' FAILED ******************** Script: -- : 'RUN: at line 3'; mkdir -p /b/s/w/ir/k/staging/llvm_build/tools/lld/test/MachO/invalid/Output/stub-link.s.tmp : 'RUN: at line 5'; /b/s/w/ir/k/staging/llvm_build/bin/llvm-mc -filetype obj -triple x86_64-apple-ios /b/s/w/ir/k/llvm-project/lld/test/MachO/invalid/stub-link.s -o /b/s/w/ir/k/staging/llvm_build/tools/lld/test/MachO/invalid/Output/stub-link.s.tmp/test.o : 'RUN: at line 6'; not lld -flavor darwinnew -o /b/s/w/ir/k/staging/llvm_build/tools/lld/test/MachO/invalid/Output/stub-link.s.tmp/test -Z -L/b/s/w/ir/k/llvm-project/lld/test/MachO/invalid/../Inputs/iPhoneSimulator.sdk/usr/lib -lSystem /b/s/w/ir/k/staging/llvm_build/tools/lld/test/MachO/invalid/Output/stub-link.s.tmp/test.o 2>&1 | /b/s/w/ir/k/staging/llvm_build/bin/FileCheck /b/s/w/ir/k/llvm-project/lld/test/MachO/invalid/stub-link.s -- Exit Code: 1 Command Output (stderr): -- /b/s/w/ir/k/llvm-project/lld/test/MachO/invalid/stub-link.s:8:14: error: CHECK-DAG: expected string not found in input # CHECK-DAG: error: undefined symbol __cache_handle_memory_pressure_event ^ <stdin>:1:1: note: scanning from here lld: error: undefined symbol _from_non_reexported_tapi_dylib, referenced from test.o ^ <stdin>:1:6: note: possible intended match here lld: error: undefined symbol _from_non_reexported_tapi_dylib, referenced from test.o ^ Input file: <stdin> Check file: /b/s/w/ir/k/llvm-project/lld/test/MachO/invalid/stub-link.s -dump-input=help explains the following input dump. Input was: <<<<<< 1: lld: error: undefined symbol _from_non_reexported_tapi_dylib, referenced from test.o dag:8'0 X~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error: no match found dag:8'1 ? possible intended match >>>>>> --
I see that you sent out a fix for the objc.s test. Would you mind taking a look at this one? Thanks.
Builder: https://luci-milo.appspot.com/p/fuchsia/builders/ci/clang-mac-x64/b8870848238102897488?
Hmm, oddly enough the LLVM buildbots seem ok with it. But @thakis also mentioned the build failure on D86180: [lld-macho] Make it possible to re-export .tbd files...
I'm kicking off a build on another machine now. If I still can't figure it out in a bit I'll disable the test.
IIUC, the previous version would prefer the tbd to the dylib, and resolveDylibPath will prefer the dylib to the TBD.
IIRC, if ld64 finds both a TBD and a dylib for the same file, it verifies the TBD against the dylib and falls back to the dylib in case of a mismatch. I don't think we need to implement that (and I don't think LLVM's version of TextAPI has the equivalence checking logic yet anyway), so perhaps always preferring the dylib in that case (as you're doing now) is reasonable.