Avoid symbol reference in static library - ios

I develop a static library that is distributed to other developers. I want to use CocoaLumberjack (DDLog) class if it is available in the final binary. In the static library I define the class interface and check [DDLog class] to see if it exists. But in the host App, if CocoaLumberjack isn't present, the linker complains because DDLog does not exist.
I know I can defer symbol checking to runtime in the App configuration, but is there a way to prevent the static library compilation from referencing the DDLog class in the compiled objects?

I don't think it is possible to have undefined symbols at final App link time, even if they are weak. From the docs for Mac OS X ld:
When creating a output file with the static link editor when -twolevel_namespace is in effect (now the default) all undefined references must be satisfied at static link time. The flags to allow undefined references, -Usymbol_name, -undefined warning and -undefined sup_press can't be used. When the environment variable MACOSX_DEPLOYMENT_TARGET is set to 10.3 then -undefined dynamic_lookup can also be used.
So if there are any undefined references at link time (including any API you use that is not in the current Base SDK version) it will produce an error. The only way around this is to use the -undefined dynamic_lookup linker option. Unfortunately this defers all symbol lookup to runtime, you can't specify just the symbols you want to skip when using two level namespace.
For me, I don't want to burden the end developer with that. So I switched to using objc_msgSend and NSClassFromString instead, avoiding all use of the symbol. It's a shame it has to be done in that way and this seems like something Apple could improve.

Related

Marking a symbol as undefined in iOS framework

I'm building an iOS framework using Swift and Objective-C (have to use both of them due to other constraints).
In my objective-c code, I need to use a symbol, call it MyUndefinedSymbol, that will be available during runtime, but not during compile and link time.
I defined the symbol as extern to silence compiler errors:
extern void MyUndefinedSymbol();
However, linker complains that MyUndefinedSymbol is not defined.
Question is: how do I make the linker ignore that undefined symbol?
Searching for this brought me many solutions, none of them workd.
The things I tried:
Using the -undefined linker flag. It is deprecated and won't allow enabling bitcode (which I need).
Using clang's -U flag.
Adding a weak_import attribute using __attribute__((weak_import))
Using --unresolved-symbols=ignore-all.
For future readers:
Seems like the right way to do it is exactly as #Martin R suggested - using dlsym() to load the symbol at runtime.
Note that dlsym will not work on symbols which are marked as "Internal".
You can find out if a symbol is internal by running nm -m on your library/binary.
In addition, solutions 1 and 2 from the questions itself should work if you disable bitcode.

iOS App is including ALL symbols from static library

I have a problem that I haven't been able to resolve in regards to when I link my iOS App against one or more static libraries. Here is the issue:
I am creating several static libraries (MACH-O type: Relocatable Object File) each of which contain a large number of symbols. Each of these static libraries are FAT libraries, containing a slice for each iOS/Simulator architecture. From my understanding, when I compile my iOS Application only the symbols that are used by my App should be compiled into and included in my App, however, ALL of the symbols are being included.
I've done some testing using otool, nm, and other tools and can see that when I link against any of these libraries, even if I don't call any of the code in the libraries, ALL of the symbols are being compiled into the App. It takes my App from 42kB all the way to 3+MB.
Any ideas on why this is happening?
Objective-C is a dynamic runtime; it is permissible e.g. to perform:
NSString *classToUse = ["MPViewController" stringByAppendingString:class];
return [[NSClassFromString(classToUse) alloc] init];
... and furthermore this is more or less exactly what happens when you load a NIB — string class and property names are loaded from disk, the backing classes are then found via the runtime and properties applied by key-value coding. So dynamic lookup is not an edge case.
A linker can therefore not make any assumptions about which symbols are used from an Objective-C static library, unlike e.g. a C linker.
Yes,It can be done by stripping static libraries, but if post-processing is enabled. Set Xcode build setting "Deployment Postprocessing" to yes. (DEPLOYMENT_POSTPROCESSING=YES). Also make sure that "Use separate strip" is set to Yes. You can check this
You can also achieve what you are expecting by using Dynamic Library, there is a very nice article here
Extra Tip
* While creating library set Debug Symbols to NO in your build settings. This can reduce the size of your static library by up to 30%.

iOS Static Library | Linking only USED symbols

I am using some third party iOS Static Libraries and am having some trouble keeping the binary size small. The libraries that I am using have an Objective-C interface that is backed by native C/C++ code. The issue is that ALL of the symbols from the library (checked with nm) are being included in my App when linked (even if I don't reference any of the code in the library). This is contrary to my understanding of static libraries where only the code that your application (or other linked libraries) reference is pulled into your application.
I've done a bunch of reading and have found that due to the dynamic nature of Objective-C, there are particular issues that can arise from linking object files or static libraries containing only Objective-C category methods. Because of this, you can pass the -ObjC flag to the linker to have the linker pull in ALL object files containing Objective-C classes or categories. This ensures that all classes and categories are defined at runtime, but bloats your App's binary with unused Objective-C classes/categories/method definitions.
Strangely enough, I am seeing the effects of adding the -ObjC linker flag while not using it anywhere in my build. ALL of the Objective-C symbols are being included, and as a consequence, ALL of the native C/C++ symbols that the Objective-C code references, whether or not my App references any of the code in the library. Has anyone else experienced this problem or found a solution to it?
OS X 10.11.4 and Xcode 7.3.
First, a disclaimer: I haven't personally tried the solution I describe here (all the projects I've worked on just use the -ObjC flag indiscriminately), so YMMV.
That said, this might be of use: https://github.com/CocoaPods/CocoaPods/issues/712
Basically, the idea is, rather than using the -Objc carpet bomb, you can do a slightly more targeted load on a per-library basis: -force_load $(TARGET_BUILD_DIR)/lib<yourLibName>.a.
The author of the referenced link specifically mentions CocoaPods as the culprit for a specific problem he encountered, but I would think (hope) that this solution would apply to the more general question you're asking.
As to the question of why you even have to bother, the only thing I can find that even comes close to an actual explanation can be found here: https://developer.apple.com/library/mac/qa/qa1490/_index.html. This post describes an "impedance mismatch" between Unix (BSD) static libraries and the more dynamic Objective-C based libraries (even static ones) containing e.g. categories. Current linkers can't make the required connections at compile/link time for methods that are essentially bound at runtime, so these linker flags are a workaround to that problem.

iOS Framework weak link: undefined symbols error

I'm building my own framework which proposed to be distributed to other developers for including to their projects. This framework links optionally certain frameworks (e.g. CoreLocation). The problem is that when I link my framework to real stand-alone project which doesn't contain CoreLocation in Build Phases, I'm getting linker errors like 'Undefined symbols for architecture' when tryin to build this host-project
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_CLLocationManager", referenced from:
objc-class-ref in MySDK(MyServerConnection.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Is it possible to avoid this because I don't want to force developers to include CoreLocation to their projecgts? Actually, I know it's possible, but what should I do to achieve this?
From the description of your issue, I assume you already know how to make Xcode weakly link against the framework by setting it as 'Optional'.
You have two problems to solve: Class availability and symbol availability. Apple covers this in the Framework Programming Guide: Frameworks and Weak Linking and the SDK Compatibility Guide: Using SDK Based Development
Class availability
This is pretty straightforward: use NSClassFromString() to see if the class is available in the current environment.
if (NSClassFromString("CLLocationManager") != NULL){
If the class is available it can be instantiated and sent messages, otherwise it cannot.
Symbol availability
What you are specifically interested in is using constants or structs from a weakly linked framework. A C-style function would be similar, but those are not a concern when using CoreLocation. We'll use a CoreLocation constant as an example.
Every time you use it you MUST check to make sure it exists:
if (&kCLErrorDomain != NULL){
// Use the constant
}
Note that the & takes the address of the constant for the comparison.
Also note that you CANNOT do this:
if (kCLErrorDomain){
// Use the constant
}
Or this:
if (&kCLErrorDomain){
// Use the constant
}
Constant symbols can also be lookup up at runtime using dlsym.
Using the negation operator in this way will not work. You must check the address of the constant against the NULL address.
I have put a very simple example of an application that is using a static library which weak links against Core Location on GitHub. The example application does not link against Core Location:
But the dependency does, as an optional (weak) framework:
You could wrap any code and imports that uses the CoreLocation framework in a
#ifdef __CORELOCATION__
... do stuff
#endif
This will negate all CoreLocation code when the framework is not being linked at compile time

iOS + C: the use of __attribute__ ((__constructor__)) in static framework

I did quite a bit of googling for a definitive answer, but I could not find one.
We have cross-platform sources that need to be used by our iOS apps. I have already packaged them as static framework and got a test app to link successfully against and make a call into it.
The problem I am having is that it makes heavy use of global static constructors.
I am seeing erratic behavior with only a subset of objects getting instantiated but not all.
My questions:
Do static constructors even work within the context of an iOS static
lib?
How would one debug such a problem?
Thanks!
constructors in a static library get called erratically
The use of -all_load and -force_load does not make any difference in invocation of constructor attribute labeled functions
What you need to do is this:
make sure the constructors are global symbols. do this by exporting them via a symbols' file.
consider creating an init function and use the linker setting "initialization routine" to control the order of the instantiation of your global objects..
Do static constructors work within the context of an iOS static lib?
They only start to work once they're linked into an executable, but once that is accomplished they will work.
How do you debug such a problem
There are a few approaches
You have to presume no specific order of the invocation of the constructors.
Make sure the constructors are being linked into the executable (use nm to determine this)
You can try an -Wl,-all_load to get it to load all the components of all archives into the executable (or -Wl,-force_load,libstatic.a to just load for a specific static archive).

Resources