RPATH switch from DYLD_LIBRARY_PATH: strange results

I am trying to modify an existing bundle so that I can notarize it. Here its organisation:

 graps.app
    	Contents
    		MacOS
   			  prelaunch <-- main
     			applauncher
          grasp  <-- auxiliary binary calling the libraries

Here the original DYLD_LIBRARY_PATH:

DYLD_LIBRARY_PATH="."
DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:/Applications/MATLAB/MATLAB_Runtime/v911/runtime/maci64"
DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:/Applications/MATLAB/MATLAB_Runtime/v911/bin/maci64"
DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:/Applications/MATLAB/MATLAB_Runtime/v911/sys/os/maci64"
export DYLD_LIBRARY_PATH

and the RPATHs that mimic it:

install_name_tool -add_rpath "/." grasp
install_name_tool -add_rpath "/Applications/MATLAB/MATLAB_Runtime/v911/bin/maci64" grasp
install_name_tool -add_rpath "/Applications/MATLAB/MATLAB_Runtime/v911/runtime/maci64" grasp
install_name_tool -add_rpath "/Applications/MATLAB/MATLAB_Runtime/v911/sys/os/maci64" grasp
install_name_tool -add_rpath "/Applications/MATLAB/MATLAB_Runtime/v911/extern/bin/maci64" grasp

Note that it is not enough to add RPATHs to the auxiliary binary "grasp".

They must also be added to the main binary "prelaunch".

But why ?

1- Unsigned application using DYLD_LIBRARY_PATH

set        RPATHs
set        DYLD_LIBRARY_PATH
not set    DYLD_PRINT_LIBRARIES
	### App running ###

2- Unsigned application with lib tracking

set        RPATHs
set        DYLD_LIBRARY_PATH
set        DYLD_PRINT_LIBRARIES
	### App hangs after loading 894 libraries (MatLab has more than 3000 libs and uses many macOS libs)
	### The terminal output is a total of 119594 characters. 
  ### Is this more than allowed by `DYLD_PRINT_LIBRARIES`?
	### As a consequence the check cannot be completed. Too bad !!!

3- Unsigned application using RPATHs

set        RPATHs
not set    DYLD_LIBRARY_PATH
set or not DYLD_PRINT_LIBRARIES 
	### Error: Could not find version 9.11 of the MATLAB Runtime.
	### Attempting to load libmwmclmcrrt.9.11.dylib.       <-- it does not say "not found"!

307 libs are loaded before this error message occurs

59 are MatLab libs all from /bin/maci64 but the following one is from /runtime/maci64

libmwmclmcrrt.9.11.dylib is the 13th lib loaded and the 6th MatLab one:

dyld: loaded: <11D060E5-13C3-34EE-96C9-A7EA2A7E34B3> /Applications/MATLAB/MATLAB_Runtime/v911/runtime/maci64/libmwmclmcrrt.9.11.dylib

  • Note that MacOS does not say "not found"
  • Thus RPATH does not seem to behave exactly as DYLD_LIBRARY_PATH did but why?
  • How can "ignoring DYLD_LIBRARY_PATH" break a library loading ?
  • How can I debug this ?

This is very disappointing since, otherwise, the application would easily sign and notarize. I have exhausted all the tracks I knew and found on the internet.

Alain

So, lemme see if I have this straight:

  • You are trying to ship the grasp app.

  • It has two executables. The main executable is at Contents/MacOS/grasp. There’s also a helper executable at Contents/MacOS/prelaunch.

  • Both executables need to load libraries from within /Applications/MATLAB/MATLAB_Runtime.

Is that right?

Some questions:

  • What’s applauncher?

  • In Contents/Info.plist, what value have you set for CFBundleExecutable?

  • Do you intend to ship /Applications/MATLAB/MATLAB_Runtime as part of your app? Or is the user expected to install that themselves?

  • If it’s the latter, do you want the user to be able to install it at arbitrary locations? Or are you always expecting it to be at that absolute path?

  • Are all of these things Mach-O executables? Or do you have any executable shell scripts involved?

Thus RPATH does not seem to behave exactly as DYLD_LIBRARY_PATH did but why?

That’s easy to explain: These are very different concepts. DYLD_LIBRARY_PATH affects all library loading and is completely dynamic. On modern versions of macOS it’s primarily intended to be used by:

  • Users running programs from a shell

  • Debugging tools

In contrast, the rpath mechanism is much more static and it only gets involved if you have rpath-relative references. I see no indication from your post that you rewrote your library references to be rpath relative. For a concrete example of that, see Embedding nonstandard code structures in a bundle.

Share and Enjoy

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

Thank you very much for your message.

"Embedding nonstandard code structures in a bundle."

Yes, I read this and tried to adapt it to my case.

You are trying to ship the grasp app.

At least I would like to distribute it as a notarized bundle since users find the current Terminal app too difficult to install and use. To make my previous post shorter I did not mention the full structure of the final bundle since this is not necessary to get and track the bug (see later).

	Grasp4Mac.app
		Contents
			MacOS
				grasp4Mac  <- calls Grasp4Mac.sh
			Ressource
				Grasp4Mac.sh <- check Matlab_runtime and calls run_grasp.sh
	----------------------- provided by a colleague and generated by scripts -------------
				Grasp
					run_grasp.sh <- set variables such as DYLD_LIBRARY_PATH and launches grasp.app
					grasp.app
						MacOS
							prelaunch  <-- the main
							grasp <-- which calls the MatLab_Runtime libraries
							applauncher <-- auxiliary
	--------------------------------------------------------------------------------------
	The MatLab_Runtime (5 Gb) is external to the bundle and located at /Applications/MATLAB/MATLAB_Runtime/v911
  • Signing both the embedded bundle (grasp.app) and the bundle (Grasp4Mac.app) is not a problem
  • Notarizing it is not a problem
  • Thus I concentrated my efforts on grasp.app launched via run_grasp.sh. It exhibits the same error if SIP is enabled (notarized Grasp4Mac bundle) or if I unset DYLD_LIBRARY_PATH and launches ./run_grasp.sh

In Contents/Info.plist, what value have you set for CFBundleExecutable?

prelaunch

DYLD_LIBRARY_PATH is intended for users running programs from a shell

run_grasp.sh sets DYLD_LIBRARY_PATH and launches grasp.app

I see no indication from your post that you rewrote your library references to be rpath relative.

The MatLAB libraries are already using @rpath. Here an example: The lib called by "grasp" i.e. the parent of a long list of dependencies. /bin/maci64/libmwlaunchermain.dylib:

	@rpath/libmwlaunchermain.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwi18n.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwfoundation_filesystem.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwctfdatainterfaces.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwctfpackage.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwmcli18nutil.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwmclmcrrt.9.11.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwopccore.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwopcmodel.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwopczippackage.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwfoundation_log.dylib (compatibility version 0.0.0, current version 0.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
	@rpath/libmwcpp11compat.dylib (compatibility version 0.0.0, current version 0.0.0)
	@rpath/libmwboost_log.dylib (compatibility version 0.0.0, current version 0.0.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0)
	/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 2022.20.117)
	/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1770.255.0)
	/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1770.255.0)
	/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)

/runtime/maci64/libmwmclmcrrt.9.11.dylib:

	@rpath/libmwmclmcrrt.9.11.dylib (compatibility version 0.0.0, current version 0.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0)

Note that is calls /bin/mac164/libmwmclmcrrt.9.11.dylib which was already loaded by /bin/maci64/libmwlaunchermain.dylib Both /bin/mac164 and /bin/maci64 are amongst the RPATHs May be I should write a script to explore the >3000 MatLab libs and search for something strange. Okay but what shall I search, that's the question!

Could it be that one of the MathLab's libraries checks that DYLD_LIBRARY_PATH is set and that SIP doe not "ignore it" but erase or hide it? In that case what could be the workaround ?

All the best Alain

OK, well, that’s a tangled web (-:

I’m not sure I got answers to all my questions:

Do you intend to ship /Applications/MATLAB/MATLAB_Runtime as part of your app?

It sounds like the answer to that is “No.”

Or is the user expected to install that themselves?

Meaning that this answer is “Yes.”

If it’s the latter, do you want the user to be able to install it at arbitrary locations?

What’s the story here?


This matters because it informs how you set up your library paths. If MATLAB is always installed at a fixed location, you can reference it statically and thus the environment variable stuff is irrelevant. OTOH, if the user can choose which MATLAB to use, your only option is to use an environment variable.

Share and Enjoy

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

Sorry if I was not clear enough but your guesses are fully correct:

The Matlab Runtime will be installed by users at the imposed location defined by the MathWorks installer: /Applications/MATLAB/MATLAB_Runtime There are two reasons for not to bundle the runtime and not to try to publish to the App Store :

  • the developper of Grasp warned me that, in hot experimental periods, he may produce several upgrades a day to satisfy the immediate needs of scientists. Hence publishing to the store would be too slow by far.
  • the runtime size is 5 GB and downloading it several times a day is not advisable.

If MATLAB is always installed at a fixed location, you can reference it statically and thus the environment variable stuff is irrelevant.

Yes but, as stated in my last post I found that the binary of some of the libs contains the string DYLD_LIBRARY_PATH probably because they read auxiliary data files. Thus my guess is that DYLD_LIBRARY_PATH being erased or hidden by SIP, MatLab can't find those files and prompts: Error: Could not find version 9.11 of the MATLAB Runtime.

In other words I got the feeling that the RPATH mechanism is Okay for the libs but does not apply to auxiliary data files, while the DYLD_LIBRARY_PATH mechanism is Okay for both. Do you have in mind something else that the RPATHs ?

The only workaround I can imagine is using a BinHex editor to change DYLD_LIBRARY_PATH to something like MY___LIBRARY_PATH and set that new environment variable that SIP will not alter. Unfortunately I don't think MathWorks will give me the permission to do so. Furthermore this would mean distributing and alternative Matlab Runtime that would not be fully functional (I checked only the .dydlib but there is also a lot of java, python, etc., files.

Anyway, your questions helped me understand the problem better. Thank you so much. May be I will be forced to limit myself to the unsigned version which relies on the DYLD_LIBRARY_PATH mechanism.

I found that the binary of some of the libs contains the string DYLD_LIBRARY_PATH probably because they read auxiliary data files.

Oh, that’s just weird.

Thus my guess is that DYLD_LIBRARY_PATH being erased or hidden by SIP

It’s not SIP specifically, but the hardened runtime [1]. You can opt out of this extra security using the com.apple.security.cs.allow-dyld-environment-variables exception entitlement.

You’ll also need to opt out of library validation by setting the com.apple.security.cs.disable-library-validation entitlement. That’s because you’re not shipping the MATLAB runtime but rather using the user-installed one, which means that it’ll be signed by a different team.

That should get your signed and notarised code running. The next challenge is passing Gatekeeper. The potential issue is the one discussed in Resolving Gatekeeper Problems Caused by Dangling Load Command Paths. You may be able to avoid those by not importing the MATLAB libraries directly but instead relying on… ironically… DYLD_LIBRARY_PATH. However, it’s hard to be sure without looking at your stuff in a lot more depth.

Share and Enjoy

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

[1] But SIP affects things because, when you disable SIP, it disables this hardened runtime policy.

Thank you for your suggestions

I will experiment further with entitlements. I already use com.apple.security.cs.disable-library-validation but not the other one because I read in some of your posts that you discourage its use... and because I did not fully understand what it does.

Just to let you know, we use the MatLab_Runtine v911 and not a more recent one (perhaps easier to use) because, for some strange reasons, it is the only one which is free of use.

However, it’s hard to be sure without looking at your stuff in a lot more depth.

If I cannot solve the problem by myself, there would be no problem on my side in sharing the whole project with you, but I do not wish to take up too much of your time. Formalizing everything for you and your informed answers , this is already a big help.

Thank you very much again

Alain

I do not wish to take up too much of your time.

Fortunately there’s a way you can ‘pay’ for that, by opening a DTS tech support incident. I’ve been clinging to the hope that we could get this sorted here on DevForums but opening a TSI was going to be my next suggestion (-:

because I read in some of your posts that you discourage its use

Indeed, but you’re requirements are weird and weird situations are exactly where you end up needing such things!

I will experiment further with entitlements.

Cool. Let me know how that goes.

Share and Enjoy

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

Hi,

As suggested, I added both com.apple.security.cs.allow-dyld-environment-variables  and com.apple.security.cs.disable-library-validation entitlements. Both are set for the main bundle but checked that they are also present for the embedded bundle while auxiliary binaries have the com.apple.security.inherit entitlement. Unfortunately enough the problem is still present. I will be in travel until the end of the month but when I will be back I am ready for a DTS tech support incident. However, since most of the contents of the App are generated by MatLab, I really don't know how to produce a small test App.

All the best Alain

RPATH switch from DYLD_LIBRARY_PATH: strange results
 
 
Q