Hardened Runtime: dyld: Library not loaded

Hi,


I've released and packaged an application, and I have a user reporting an error when trying to start the application:


/Applications/Onivim2.app/Contents/MacOS/Oni2 -f --trace --debug
dyld: Library not loaded: @executable_path/../Frameworks/libffi.6.dylib
  Referenced from: /Applications/Onivim2.app/Contents/MacOS/Oni2_editor
  Reason: no suitable image found. Did find:
 /Applications/Onivim2.app/Contents/MacOS/../Frameworks/libffi.6.dylib: open() failed with errno=13
 /Applications/Onivim2.app/Contents/MacOS/../Frameworks/libffi.6.dylib: stat() failed with errno=1
 /Applications/Onivim2.app/Contents/MacOS/../Frameworks/libffi.6.dylib: open() failed with errno=13
 /Applications/Onivim2.app/Contents/MacOS/../Frameworks/libffi.6.dylib: stat() failed with errno=13
 file system relative paths not allowed in hardened programs


I'm currently code-signing with the hardened runtime and these entitlements:


com.apple.security.cs.allow-jit

com.apple.security.cs.allow-unsigned-executable-memory

com.apple.security.cs.disable-library-validation


I saw an issue another application ran into, which suggests the problem was with @executable_path, and that they were able to solve their problem by adding the 'com.apple.security.cs.allow-dyld-environment-variables' entitlement. I'm going to test that out and give it a shot!


However, the concerning aspect is I'm not able to reproduce this issue on any OSX environment I've tried. I have access to two environments:


- 10.14.6

- 10.15.3


I'm not able to reproduce this error on either machine - the application starts up successfully. I'd like to figure out how to test for this, so that I can verify the fix.


So two questions:


- Is there anything I should look at, aside from the allow-dyld-environment-variables entitlement?

- Is there any setting I should adjust so I can reproduce this issue locally?


Thank you for your help!

However, the concerning aspect is I'm not able to reproduce this issue on any [macOS] environment I've tried.

Have you tried quarantining the download? The way I test stuff like this is as follows:

  1. I restore a VM from a fresh snapshot that’s never seen my product.

  2. I download the product using a mechanism, like Safari, that quarantines the download.

  3. I install and run the product like a user would.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi eskimo,


Thank you for the response and suggestions! I did attempt to to reproduce it by creating a fresh user and downloading it via Chrome. I see the 'Verification' and quarantine dialog come up (This application was downloaded from the internet..), as expected. Unfortunately for me - this doesn't reproduce, and I'm able to run it successfully.


I also pushed out a build with the com.apple.security.cs.allow-dyld-environment-variables entitlement, but I'm still getting a report of a crash here:


/Applications/Onivim2.app/Contents/MacOS/Oni2 -f --trace --debug
dyld: Library not loaded: @executable_path/../Frameworks/libffi.6.dylib
  Referenced from: /Applications/Onivim2.app/Contents/MacOS/Oni2_editor
  Reason: no suitable image found. Did find:
 /Applications/Onivim2.app/Contents/MacOS/../Frameworks/libffi.6.dylib: open() failed with errno=13
 /Applications/Onivim2.app/Contents/MacOS/../Frameworks/libffi.6.dylib: stat() failed with errno=1
 /Applications/Onivim2.app/Contents/MacOS/../Frameworks/libffi.6.dylib: open() failed with errno=13
 /Applications/Onivim2.app/Contents/MacOS/../Frameworks/libffi.6.dylib: stat() failed with errno=13


Running `otool -L` on my published executable - I have a few `dylibs` that I bundle that use `@executable_path`:

compatibility version 150.0.0
Load command 24
          cmd LC_LOAD_DYLIB
      cmdsize 80
         name @executable_path/../Frameworks/libSDL2-2.0.0.dylib (offset 24)
   time stamp 2 Wed Dec 31 16:00:02 1969
      current version 11.0.0
compatibility version 11.0.0
compatibility version 11.0.0
Load command 25
          cmd LC_LOAD_DYLIB
      cmdsize 72
         name @executable_path/../Frameworks/libffi.6.dylib (offset 24)
   time stamp 2 Wed Dec 31 16:00:02 1969
      current version 7.4.0
compatibility version 7.0.0


I validated that the codesigning was OK with codesign:

Bryans-Mac-mini:MacOS bryphe$ codesign --verify --verbose=4 /Applications/Onivim2.app
--prepared:/Applications/Onivim2.app/Contents/MacOS/rg
--validated:/Applications/Onivim2.app/Contents/MacOS/rg
--prepared:/Applications/Onivim2.app/Contents/MacOS/rls
--validated:/Applications/Onivim2.app/Contents/MacOS/rls
--prepared:/Applications/Onivim2.app/Contents/MacOS/Oni2_editor
--validated:/Applications/Onivim2.app/Contents/MacOS/Oni2_editor
--prepared:/Applications/Onivim2.app/Contents/MacOS/node
--validated:/Applications/Onivim2.app/Contents/MacOS/node
--prepared:/Applications/Onivim2.app/Contents/Frameworks/libSDL2-2.0.0.dylib
--validated:/Applications/Onivim2.app/Contents/Frameworks/libSDL2-2.0.0.dylib
--prepared:/Applications/Onivim2.app/Contents/Frameworks/libonig.5.dylib
--validated:/Applications/Onivim2.app/Contents/Frameworks/libonig.5.dylib
--prepared:/Applications/Onivim2.app/Contents/Frameworks/libharfbuzz.0.dylib
--validated:/Applications/Onivim2.app/Contents/Frameworks/libharfbuzz.0.dylib
--prepared:/Applications/Onivim2.app/Contents/Frameworks/libffi.6.dylib
--validated:/Applications/Onivim2.app/Contents/Frameworks/libffi.6.dylib
/Applications/Onivim2.app: valid on disk
/Applications/Onivim2.app: satisfies its Designated Requirement


And that the entitlements were correct:

Bryans-Mac-mini:MacOS bryphe$ codesign -d --entitlements - /Applications/Onivim2.app
Executable=/Applications/Onivim2.app/Contents/MacOS/Oni2?qq?


  
    com.apple.security.cs.allow-jit
    
    com.apple.security.cs.allow-unsigned-executable-memory
    
    com.apple.security.cs.disable-library-validation
    
    com.apple.security.cs.allow-dyld-environment-variables
    
  

Bryans-Mac-mini:MacOS bryphe$

Given the permission error - I also tried installing as Standard user as well as an Administrator user. That succeeded as well, although I needed to give adminstrator access.


Do you have any other ideas or things I should try to narrow it down?


Thank you for your help!

You will need to test things in a virutal machine. Your development machine, regardless of user, may have the necessary files already installed in various paths, perhaps without quarantine flags. The app could find those and run happily.


Also, your embedded frameworks are using @executable_path, not @rpath. I'm not sure if @executable_path is going to work with modern security requirements. I used to use that when hacking things up years ago. But now, for production-quality code, everything is @rpath.


Here is how I debug these issues. I try to find a simple example of how Xcode would do the same thing. Then I compare how Xcode does it with what I'm doing. If there is a discrepancy, I have to justify that. In this case, Xcode uses @rpath for embedded frameworks. I think you were led down the wrong path in that RStudio/Firefox thread. @executable_path doesn't have anything to do with that "com.apple.security.cs.allow-dyld-environment-variables" entitlement. That entitlement is for the DLYD_ environment variables.


I can't guarantee that @rpath will fix the problem. libffi is one of those funky things. You might want to try a piecemeal process. Setup a VM. Build your app with just a signature. Either transfer it via a non-quarantine route or just remove the quarantine flags. Verify the code signature with code_sign and spctl. See if it runs. Then, take it step by step. Reset your VM. Add the hardened runtime, try again. Reset the VM. Transfer with quarantine flags and try that.


Because you have embedded dylibs, you will need to use otool -L (and maybe otool -l) to verify the embedded libraries. Furthermore, you have to do all of that again with each embedded library. They could be attempting to load other embedded libraries. Where are they trying to load them from? Speaking of which, where did you get these dylibs from? Please don't say homebrew. You have to be careful how you build some of these open-source dylibs. The funkier the dylib, the more careful you have to be. They may have embedded, and hard-coded, install paths and/or environment variable expectations. You can manage those, but you have to find out those details for each open source dylib you are using.

Hardened Runtime: dyld: Library not loaded
 
 
Q