How can I tell why my program requires a specific shared library? - xcode

I'm working on an OS X application using a third-party framework. This framework is distributed both as shared objects and static objects. For my purposes, I want to use the static objects because I can't rely on the presence of the library on other systems.
However, when I build the application with Xcode, something decides it needs the shared objects, and when I run it, dyld tells me off before I even get to my program:
dyld: Library not loaded: /usr/local/lib/libshared.dylib
  Referenced from: /Users/me/Library/Developer/Xcode/snip/Application.app/Contents/MacOS/Application
  Reason: image not found
I ran otool -L on the executable, and sure enough, it tried to link against the shared objects (which aren't even installed on my system). However, when I ran it on the thirty-some .a files that I link against, none of them indicated any dependency on them.
Apple's ld -v is just a tad verbose: it displays the library search paths but doesn't produce any other kind of useful output.
How can I find what tried to link against the shared objects?

otool -L does list the libraries against which any object links.
This specific instance was caused by an Xcode bug (known as rdar://2725744 to the ones blessed with Apple bug database access, and not fixed as of Xcode 6.1.1) where if you try to link against a .a static library with Xcode but there's a .dylib (or .so) dynamic library with the same name in the same directory, the linker will pick the dynamic one.
When you instruct Xcode to link against a static library (say /some/path/to/my/libFooBar.a), it adds -L/some/path/to/my -lFooBar to the linker invocation. However, with it, ld first searches for a dynamic library called libFooBar.dylib, and it will fall back to the static library only if it can't find the dynamic one.

If you added the framework is listed in the "Build Phases" of "Link Binary with Libraries" and it has the setting of "Required" that could explain why the launch fails.
Try changing the setting to "Optional". Then if nothing uses the framework, it should launch fine. (If something tries to use the framework, but fails to check for it first, it might crash.)

Related

Xcode: Link kernel framework to kernel extension?

I am attempting to write a kernel extension (kext) that uses some parts of the Kernel framework.
I added Kernel.framework to the list of Frameworks and Libraries in the target settings.
However, when I try to build my kext and link against it, Xcode refuses to do this and claims to not be able to find the framework, even though it had shown up in the list of available frameworks.
Am I doing this wrong? Is it not possible to use this framework even for kexts?
Edit: I am aware of the user space alternatives to kexts but these don't fulfill my needs.
The Kernel.framework is a header-only "framework". Kext linking is fundamentally different from user space executable linking, as there's no dyld. You don't need to link against any libraries at build time, but you must specify the KPIs to link against in the Info.plist file's OSBundleLibraries dictionary.
You can generate a draft of this dictionary using the following command:
kextlibs -xml -c path/to/your.kext
If using any unsupported KPIs, you'll also want to specify -unsupported. If it's complaining about symbols not being found, adding the -undef-symbols option will help with debugging.

macOS: Load one or other system framework at run time based on availability

I'm working on a macOS tool which uses Apple's Safari framework. When running in macOS 10.13, the tool links to and loads it from
/System/Library/PrivateFrameworks/Safari.framework
and all works fine. But when running in macOS 10.12.6, some behaviors are missing. Based on some probing with DTrace, I think that this is because my tool needs to load instead the latest Staged framework, which is here:
/System/Library/StagedFrameworks/Safari/Safari.framework
This is apparently what Safari does, because if I attach to Safari with lldb and run image list, in 10.13 the list includes only the former path, and in 10.12.6 only the latter.
I tried the following:
NSBundle* stagedBundle = [NSBundle bundleWithPath:#"/System/Library/StagedFrameworks/Safari/Safari.framework"];
That returns nil in 10.13 because there is, at this time, no such directory. However, in 10.12.6, I get a stagedBundle, and then:
NSBundle* privateBundle = [NSBundle bundleForClass:[BookmarksController class]];
[privateBundle unload];
[stagedBundle load];
The unloading and loading apparently works, because if I log -description of those two bundles, before running that code the Private bundle is (loaded) and the Staged bundle is (not yet loaded), but after running that code those states are swapped, as desired.
But it is not effective. (1) If I again invoke -bundleForClass:, passing a class known to be in both frameworks, it gives me the Private bundle. (2) If I invoke -respondsToSelector:, passing a selector which is known to exist only in the Staged framework, I get NO.
I tried calling _CFBundleFlushBundleCaches(), as suggested here, but that did not help.
I've also tried changing my target's FRAMEWORK_SEARCH_PATHS, and installing the Staged framework on my Mac and linking to it, but since this post is already too long I'll just say that this resulted in more heat than light.
How can one selectively load a framework in this situation?
UPDATE
I've tried another approach. After re-reading Apple's Framework Programming Guide, even though it seems really dated, I decided that this framework needs to be weakly linked. Did this:
In the code, removed those NSBundle -load and -unload calls
In my tool's target,
In Build Phases > Link Binary with Libraries, removed path to the Safari Private framework, because this was a strong link.
In Build Settings > Other Linker Flags added -weak_framework Safari
In Build Settings > Framework Search Paths, listed paths to both frameworks' parent directories, with the Staged path before the Private path, because I want this one to load in macOS 10.12.6, where both exist.
It makes sense to me, builds and runs in both 10.13 and 10.12.6, but it is apparently still loading the undesired Private framework in 10.12.6. NSLog reports that as the bundle's path, and a class does not respond to a selector known to be in Staged framework only.
Any other ideas?
First, a disclaimer: I'd strongly suggest you don't rely on loading private frameworks in any application that you ship to users. It's fragile and unsupported.
That said, if you really want to do this, my suggestion would be to use the same technique that Safari itself uses to select between the two copies of the framework, which is dyld's DYLD_VERSIONED_FRAMEWORK_PATH environment variable.
To quote the dyld man page:
This is a colon separated list of directories that contain potential override frameworks. The dynamic linker searches these directories for frameworks. For each framework found dyld looks at its LC_ID_DYLIB and gets the current_version and install name. Dyld then looks for the framework at the install name path. Whichever has the larger current_version value will be used in the process whenever a framework with that install name is required. This is similar to DYLD_FRAMEWORK_PATH except instead of always overriding, it only overrides is the supplied framework is newer. Note: dyld does not check the framework's Info.plist to find its version. Dyld only checks the -current_version number supplied when the framework was created.
In short, this results in dyld performing a version check between the framework being loaded and the one in the versioned framework path, with the higher version being loaded. If the versioned framework path doesn't exist or the framework in question doesn't exist within it, the original framework path will be used.
Safari makes use of a second dyld feature to simplify its use of DYLD_VERSIONED_FRAMEWORK_PATH, the LC_DYLD_ENVIRONMENT load command. This load command allows DYLD_* environment variables to be specified at link time that will be applied by dyld at runtime prior to it attempting to load any dependent libraries. Without this trick you'd need to set DYLD_VERSIONED_FRAMEWORK_PATH as an environment variable prior to your application being launched, which typically requires a cumbersome re-exec to achieve.
Putting these two building blocks together, you end up adding a configuration setting like:
OTHER_LDFLAGS = -Wl,-dyld_env -Wl,DYLD_VERSIONED_FRAMEWORK_PATH=/System/Library/StagedFrameworks/Safari;
You can then either link statically against /S/L/PrivateFrameworks/Safari.framework, or attempt to load it dynamically at runtime. Either should result in the appropriate framework being loaded at runtime.
To address some of the misunderstandings your question reveals:
The unloading and loading apparently works, because if I log -description of those two bundles, before running that code the Private bundle is (loaded) and the Staged bundle is (not yet loaded), but after running that code those states are swapped, as desired.
Unloading shared libraries containing Objective-C code isn't supported. I suspect the only thing it does is result in a "loaded" flag being toggled on the NSBundle instance, since at dyld's level it is ignored.
In Build Settings > Framework Search Paths, listed paths to both frameworks' parent directories, with the Staged path before the Private path, because I want this one to load in macOS 10.12.6, where both exist.
Framework search paths are a concept that's only used at compile-time. At runtime, the library's install name is what tells dyld where to find the binary to load.

Proper way to link a static library using GCC

Why is it that some static libraries (lib*.a) can be linked in the same way that shared libraries (lib*.so) are linked (ld -l switch), but some can not?
I had always been taught that all libraries, static or not, can be linked with -l..., however I've run into one library so far (GLFW), which does nothing but spew "undefined reference" link errors if I attempt to link it this way.
According to the response on this question, the "proper" way to link static libraries is to include them directly, along with my own object files, rather than using -l. And, in the case of the GLFW library, this certainly solves the issue. But every other static library I'm using works just fine when linked with -l.
So:
What could cause this one library to not work when linked rather than included directly? If I knew the cause, maybe I could edit and recompile the library to fix the issue.
Is it true that you're not supposed to link static libraries the same way you link shared libraries? (And if not, why not?)
Is the linker still able to eliminate unused library functions from the output executable when the library is directly included in this way?
Thanks for the replies! Turns out the problem was due to link order. Apparently, if you use a library which in turn has other library dependencies, those other dependencies must be listed after the library, not before as I had been doing. Learned something new!
Have you cared to indicate to GCC the path of your library (using -L) ? By using -l solely, GCC will only be able to link libraries available in standard directories.
-L[path] -l[lib]
The correct way to link a static library is using -l, but that only works if the library can be found on the search path. If it's not then you can add the directory to the list using -L or name the file by name, as you say.
The same is true for shared libraries, actually, although they're more likely to be found, perhaps.
The reason is historical. The "ar" tool was original the file archive tool on PDP11 unix, though it was later replaced entirely by "tar" for that purpose. It stores files (object files, in this case) in a package. And there's a separate extension containing the symbol table for the linker to use. It's possible if you are manually managing files in the archive that the symbol table can get out of date.
The short answer is that you can use the "ranlib" tool on any archive to recreate the symbol table. Try that. More broadly, try to figure out where the corrupt libraries are coming from and fix that.

Boost library static linking on Xcode 4

I am using the Boost library on OS X using Xcode. Boost was installed on my system using macports. I have successfully built my app by adding the 3 boost libraries I need (for example, libboost_thread-mt.a) to that Targets 'Link Binary With Libraries' list. However I need to link these libraries statically so that the app will run on other computers without the boost library needing to be installed.
How do I do this exactly? Through my numerous google searches I'm finding I might need to add '-static' - where do I add this in Xcode?
If you've linked with a .a library, then you have already linked statically. You never need to ship .a libraries. They're just bundles of objects.
EDIT: Your error strongly suggests that you're linking the dylib rather than the .a. If you have libfoo.dylib and libfoo.a in your library path, even if you say "link libfoo.a" in Xcode, and even if libfoo.a is earlier in the search path, it will still link libfoo.dylib. This is because Xcode's linking is totally broken and passes -lfoo to the linker (you should never use -l for something you built and have the exact path to). I always recommend linking libraries you built in LDFLAGS in an xcconfig file rather than using the build pane. You pass the exact path you want rather than using -l. See Abandoning the Build Panel for more of my thoughts on xcconfig. It's out of date now, since it was written for Xcode3, but the basics still apply.
Using the build pane, you can also pass the entire path to the library in "Other Linker Flags." But this still has all the problems of the build pane.
The quicker (but less robust) solution is sometimes to add -Wl,-search_paths_first to the "Other Linker Flags." This changes the behavior so that each library path is searched for both .dylib and .a before going on (the default behavior is to search everywhere for .dylib and only then search for .a). So if your .a is in a different directory from your .dylib, and that directory is earlier in the search path, this will work.
This question finally got me to open a radar on this, which I should have done years ago. I recommend that others open duplicates.

How do I force Xcode to link to a custom version of a system framework?

I have a project that uses OpenAL. The project is built against the 10.5 SDK, and the version of the OpenAL.framework in 10.5 causes some problems. I want to link to a custom-built version of the OpenAL.framework that resides in my source tree.
However, Xcode resolutely refuses to do this. No matter what I try, it insists on linking to the framework located at /Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/OpenAL.framework/OpenAL. Here are a couple of things I've tried without success:
Set the path to the framework directory in a variety of ways (relative, absolute) using -F.
Pass the linker the -Z flag to eliminate default link paths, then explicitly pass /System/Library further on in the link process, to ensure that it sees the system paths after my custom library path.
Build my library using a prelinking pass, and explicitly pass the library inside the framework to THAT.
According to man gcc, passing the -F parameter should be sufficient to ensure that a link path is searched before the default paths. Either this isn't happening correctly or I'm misunderstanding the problem, and it seems too simple and obvious to be a linker problem :-)

Resources