DYLD_INSERT_LIBRARIES causes dyld to fail when SIP is disabled

Running an executable with DYLD_INSERT_LIBRARIES set and SIP disabled causes dyld to fail with the following error: (fat file, but missing compatible architecture (have 'x86_64,arm64', need '')). The same command succeeds without error when SIP is enabled. Is this intended behavior? If not, how can I solve the issue? Both the library to be inserted and the target binary are code signed, and the target binary has "Allow DYLD Environment Variables" enabled. This issue is not limited to my code, either- when setting DYLD_INSERT_LIBRARIES with launchctl setenv DYLD_INSERT_LIBRARIES <dylib path>, most apps fail to launch if SIP is disabled. In this case, I was able to launch Safari and Finder, but no third-party apps launched, and I could not find any other system apps that launched.

macOS 13.3.1 (a), but this issue has existed for a while.

Replies

Running an executable with DYLD_INSERT_LIBRARIES set and SIP disabled causes dyld to fail with the following error

Is this on Apple silicon?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Is this on Apple silicon?

It is

In that case I have a theory as to what’s happening here.

macOS has the concept of ‘protected’ executables. This includes apps downloaded from the App Store, programs signed with the hardened runtime enabled, and stuff that’s built in to the system. In general, the dynamic linker does not honour DYLD_INSERT_LIBRARIES for protected executables [1].

This explains why things aren’t failing in the SIP-enabled case: The dynamic linker is simply ignoring your DYLD_INSERT_LIBRARIES setting. However, if you disable SIP then that protection goes away.

As to why things fail in the SIP-disabled case, I suspect that’s related to arm64e. Most built-in system components [2] use the arm64e architecture. This enables pointer authentication. If you tell an arm64e executable to load an arm64 library, it’ll fail [3]. And that’s exactly what you’re doing here.

You might be able to work around this by building your library as arm64e.

WARNING Do not ship code this way. The reason why arm64e is not enabled by default is that it doesn’t yet have a stable ABI. This isn’t a problem for built-in stuff because it’s revlocked to the system. However, if you ship an arm64e binary independently, it’s likely to break as we evolve the ABI.

Then again, you shouldn’t be shipping stuff that requires SIP, or stuff that tries to insert libraries into system executables. But you probably know that already (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] In some cases it’s possible to opt out of this protection. For example, in the hardened runtime case, there’s the com.apple.security.cs.allow-dyld-environment-variables exception entitlement.

[2] But not all. Some subsystems need to load third-party code and those are specifically built arm64.

[3] Note that the reverse is not true in general. We have set things up so that an arm64 executable can use arm64e libraries.