In some recent releases of macos (14.x and 15.x), we have noticed what seems to be a slower dlopen() implementation. I don't have any numbers to support this theory. I happened to notice this "slowness" when investigating something unrelated. In one part of the code we have a call of the form:
const char * fooBarLib = ....;
dlopen(fooBarLib, RTLD_NOW + RTLD_GLOBAL);
It so happened that due to some timing related issues, the process was crashing. A slow execution of code in this part of the code would trigger an issue in some other part of the code that would then lead to a process crash. The crash itself isn't a concern, because it's an internal issue that will addressed in the application code. What was interesting is that the slowness appears to be contributed by the call to dlopen(). Specifically, whenever a slowness was observed, the crash reports showed stack frames of the form:
Thread 1:
0 dyld 0x18f08b5b4 _kernelrpc_mach_vm_protect_trap + 8
1 dyld 0x18f08f540 vm_protect + 52
2 dyld 0x18f0b87e0 lsl::MemoryManager::writeProtect(bool) + 204
3 dyld 0x18f0a7fe4 invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 932
4 dyld 0x18f0e629c invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 172
5 dyld 0x18f0d9c38 invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 496
6 dyld 0x18f08c2dc dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 300
7 dyld 0x18f0d8bcc dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 192
8 dyld 0x18f0db5a0 dyld3::MachOFile::forEachInitializerPointerSection(Diagnostics&, void (unsigned int, unsigned int, bool&) block_pointer) const + 160
9 dyld 0x18f0e5f90 dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 432
10 dyld 0x18f0a7bb4 dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 176
11 dyld 0x18f0af190 dyld4::JustInTimeLoader::runInitializers(dyld4::RuntimeState&) const + 36
12 dyld 0x18f0a8270 dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&, dyld3::Array<dyld4::Loader const*>&) const + 312
13 dyld 0x18f0ac560 dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_0::operator()() const + 180
14 dyld 0x18f0a8460 dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 412
15 dyld 0x18f0c089c dyld4::APIs::dlopen_from(char const*, int, void*) + 2432
16 libjli.dylib 0x1025515b4 DoFooBar + 56
17 libjli.dylib 0x10254d2c0 Hello_World_Launch + 1160
18 helloworld 0x10250bbb4 main + 404
19 libjli.dylib 0x102552148 apple_main + 88
20 libsystem_pthread.dylib 0x18f4132e4 _pthread_start + 136
21 libsystem_pthread.dylib 0x18f40e0fc thread_start + 8
So, out of curiosity, have there been any known changes in the implementation of dlopen() which might explain the slowness?
Like I noted, I don't have concrete numbers, but to quantify the slowness I don't think it's slower by a noticeable amount - maybe a few milli seconds. I guess what I am trying to understand is, whether there's anything that needs attention here.
Post
Replies
Boosts
Views
Activity
Until recently, it was possible to view the profiles of every user who posted in the forums. The profile page would then have links to posts/replies and other pages so that one could "follow" the recent comments by those users.
However, it appears that it no longer is possible to view a profile of individual "DTS Engineer". What I mean is I can no longer view their profile page and as a result the recent posts/replies that a specific "DTS Engineer" makes.
It's especially a loss because posts made by such engineers are very helpful and valuable and not being able to easily follow such posts on a single page makes the forum software less useful.
Is there a way the previous feature can be brought back?
I am looking for inputs to better understand MacOS entitlements. I ask this in context of OpenJDK project, which builds and ships the JDK. The build process makes uses of make tool and thus doesn't involving building through the XCode product. The JDK itself is a Java language platform providing applications a set of standard APIs. The implementation of these standard APIs internally involves calling platform specific native library functions. In this discussion, I would like to focus on the networking functions that the implementation uses. Almost all of these networking functions and syscalls that the internal implementation uses are BSD socket related. Imagine calls to socket(), connect(), getsockopt(), setsockopt(), getaddrinfo(), sendto(), listen(), accept() and several such.
The JDK that's built through make is then packaged and made available for installation. The packaging itself varies, but for this discussion, I'll focus on the .tar.gz archived packaging. Within this archive there are several executables (for example: java, javac and others) and several libraries. My understanding, based on what I have read of MacOS entitlements is that, the entitlements are set on the executable and any libraries that would be loaded and used by that executable will be evaluated against the entitlements of the executable (please correct me if I misunderstand).
Reading through the list of entitlements noted here https://developer.apple.com/documentation/bundleresources/entitlements, the relevant entitlements that an executable (like "java") which internally invokes BSD socket related syscalls and library functions, appear to be:
com.apple.security.network.client - https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.security.network.client
com.apple.security.network.server - https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.security.network.server
com.apple.developer.networking.multicast - https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.networking.multicast
Is my understanding correct that these are the relevant ones for MacOS? Are there any more entitlements that are of interest? Would it then mean that the executables (java for example) would have to enroll for these entitlements to be allowed to invoke those functions at runtime?
Reading through https://developer.apple.com/documentation/bundleresources/entitlements, I believe that even when an executable is configured with these entitlements, when the application is running if that executable makes use of any operations for which it has an entitlement, the user is still prompted (through a UI notification) whether or not to allow the operation. Did I understand it right?
The part that isn't clear from that documentation is, if the executable hasn't been configured with a relevant entitlement, what happens when the executable invokes on such operation. Will the user see a UI notification asking permission to allow the operation (just like if an entitlement was configured)? Or does that operation just fail in some behind the scenes way?
Coming back to the networking specific entitlements, I found a couple of places in the MacOS documentation where it is claimed that the com.apple.developer.networking.multicast entitlement is only applicable on iOS. In fact, the entitlement definition page for it https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.networking.multicast says:
"Your app must have this entitlement to send or receive IP multicast or broadcast on iOS. It also allows your app to browse and advertise arbitrary Bonjour service types."
Yet, that same page, a few lines above, shows "macOS 10.0+". So, is com.apple.developer.networking.multicast entitlement necessary for an executable running on MacOS which deals with multicasting using BSD sockets?
As a more general comment about the documentation, I see that the main entitlements page here https://developer.apple.com/documentation/bundleresources/entitlements categorizes some of these entitlements under specific categories, for example, notice how some entitlements are categorized under "App Clips". I think it would be useful if there was a category for "BSD sockets" and under that it would list all relevant entitlements that are applicable, even if it means repeating the entitlement names across different categories. I think that will make it easier to identify the relevant entitlements.
Finally, more as a long term question, how does one watch or keep track of these required entitlements for these operations. What I mean is, is it expected that application developers keep visiting the macos documentation, like these pages, to know that a new entitlement is now required in a new macos (update) release? Or are there other ways to keep track of it? For example, if a newer macos requires a new entitlement, then when (an already built) executable is run on that version of macos, perhaps generate a notification or some kind of explicit error which makes it clear what entitlement is missing? I have read through https://developer.apple.com/documentation/bundleresources/diagnosing-issues-with-entitlements but that page focuses on identifying such issues when a executable is being built and doesn't explain the case where an executable has already been shipped with X entitlements and a new Y entitlement is now required to run on a newer version of macos.
Around a month back, developers of the OpenJDK project, when using XCode 16 to build the JDK started noticing odd failures when executing code which was compiled using the clang++ compiler shipped in that XCode 16 release (details in https://bugs.openjdk.org/browse/JDK-8340341). Specifically, a trivial for loop in a c++ code of the form:
int limit = ... // method local variable
for (i=0; i<limit; i++) {
...
}
ends up iterating more times than the specified limit. The "i<limit" returns true even when it should have returned false. In fact, debug log messages within the for loop of the form:
fprintf(stderr, "parsing %d of %d, %d < % d == %s", i, limit, i, limit, (i<limit) ? "true" : "false");
would show output of the form:
parsing 0 of 2, 0 < 2 == true
parsing 1 of 2, 1 < 2 == true
parsing 2 of 2, 2 < 2 == true
Notice, how it entered the for loop even when 2 < 2 should have prevented it from entering it. Furthermore, notice the message says 2 < 2 == true (which clearly isn't right).
This happens when that code is compiled with optimization level -O2 or -O3. The issue doesn't happen with -O1.
I had reported this as an issue to Apple through feedback assistance, more than a month back. The feedback id is FB15162411. There hasn't been any response to it nor any indication that the issue has been noticed and can be reproduced (the steps to reproduce have been provided in that issue). In the meantime, more and more users are now running into this failure in JDK when using XCode 16. We haven't put any workaround in place (the only workaround we know of is using -O1 for the compilation of this file) because it isn't clear what exactly is causing this issue (other than the fact that it shows up with specific optimization levels). It's also unknown if this bug has wider impact. Would it be possible to check if FB15162411 is being looked into and any technical details on what's causing this? That would help us decide if it's OK to put in place a temporary workaround in the OpenJDK build and how long to maintain that workaround.
For reference, this was reproduced on:
clang++ --version
Apple clang version 16.0.0 (clang-1600.0.26.3)
Target: arm64-apple-darwin23.6.0
Thread model: posix
InstalledDir: /xcode-16/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Consider this very trivial code which accesses the operatingSystemVersion property of NSProcessInfo as documented at https://developer.apple.com/documentation/foundation/nsprocessinfo/1410906-operatingsystemversion
osversion.c:
#include <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
fprintf(stderr, "OS version: %ld.%ld.%ld\n", osVersion.majorVersion, osVersion.minorVersion, osVersion.patchVersion);
}
Compile it:
/usr/bin/clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk/System/Library/Frameworks -x objective-c -o a.out -framework Foundation osversion.c
Then run it:
./a.out
It works fine and prints the OS version:
OS version: 14.6.1
Run it again and pass it some arbitrary program arguments:
./a.out foo bar
Still continues to work fine and prints the output:
OS version: 14.6.1
Now run it again and this time pass it two program arguments, the first one being - and the second one being something of the form {x=y}
./a.out - {x=y}
This time notice how it prints a couple of warning logs from CFPropertyListCreateFromXMLData before printing the output:
2024-10-11 11:18:03.584 a.out[61327:32412190] CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary on line 1. Parsing will be abandoned. Break on _CFPropertyListMissingSemicolon to debug.
2024-10-11 11:18:03.585 a.out[61327:32412190] CFPropertyListCreateFromXMLData(): Old-style plist parser: missing semicolon in dictionary on line 1. Parsing will be abandoned. Break on _CFPropertyListMissingSemicolon to debug.
OS version: 14.6.1
As far as I can see there's nothing wrong in the code nor the user inputs to the program. Is this some issue in the internal implementation of NSProcessInfo? Should this be reported as an issue through feedback assistant (which category)?
Although this example was run on 14.6.1 of macos, the issue is reproducible on older versions too.
I am in the middle of investigating an issue arising in the call to setsockopt syscall where it returns an undocumented and unexpected errno. As part of that, I'm looking for a way to list any socket content filters or any such extensions are in play on the system where this happens.
To do that, I ran:
systemextensionsctl list
That retuns the following output:
0 extension(s)
which seems to indicate there's no filters or extensions in play.
However, when I do:
netstat -s
among other things, it shows:
net_api:
2 interface filters currently attached
2 interface filters currently attached by OS
2 interface filters attached since boot
2 interface filters attached since boot by OS
...
4 socket filters currently attached
4 socket filters currently attached by OS
4 socket filters attached since boot
4 socket filters attached since boot by OS
What would be the right command/tool/options that I could use to list all the socket filters/extensions (and their details) that are in use and applicable when a call to setsockopt is made from an application on that system?
Edit: This is on a macosx-aarch64 with various different OS versions - 13.6.7, 14.3.1 and even 14.4.1.
I've been investigating an issue with the SO_OOBINLINE socket option. When that option is disabled, the expectation is that out-of-band data that is sent on the socket will not be available through the use of read() or recv() calls on that socket.
What we have been noticing is that when the socket is bound to a non-loopback address (and the communication is happening over that non-loopback address), then even when SO_OOBINLINE is disabled for the socket, the read()/recv() calls both return the out-of-band data. The issue however isn't reproducible with loopback address, and read()/recv() both correctly exclude the out-of-band data.
This issue is only noticed on macos. I have been able to reproduce on macos M1, following version, but the original report which prompted me to look into this was reported on macos x64. My M1 OS version is:
sw_vers
ProductName: macOS
ProductVersion: 14.3.1
BuildVersion: 23D60
Attached is a reproducer (main.c.txt - rename it to main.c after downloading) that I have been able to develop which reproduces this issue on macos. When you compile and run that:
./a.out
it binds to a non-loopback address by default and you should see the failure log, resembling:
...
ERROR: expected: 1234512345 but received: 12345U12345
To run the same reproducer against loopback address, run it as:
./a.out loopback
and that should succeed (i.e. no out-of-band data) with logs resembling:
...
SUCCESS: completed successfully, expected: 1234512345, received: 1234512345
Is this a bug in the OS? I would have reported this directly through feedback assistant, but my past few open issues (since more than a year) have not even seen an acknowledgement or a reply, so I decided to check here first.
main.c.txt
We have been observing an issue where when binding a UDP socket to an ephemeral port (i.e. port 0), the OS ends up allocating a port which is already bound and in-use. We have been seeing this issue across all macos versions we have access to (10.x through recent released 13.x).
Specifically, we (or some other process) create a udp4 socket bound to wildcard and ephemeral port. Then our program attempts a bind on a udp46 socket with ephemeral port. The OS binds this socket to an already in use port, for example you can see this netstat output when that happens:
netstat -anv -p udp | grep 51630
udp46 0 0 *.51630 *.* 786896 9216 89318 0 00000 00000000 00000000001546eb 00000000 00000800 1 0 000001
udp4 0 0 *.51630 *.* 786896 9216 89318 0 00000 00000000 0000000000153d9d 00000000 00000800 1 0 000001
51630 is the (OS allocated) port here, which as you can see has been allocated to 2 sockets. The process id in this case is the same (because we ran an explicit reproducer to reproduce this), but it isn't always the case.
We have a reproducer which consistenly shows this behaviour. Before filing a feedback assistant issue, I wanted to check if this indeed appears to be an issue or if we are missing something here, since this appears to be a very basic thing.
We have been noticing some mysterious port binds on our macos setups, where the syslogd process binds to a ephemeral port on UDP. This socket isn't bound from the time syslogd process starts, but something/ some event triggers this bind.
So we investigated further. It appears that one of the macos specific modules in syslogd is the "bsd_out" module which reads the config rules from a file called "/etc/syslog.conf". The contents of that file on my setup are:
cat /etc/syslog.conf
# Note that flat file logs are now configured in /etc/asl.conf
install.* @127.0.0.1:32376
These contents are the default ones shipped in macos and nothing has been edited/changed.
So it appears that the bsd_out module has been configured with a rule to send logs/messages in the "install" facility to be forwarded to some process which has a socket listening on loopback's 32376 port.
Whenever some software gets installed/uninstalled from the machine, it looks like a log message gets generated which falls under this "install" facility and then the bsd_out module binds a socket for UDP and uses that socket to send the data to 127.0.0.1:32376. You will notice that before installing/uninstalling any software the command:
sudo lsof -p <syslogd-pid>
will not list any UDP port. As soon as you install/uninstall something that socket gets bound and is visible in the output of the above command. The (bound) socket stays around.
The curious part is there's still no one/nothing that listens on that 32376 port. So it appears that this module is sending some datagrams that are just lost and not delivered? Is there a reason why the /etc/syslog.conf has this rule if there's nothing that's receiving that data?
The "man syslogd" page does state that bsd_out module is only there for backward compatibility, so perhaps this config rule in /etc/syslog.conf is just a left over that is no longer relevant?
I'm on macos 13.2.1:
sw_vers
ProductName: macOS
ProductVersion: 13.2.1
BuildVersion: 22D68
but this has been noticed on older version (even 10.15.x) too.
To reproduce, here are the steps:
Find the pid of syslogd (ps -aef | grep syslogd)
Find the resources used by this process including ports (sudo lsof -p <syslog-pid>)
At this point, ideally, you shouldn't see any UDP ports being used by this process
Install/uninstall any software (for example: move to trash and delete any installed application)
Run the lsof command again (sudo lsof -p <syslog-pid>), you will now see that it uses a UDP port bound to INADDR_ANY address and an ephemeral port:
syslogd 12345 root 11u IPv4 0xf557ad678c99264b 0t0 UDP *:56972
netstat output too will show that port (for example: netstat -anv -p UDP)
I have a setup where /bin/ksh constantly crashes. The generated crash logs available at ~/Library/Logs/DiagnosticReports aren't too helpful because the content there contains only hex references to the ksh stack backtrace.
I then ran "dtruss" against the crashing /bin/ksh program and even that isn't helping narrow it down because that too uses hex digits in the stack backtrace.
What would be the right way to debug this crashing ksh program? Are there debug symbols available for the /bin/ksh binary shipped in macos M1 (13.0.1 version)?
I found some documentation about using XCode to "symbolicate" the crash logs, but following those instructions too wasn't helpful because even after loading the crash report in the XCode "Device Logs" window, it still only showed hex references.
Hello,
I have been looking at some TCP related configurations on Linux and then checking their counterparts on macos. On macos, I see these 2 (among many others) from "sysctl -a" output:
net.inet.tcp.ack_strategy: 1
...
net.inet.tcp.delayed_ack: 3
What does the ack_strategy = 1 imply and what does the value 3 for delayed_ack imply? Is there some additional documentation on what values are supported here and what each one implies? I did a basic search but couldn't find any details about these configurations.
We have some extensive tests which exercise UDP communication. Some of these tests fail fairly often due to the UDP packet being dropped by the kernel (or related reasons). These tests use loopback interface for communication. I have been looking to see if there's a way to pinpoint or narrow down exactly why a particular packet was dropped by the kernel. Looking at the kernel code, like here https://github.com/apple-opensource/xnu/blob/master/bsd/netinet/udp_usrreq.c#L1463 it appears that there are log message that get written out during some of this communication. However, looking at what KERNEL_DEBUG stands for, it appears that it's:
/*
* Traced only on debug kernels.
*/
#define KDBG_DEBUG(x, ...) KDBG_(_DEBUG, x, ## __VA_ARGS__, 4, 3, 2, 1, 0)
So I don't think these logs get generated in a regular release build of the OS.
Are there any other ways we can generate similar logs or any other tools that will give a clearer picture of why the packet might be drop?
In this thread https://developer.apple.com/forums/thread/705281 (which I am the original poster), I have marked my own post as the accepted answer. I did that before I got input from Quinn on how to achieve what I was after. I have posted that information in a subsequent post in that thread here https://developer.apple.com/forums/thread/705281?answerId=712746022#712746022. Can someone please mark this as the correct answer? I tried but I don't see any option to do it myself.
P.S: In some forum threads, I see that things like these are sorted through direct messages sometimes. But I can't see any place in my profile where I can send a direct message from. Does that feature require some level of forum points?
The man page of "connect" states (snippet below):
".... datagram sockets may use connect() multiple times to change their association. Datagram sockets may dissolve the association by calling disconnectx(2), or by connecting to an invalid address, such as a null address ..."
Up until 12.3.1 (and even 12.4 Beta1), if a connect() call was used on a datagram socket to disconnect an IPv4-mapped IPv6 address, the call used to complete and return back "EADDRNOTAVAIL". Internally it would indeed dissolve the current association of the connected datagram socket (i.e. subsequent calls to "send" would return back a "EDESTADDRREQ" errno).
However, starting 12.4 Beta2, if a connect() call is made on such a datagram socket which is connected to a IPv4-mapped IPv6 address, the call now returns a "EINVAL" errno. Do note that this change in behaviour is happening in this Beta release only with IPv4-mapped IPv6 addresses. Other addresses like plain IPv6 address, continue to return "EADDRNOTAVAIL" on such connect() calls which are used to dissolve the current association.
We have been able to reproduce this issue with a trivial C program that is attached to this discussion. The attached program, creates a datagram socket which then connects to a IPv4-mapped IPv6 loopback address. This connect is expected to succeed and does succeed. Once the connect is done, a next connect() is issued on the connected datagram socket, this time passing it a null address (as noted in the man page) and this call is expected to dissolve the connection association. The program expects a EADDRNOTAVAIL to be returned by this call (since that's what was being returned in all MacOS releases so far) and if some other errno gets returned then the program fails.
When the attached reproducer is run against 12.3.1 (or other previous MacOS versions or even 12.4 Beta1), the program gets the expected EADDRNOTAVAIL and when it is run against 12.4 Beta2, it gets the unexpected EINVAL errno.
Here's a sample output from 12.3.1 (the "successful" one):
$> sw_vers
ProductName: macOS
ProductVersion: 12.3.1
BuildVersion: 21E258
$> clang main.c
$> ./a.out
created a socket, descriptor=3
trying to bind to addr ::ffff:127.0.0.1:0, addr family=30
Bound socket to ::ffff:127.0.0.1:46787, addr family=30
created another socket, descriptor=4
connecting to ::ffff:127.0.0.1:46787, addr family=30
successfully connected
Disconnecting using addr :::0, addr family=30
got errno 49, which is considered OK for a connect to null address (a.k.a disconnect)
Successfully disconnected
Output from 12.4 Beta2 (the case where the behaviour has changed):
$> sw_vers
ProductName: macOS
ProductVersion: 12.4
BuildVersion: 21F5058e
$> clang main.c
$> ./a.out
created a socket, descriptor=3
trying to bind to addr ::ffff:127.0.0.1:0, addr family=30
Bound socket to ::ffff:127.0.0.1:23015, addr family=30
created another socket, descriptor=4
connecting to ::ffff:127.0.0.1:23015, addr family=30
successfully connected
Disconnecting using addr :::0, addr family=30
Got unexpected errno 22
disconnect failed: Invalid argument
Notice how the call to connect() to dissolve the connected association now returns errno 22 == Invalid Argument.
We had a look at the release notes here https://developer.apple.com/documentation/macos-release-notes/macos-12_4-release-notes but they appear to be very high level release notes and we couldn't find anything relevant related to this change. So:
Is this an intentional change? Are applications expected to account for this errno to be returned when calling connect() to dissolve a connected association of a datagram socket? If this is intentional, is there a reason why this is specific only for IPv4-mapped IPv6 address?
The man connect page also talks about using disconnectx as an alternative way to dissolve the association. This appears to be specific to MacOS systems (couldn't find it in BSD man pages for example). Is disconnectx the recommended way to do the dissociation?
P.S: I created a issue for this through the bug/feedback reporting tool (issue id: FB9996296), but there hasn't been any response to it since almost a week. So creating this thread in the forums hoping to get some inputs.
main.c
I realize that recent versions of MacOS ship with a dynamic library cache. I am looking for inputs on how to list the dependencies of one such system library that's part of this cache.
Consider the case where I have a libfoo library which when inspected through "otool -L libfoo.dylib" shows this output:
otool -L libfoo.dylib
/home/me/lib/libjli.dylib:
@rpath/libjli.dylib (compatibility version 1.0.0, current version 1.0.0)
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 23.0.0)
/System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 60157.60.19)
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices (compatibility version 1.0.0, current version 56.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1311.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1856.105.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1856.105.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
One of the dependencies in that library is the /usr/lib/libSystem.B.dylib. Previously, I could just do otool -L /usr/lib/libSystem.B.dylib to find the dependencies of libSystem.B.dylib, but now with the dynamic library cache in picture, if I do:
otool -L /usr/lib/libSystem.B.dylib
error: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/otool-classic: can't open file: /usr/lib/libSystem.B.dylib (No such file or directory)
So how do I get otool to show the dependencies of this cache library. Are there any other tools instead of otool which can do this in recent versions of MacOS?
I'm on 12.3.1 of macOS