XCode 4.4.1 ignores LD_RUNPATH_SEARCH_PATHS - objective-c

I am dealing with a weird behavior of XCode:
dyld: Library not loaded: /Library/Frameworks/SBJson.framework/Versions/A/SBJson
Basically it ignores my Runpath Search Path (LD_RUNPATH_SEARCH_PATHS) configuration that is actually #loader_path/../Frameworks.
I am not able to load any embedded framework in this moment :/
otool says
otool -L /Users/kilian/Library/Developer/Xcode/DerivedData/r-ghohkslxtxgpnuepmblogfjtuefx/Build/Products/Debug/r.app/Contents/MacOS/r
/Users/kilian/Library/Developer/Xcode/DerivedData/r-ghohkslxtxgpnuepmblogfjtuefx/Build/Products/Debug/r.app/Contents/MacOS/r:
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 19.0.0)
/Library/Frameworks/SBJson.framework/Versions/A/SBJson (compatibility version 1.0.0, current version 37.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 945.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1186.0.0)
P.S. If you're wondering if I added the copy to framework build phase, the answer is yes.

The short version
The problem is that when you built SBJSON.framework, the install name wasn't configured to use #rpath. As a result, when your app goes to load the dynamic library, it's looking in /Library/Frameworks instead of where your app's runpath tells it to look.
To fix it, you need to change a build setting on the SBJSON.framework target. Change the the Dynamic Library Install Name setting to #rpath/${EXECUTABLE_PATH}. Then just build again, linking against the newly-built framework, and you're good to go.
The long version
Frameworks are, at the core, just dynamic libraries. This means that the code contained in the library is not embedded into your app, but rather pulled in from the SBJSON.framework bundle at runtime. In order to do this, your app needs to know where to look for the dynamic library.
The way this works is that when you link against a framework, the linker doesn't actually embed the whole library into your app. Instead, it just adds a small section that tells the app where to find the library when it runs. Of course, that means that the compiler has to know where the library will be at runtime. It finds that information by looking at the "Install Name" of the dynamic library.
A dynamic library's "Install Name" is essentially just the path where the library is expected to be. Historically, most dynamic libraries and frameworks have been shared system-wide. Things like Foundation.framework and CoreData.framework, for example, live in /System/Library/Frameworks, and all apps can reasonably expect to find them there. Thus, when CoreData.framework is built, its install name is set to /System/Library/Frameworks/.... When Xcode links your app against Core Data, it looks at the framework's install name and tells it to load in the framework at that path when the app is launched.
That's all fine and well, but it doesn't help you when you need to embed a framework inside your app. You don't know where the app will be located on the system at runtime. The user may run it from /Applications, but they might also run it from ~/Downloads. There's no single path you could provide as the install name that would always correctly point to the framework at runtime.
To deal with this, you can set the framework's install name to #loader_path/../Frameworks. When the dynamic loader sees #loader_path, it replaces it with the path of the currently-loading app. Using this install name would allow the framework to be installed inside the Frameworks folder of any app.
However, things still aren't perfect. The framework is still dictating where it should be placed. If another app wanted to place the framework inside a Libraries folder instead, for example, it's out of luck. The framework is in charge of where it can be placed, instead of the app. This is an inversion of the dependency tree, and is not ideal. The app should be able to load the framework from wherever it wants to stash it, regardless of what other frameworks do.
Thus, in OS X 10.5, #rpath was introduced. If the install name of a dylib or framework begins with #rpath, then the loader will turn around and ask the app what it's "Runpath search paths" are, and substitute those in. This allows the app to specify where its frameworks will live. By using #rpath, the framework delegates the decision back to the app. The app can use #loader_path if it wants, or can specify an absolute path if it wants. It could even specify a shared folder that a whole suite of apps will use.
Your problem
So, on to your problem. You're correctly setting the app's runpath search path to #loader_path/../Frameworks. However, the framework's install name isn't using #rpath; in fact, it's still using a hard-coded path to /Library/Frameworks/.... Since the framework's install name doesn't use #rpath, the app's runpaths aren't even consulted. It's simply trying to link in SBJSON from the /Library folder. Since it's not there, your app crashes before it even launches.
You need to change the install name of the SBJSON framework to use #rpath. Set the Dynamic Library Install Name setting to #rpath/${EXECUTABLE_PATH}. (${EXECUTABLE_PATH} is the relative path to the internal dynamic library, from the folder containing the framework.)
Once you've built the framework with the new install name, you should be able to link against the new framework, make sure it's copied into the app bundle's Frameworks/ folder, and you're good to go!
P.S. If this isn't all clear, Mike Ash did a pretty nice review of #rpath and friends. You can find it here.

Related

Embedding dylib in executable

I've created a CLI version of my OS X app (which is an App Bundle). In my bundle version there's a pre-compiled .dylib that is normally placed within the directory where the executable resides.
This won't work for the command line version - is there a way to embed it within my executable?
If you want to distribute only binary version of your application which related of dylib's you may do this in next ways:
1. make pkg installer which will put dylib somewhere in the system
2. most of 3rd parties libraries supports building of static version (*.a) itself as well as dynamic version. So you can build static version of your libraries and link statically with them.

Linking a framework depending on 'Build' or 'Debug' mode in Xcode5?

I am building a Mac app using Xcode5 on 10.9.2. My project uses a third party framework -- for the sake of simplicity, let's call it XYZ.framework.
There are two versions of XYZ.framework provided: a debug version and a release version. The debug version of the framework is considerably larger, but it carries important functionality for debugging like printing warnings to the console, asserts, etc. The release version of the framework is smaller, faster, and is intended for production. The debug version resides at debug/XYZ.framework and the release version resides at release/XYZ.framework.
My question is: How do I include the correct version of the framework depending on my build configuration? I.E., Xcode should include debug/XYZ.framework when my build configuration is Debug, or include release/XYZ.framework when my build configuration is Release.
One possibility is,create separate folder to keep the debug version of framework and release version of framework. Next step specify the framework search path according to build configuration. Below is the steps to customise framework search path:
Go to build settings.
Search for "framework search paths" in build settings.
Edit framework search path according to your configuration.
Additionally this thread may also help you.
Make a separate build target for your debug app:
Target > Debug.app
Under Build Phases:
Link Binary with Libraries:
Click + Then Add Your Debugging Framework
Remove the Release one by clicking -

What will happen if a framework get deleted from an app?

I was asked a question, what will happen when a framework is accidentally deleted from your device may be iphone or mac?
Whether your application crash or it will work without any error?
Please provide me answer with explainations.
You may get linking error. If framework folder is inside the application means its runtime linking.
OS X embeds an "install name" inside each dynamic library. This
install name is the path to where the library can be found when dyld
needs to load it. When you build an application that links against a
dynamic library, this install name is copied into the application
binary. When the application runs, the copied install name is then
used to locate the library or framework.
$ otool -D /Applications/Google\ Drive.app/Contents/Frameworks/Python.framework/Versions/2.6/Python
/Applications/Google Drive.app/Contents/Frameworks/Python.framework/Versions/2.6/Python:
#executable_path/../Frameworks/Python.framework/Versions/2.6/Python
so if you will delete Frameworks folder you will get Dyld Error.
Dyld Error Message:
Library not loaded: #loader_path/../Frameworks/Sparkle.framework/Versions/A/Sparkle
Referenced from: /Applications/Transmission.app/Contents/MacOS/Transmission
Reason: image not found
If a framework is deleted, only software linking to that framework would be affected.
If it is a system framework, expect your system to start failing.
If it is a 3rd party framework, it should be limited in scope of impact.
If it is in your app bundle, and your app is code-signed, the removal of the bundled framework should prevent your app from launching.
It depends on if the application needs access to the framework you have removed.
If you application requires the framework then it will not compile.
If you dont need it, it will just compile like normal.

How do I use a new framework I have built?

I have had some issues with the AWS IOS SDK framekit, since it was not built to work with OSX apps. I found a modified version of the SDK that Brad Larson created.
The directory structure looks something like:
AWSiOSSDK.framework/
src/
--Amazon.Runtime/
--Amazon.S3/
..
--AWSCocoa/
----AWSCocoa_Prefix.pch
----AWSCocoa.xcodeproj
----etc
--include/
So if I go into the xcode project, and build AWSCocoa it compiles. But when I look at the timestamp on the AWSiOSSDK.framework, it hasn't changed. So I don't know what compiling this AWSCocoa gets me, or where I can find the files it creates. So assuming that building AWSCocoa.xcodeproj is supposed to build a new version of the framework compatible with OSX development, where do I find and link what I've built?
The AWSiOSSDK.framework bundle is a precompiled framework, probably left over from my earlier experiments in making a Mac version of this (since you can't use frameworks like this with iOS, only static libraries). Ignore that.
If you are using Xcode 4, your built framework will be created somewhere in your ~/Library/Developer/Xcode/DerivedData/ directory. To find where it lies, go to your project navigator in Xcode 4, expand the Products group, right click on AWSCocoa.framework, and select Show in Finder. This is no different from any other third-party framework you would compile.
Nominally, you'll want to add this framework as a target dependency in your application so that it is built alongside that. You'll also have to make sure that the framework is copied into the appropriate location within your application bundle so that it can be used by your application at runtime.

Library search path for libstdc++

I have compiled shared libraries dynamically linked against libstdc++.so using GLIBCXX_3.4.11. I want to send my code to someone whose stdc++ library is only of version 3.4.10. Rather than ask him to update his library version (this is a software customer, so I can't assume they'll be willing or able to change system files) I would like to ship the appropriate version of libstdc++.so, placed in a lib folder with the directory location of my code. How do I get my own code to use the appropriate (later) version? I find that /etc/ld.so.conf includes the directory /lib64, where an offending older version of libstdc++.so resides. Setting LD_LIBRARY_PATH does not override this. This seems to deviate from the advertised behavior. Any idea why this is happening? How do I complete my rather simple task?
Thanks.
I understand that this question is old, but I found it while trying to sort out my own linking trouble which was similar. You will have to build your program against a version of libstdc++ which is compatible with your colleague's version of the library. The easiest solution, of course, is to link against his version of the library so he doesn't need to make special tweaks on his side to link your library.
To do this, you will want to install a version of GCC which can build binary compatible libraries so you can actually link against his version of libstdc++. GLIBCXX_3.4.11 is from gcc-4.4 and later, so you will need gcc-4.3. Build your program using this and you should be in good shape.
You can consult the following page for a list of library ABI compatabilities:
http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html
You could also build an rpm/deb which requires the version of libstdc++ you already have and if it's not available, refuse to install. This gives you a bit of an interface which gives him a promise that if his system is setup with the correct dependencies, he can use your library. In that sense, it's like a loose SLA for your library in what you do and don't support.
Hope that helps!