Post

Replies

Boosts

Views

Activity

Any recent changes to dlopen() implementation?
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.
3
0
455
Mar ’25
macos entitlements - com.apple.security.cs.allow-unsigned-executable-memory vs com.apple.security.cs.allow-jit
In context of entitlements that are applicable on macos platform, I was discussing in another thread about the com.apple.security.cs.allow-unsigned-executable-memory and the com.apple.security.cs.allow-jit entitlements in a hardened runtime https://developer.apple.com/forums/thread/775520?answerId=827440022#827440022 In that thread it was noted that: The hardened runtime enables a bunch of additional security checks. None of them are related to networking. Some of them are very important to a Java VM author, most notably the com.apple.security.cs.allow-jit -> com.apple.security.cs.allow-unsigned-executable-memory -> com.apple.security.cs.disable-executable-page-protection cascade. My advice on that front: This sequence is a trade off between increasing programmer convenience and decreasing security. com.apple.security.cs.allow-jit is the most secure, but requires extra work in your code. Only set one of these entitlements, because each is a superset of its predecessor. com.apple.security.cs.disable-executable-page-protection is rarely useful. Indeed, on Apple silicon [1] it’s the same as com.apple.security.cs.allow-unsigned-executable-memory. If you want to investigate moving from com.apple.security.cs.allow-unsigned-executable-memory to com.apple.security.cs.allow-jit, lemme know because there are a bunch of additional resources on that topic. What that tells me is that com.apple.security.cs.allow-jit is the recommended entitlement that retains enough security and yet provides the necessary programmer convenience for applications. In the OpenJDK project we use both com.apple.security.cs.allow-unsigned-executable-memory and com.apple.security.cs.allow-jit entitlements for the executables shipped in the JDK (for example java). I was told in that other thread that it might be possible to just use the com.apple.security.cs.allow-unsigned-executable-memory, but there are some additional details to consider. I'm starting this thread to understand what those details are.
3
0
397
Mar ’25
Where is the instruments command line tool?
I was reading through this documentation about instruments command line tool https://help.apple.com/instruments/mac/current/#/devb14ffaa5 and how it can be launched from the command line. However, unlike what the documentation states, there's no such instruments command anywhere on my macos M1 (OS version 15.6). That command gives: $> instruments zsh: command not found: instruments I do have XCode installed which has the Instruments.App (GUI app) but not the command line utility: $> ls Xcode.app/Contents/Applications/ ... Instruments.app Is that linked documentation up-to-date (it does say "latest" in the URL)? Is there some other way to install this command line utility?
3
0
212
Aug ’25
Using otool to list dependencies of a system library in dynamic library cache
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
2
0
8.8k
May ’22
TCP delayed ACK configuration on macos
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.
2
0
2.3k
Sep ’22
sendto() system call doesn't return an error even when there is one
Please consider this very trivial C code, which was run on 15.3.1 of macos: #include <stdio.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include "sys/socket.h" #include <string.h> #include <unistd.h> #include <ifaddrs.h> #include <net/if.h> // prints out the sockaddr_in6 void print_addr(const char *msg_prefix, struct sockaddr_in6 sa6) { char addr_text[INET6_ADDRSTRLEN] = {0}; printf("%s%s:%d, addr family=%u\n", msg_prefix, inet_ntop(AF_INET6, &sa6.sin6_addr, (char *) &addr_text, INET6_ADDRSTRLEN), sa6.sin6_port, sa6.sin6_family); } // creates a datagram socket int create_dgram_socket() { const int fd = socket(AF_INET6, SOCK_DGRAM, 0); if (fd < 0) { perror("Socket creation failed"); return -1; } return fd; } int main() { printf("current process id:%ld parent process id: %ld\n", (long) getpid(), (long) getppid()); // // hardcode a link-local IPv6 address of a interface which is down // ifconfig: // ,,, // awdl0: flags=8822<BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500 // options=6460<TSO4,TSO6,CHANNEL_IO,PARTIAL_CSUM,ZEROINVERT_CSUM> // ... // inet6 fe80::34be:50ff:fe14:ecd7%awdl0 prefixlen 64 scopeid 0x10 // nd6 options=201<PERFORMNUD,DAD> // media: autoselect (<unknown type>) // status: inactive // const char *ip6_addr_str = "fe80::34be:50ff:fe14:ecd7"; // link-local ipv6 address from above ifconfig output // parse the string literal to in6_addr struct in6_addr ip6_addr; int rv = inet_pton(AF_INET6, ip6_addr_str, &ip6_addr); if (rv != 1) { fprintf(stderr, "failed to parse ipv6 addr %s\n", ip6_addr_str); exit(EXIT_FAILURE); } // create a AF_INET6 SOCK_DGRAM socket const int sock_fd = create_dgram_socket(); if (sock_fd < 0) { exit(EXIT_FAILURE); } printf("created a socket, descriptor=%d\n", sock_fd); // create a destination sockaddr which points to the above // ipv6 link-local address and an arbitrary port const int dest_port = 12345; struct sockaddr_in6 dest_sock_addr; memset((char *) &dest_sock_addr, 0, sizeof(struct sockaddr_in6)); dest_sock_addr.sin6_addr = ip6_addr; dest_sock_addr.sin6_port = htons(dest_port); dest_sock_addr.sin6_family = AF_INET6; dest_sock_addr.sin6_scope_id = 0x10; // scopeid from the above ifconfig output // now sendto() to that address, whose network interface is down. // we expect sendto() to return an error print_addr("sendto() to ", dest_sock_addr); const char *msg = "hello"; const size_t msg_len = strlen(msg) + 1; rv = sendto(sock_fd, msg, msg_len, 0, (struct sockaddr *) &dest_sock_addr, sizeof(dest_sock_addr)); if (rv == -1) { perror("sendto() expectedly failed"); close(sock_fd); exit(EXIT_FAILURE); } printf("sendto() unexpectedly succeeded\n"); // should not reach here, we expect sendto() to return an error return 0; } It creates a SOCK_DGRAM socket and attempts to sendto() to a link-local IPv6 address of a local network interface which is not UP. The sendto() is expected to fail with a "network is down" (or at least fail with some error). Let's see how it behaves. Copy that code to a file called netdown.c and compile it as follows: clang netdown.c Now run the program: ./a.out That results in the following output: current process id:29290 parent process id: 21614 created a socket, descriptor=3 sendto() to fe80::34be:50ff:fe14:ecd7:14640, addr family=30 sendto() unexpectedly succeeded (To reproduce this locally, replace the IPv6 address in that code with a link-local IPv6 address of an interface that is not UP on your system) Notice how the sendto() returned successfully without any error giving an impression to the application code that the message has been sent. In reality, the message isn't really sent. Here's the system logs from that run: PID Type Date & Time Process Message debug 2025-03-13 23:36:36.830147 +0530 kernel Process (a.out) allowed via dev tool environment (/System/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal) debug 2025-03-13 23:36:36.833054 +0530 kernel [SPI][HIDSPI] TX: 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 RX: 20 02 00 00 00 00 38 00 10 02 00 17 00 00 2E 00 26700 error 2025-03-13 23:36:36.838607 +0530 nehelper Failed to get the signing identifier for 29290: No such process 26700 error 2025-03-13 23:36:36.838608 +0530 nehelper Failed to get the code directory hash for 29290: No such process default 2025-03-13 23:36:36.840070 +0530 kernel cfil_dispatch_attach_event:3507 CFIL: Failed to get effective audit token for <sockID 22289651233205710 <4f3051d7ec2dce>> 26700 error 2025-03-13 23:36:36.840678 +0530 nehelper Failed to get the signing identifier for 29290: No such process 26700 error 2025-03-13 23:36:36.840679 +0530 nehelper Failed to get the code directory hash for 29290: No such process default 2025-03-13 23:36:36.841742 +0530 kernel cfil_hash_entry_log:6082 <CFIL: Error: sosend_reinject() failed>: [29290 ] <UDP(17) out so 891be95f39bd0385 22289651233205710 22289651233205710 age 0> lport 60244 fport 12345 laddr fe80::34be:50ff:fe14:ecd7 faddr fe80::34be:50ff:fe14:ecd7 hash D7EC2DCE default 2025-03-13 23:36:36.841756 +0530 kernel cfil_service_inject_queue:4466 CFIL: sosend() failed 50 Notice the last line where it states the sosend() (and internal impl detail of macos) failed with error code 50, which corresponds to ENETDOWN ("Network is down"). However, like I noted, this error was never propagated back to the application from the sendto() system call. The documentation of sendto() system call states: man sendto ... Locally detected errors are indicated by a return value of -1. ... RETURN VALUES Upon successful completion, the number of bytes which were sent is returned. Otherwise, -1 is returned and the global variable errno is set to indicate the error. So I would expect sendto() to return -1, which it isn't. The 15.3.1 source of xnu hasn't yet been published but there is the 15.3 version here https://github.com/apple-oss-distributions/xnu/tree/xnu-11215.81.4 and looking at the corresponding function cfil_service_inject_queue, line 4466 (the one which is reported in the logs) https://github.com/apple-oss-distributions/xnu/blob/xnu-11215.81.4/bsd/net/content_filter.c#L4466, the code there logs this error and the cfil_service_inject_queue function then returns back the error. However, looking at the call sites of the call to cfil_service_inject_queue(...), there are several places within that file which don't track the return value (representing an error value) and just ignore it. Is that intentional and does that explain this issue? Does this deserve to be reported as a bug through feedback assistant?
2
0
363
Mar ’25
Documentation for SYSTEM_VERSION_COMPAT
Is there some official documentation about the SYSTEM_VERSION_COMPAT environment variable and how it affects the version reported by tools like sw_vers and whether the presence of that environment variable affects APIs like NSOperatingSystemVersion? I ask this in context of recent macOS 26 Beta version where NSOperatingSystemVersion from older versions of XCode (for example XCode 15.4) report the macOS version as 16.0.
2
0
75
Jul ’25
macos 15.6.1 - BSD sendto() fails for IPv4-mapped IPv6 addresses
There appears to be some unexplained change in behaviour in the recent version of macos 15.6.1 which is causing the BSD socket sendto() syscall to no longer send the data when the source socket is bound to a IPv4-mapped IPv6 address. I have attached a trivial native code which reproduces the issue. What this reproducer does is explained as a comment on that code's main() function: // Creates a AF_INET6 datagram socket, marks it as dual socket (i.e. IPV6_V6ONLY = 0), // then binds the socket to a IPv4-mapped IPv6 address (chosen on the host where this test runs). // // The test then uses sendto() to send some bytes. For the sake of this test, it uses the same IPv4-mapped // IPv6 address as the destination address to sendto(). The test then waits for (a maximum of) 15 seconds to // receive that sent message by calling recvfrom(). // // The test passes on macos (x64 and aarch64) hosts of versions 12.x, 13.x, 14.x and 15.x upto 15.5. // Only on macos 15.6.1 and the recent macos 26, the test fails. Specifically, the first message that is // sent using sendto() is never sent (and thus the recvfrom()) times out. sendto() however returns 0, // incorrectly indicating a successful send. Interesting, if you repeat sendto() a second message from the // same bound socket to the exact same destination address, the send message is indeed correctly sent and // received immediately by the recvfrom(). It's only the first message which goes missing (the test uses // unique content in each message to be sure which exact message was received and it has been observed that // only the second message is received and the first one lost). // // Logs collected using "sudo log collect --last 2m" (after the test program returns) shows the following log // message, which seem relevant: // ... // default kernel cfil_hash_entry_log:6088 <CFIL: Error: sosend_reinject() failed>: // [86868 a.out] <UDP(17) out so 59faaa5dbbcef55d 127846646561221313 127846646561221313 age 0> // lport 65051 fport 65051 laddr 192.168.1.2 faddr 192.168.1.2 hash 201AAC1 // default kernel cfil_service_inject_queue:4472 CFIL: sosend() failed 22 // ... // As noted, this test passes without issues on various macosx version (12 through 15.5), both x64 and aarch64 but always fails against 15.6.1. I have been told that it also fails on the recently released macos 26 but I don't have access to such host to verify it myself. The release notes don't usually contain this level of detail, so it's hard to tell if something changed intentionally or if this is a bug. Should I report this through the feedback assistant? Attached is the source of the reproducer, run it as: clang dgramsend.c ./a.out On macos 15.6.1, you will see that it will fail to send (and thus receive) the message on first attempt but the second one passes: ... created and bound a datagram dual socket to ::ffff:192.168.1.2:65055 ::ffff:192.168.1.2:65055 sendto() ::ffff:192.168.1.2:65055 ---- Attempt 1 ---- sending greeting "hello 1" sendto() succeeded, sent 8 bytes calling recvfrom() receive timed out --------------------- ---- Attempt 2 ---- sending greeting "hello 2" sendto() succeeded, sent 8 bytes calling recvfrom() received 8 bytes: "hello 2" --------------------- TEST FAILED ... The output "log collect --last 2m" contains a related error (and this log message consistently shows up every time you run that reproducer): ... default kernel cfil_hash_entry_log:6088 <CFIL: Error: sosend_reinject() failed>: [86248 a.out] <UDP(17) out so 59faaa5dbbcef55d 127846646561221313 127846646561221313 age 0> lport 65055 fport 65055 laddr 192.168.1.2 faddr 192.168.1.2 hash 201AAC1 default kernel cfil_service_inject_queue:4472 CFIL: sosend() failed 22 ... I don't know what it means though. dgramsend.c
2
0
222
Sep ’25
Does the zlib shipped MacOS 12.2.1 have debug logging capability?
I see that MacOS 12.2.1 ships in its dynamic libraries cache the zlib 1.2.11 version. The zlib library has the ability to log messages (at runtime) if it was configured during compilation with the --debug/-d option[1] (which internally sets the ZLIB_DEBUG compiler flag). How does one go about finding the config options that were used to build this system library (or any system library for that matter that's shipped in MacOS)? Is the current one shipped enabled for this option? [1] https://github.com/madler/zlib/blob/v1.2.11/configure#L139 [2] https://github.com/madler/zlib/blob/v1.2.11/configure#L202
1
0
1.7k
Mar ’22
Debugging a crashing ksh?
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.
1
0
649
Feb ’23
Cannot access profile page of individual DTS engineers
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?
0
1
320
Feb ’25