macOS Archive builds and environment variables

Hi All -

I'm a bit confused about setting environment variables in Mac Terminal when testing archive builds.

In Terminal, if I do something like export DYLD_PRINT_LIBRARIES=1, it works great for Debug builds. When I open my Debug binary, I can see all the dylibs and Frameworks getting loaded. However, my Archive build doesn't show me anything when I try opening it from Terminal.

I did some Googling and I guess SIP may be doing something to prevent environment vars from affecting Archive builds. This made sense to me until I tried creating a simple Test app. If I Archive my Test app and open the binary in Terminal, I see all the dylibs and frameworks. So why would an Archive build for a simple test project work with DYLD_PRINT_LIBRARIES, but my production app's Archive build won't?

Related question: I've run into a situation where I may have to set LSEnvironment in my app's Info.plist to work around a problem in 10.15 Catalina and 11 Big Sur. Setting LSEnvironment works great in my Debug and Release builds. But just like using setting DYLD_PRINT_LIBRARIES in Terminal, my Archive build ignores the LSEnvironment settings.

So... is there a way to create an Archive build that doesn't ignore Environment Variables in Terminal, or LSEnviroment in Info.plist? Hoping that there's a simple Build Setting that I can enable or disable in my Target to do this. Thanks.

Answered by DTS Engineer in 898685022

The issue here is very likely to be the hardened runtime. Consider this sequence:

% cp /usr/bin/true MyTrue
% codesign -s - MyTrue 
MyTrue: is already signed
% codesign -s - -f MyTrue
MyTrue: replacing existing signature
% DYLD_PRINT_LIBRARIES=1 ./MyTrue 
dyld[32846]: <E7F62A62-637C-314F-ADEF-157B9687C78F> /Users/quinn/Test/MyTrue
… lots and lots of output …
% codesign -s - -f -o runtime MyTrue
MyTrue: replacing existing signature
% DYLD_PRINT_LIBRARIES=1 ./MyTrue   
% 

I copied the built-in true and re-signed it without the hardened runtime. In that case DYLD_PRINT_LIBRARIES does what you expect. OTOH, if I re-sign it again with the hardened runtime enabled, DYLD_PRINT_LIBRARIES stops work.

As to why your new test project behaves differently, I suspect that you might have the Hardened Runtime capability enabled in some custom way in your real project. Regardless, you can test my theory by dumping the program’s code signature:

% codesign -d -v MyTrue 
…
CodeDirectory v=20500 size=272 flags=0x10002(adhoc,runtime) …
…

The presence of the hardened runtime is indicate by that runtime flag.


My general advice is that you enable the hardened runtime all the time. If you only enable it for distribution, that can produce a nasty surprise at the end of your development efforts when you go to distribute your app.

If you need to do something that the hardened runtime prevents — like DYLD_PRINT_LIBRARIES — you can temporarily disable it just for that test. Alternatively, you can opt out of many of its enhanced security features via entitlements. But, again, do that temporarily just for your test.

Share and Enjoy

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

Update: After some trial-n-error with my builds, this looks like it has something to do Deployment Postprocessing (which I think is enabled for Archive builds).

But I still don't understand why my Test app Archive build (which should have Deployment Postprocessing on) seems to be okay with adding Environment Variables in Terminal.

Accepted Answer

The issue here is very likely to be the hardened runtime. Consider this sequence:

% cp /usr/bin/true MyTrue
% codesign -s - MyTrue 
MyTrue: is already signed
% codesign -s - -f MyTrue
MyTrue: replacing existing signature
% DYLD_PRINT_LIBRARIES=1 ./MyTrue 
dyld[32846]: <E7F62A62-637C-314F-ADEF-157B9687C78F> /Users/quinn/Test/MyTrue
… lots and lots of output …
% codesign -s - -f -o runtime MyTrue
MyTrue: replacing existing signature
% DYLD_PRINT_LIBRARIES=1 ./MyTrue   
% 

I copied the built-in true and re-signed it without the hardened runtime. In that case DYLD_PRINT_LIBRARIES does what you expect. OTOH, if I re-sign it again with the hardened runtime enabled, DYLD_PRINT_LIBRARIES stops work.

As to why your new test project behaves differently, I suspect that you might have the Hardened Runtime capability enabled in some custom way in your real project. Regardless, you can test my theory by dumping the program’s code signature:

% codesign -d -v MyTrue 
…
CodeDirectory v=20500 size=272 flags=0x10002(adhoc,runtime) …
…

The presence of the hardened runtime is indicate by that runtime flag.


My general advice is that you enable the hardened runtime all the time. If you only enable it for distribution, that can produce a nasty surprise at the end of your development efforts when you go to distribute your app.

If you need to do something that the hardened runtime prevents — like DYLD_PRINT_LIBRARIES — you can temporarily disable it just for that test. Alternatively, you can opt out of many of its enhanced security features via entitlements. But, again, do that temporarily just for your test.

Share and Enjoy

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

Yup! You're right as always Quinn... it's the Hardened Runtime.

Although I do seem to have HR enabled all the time in my Target. Maybe something about building for Debug or Profiling disables it...? Regardless, disabling it not only got DYLD_PRINT_LIBRARIES working, it also got the LSEnvironment flags in my Info.plist to work as well.

Thanks:)

macOS Archive builds and environment variables
 
 
Q