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
Related
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.
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.
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.
I apologise for a dumb question. I really had tough time with this.
I am now, converting my existing code to support 64-bit. My app uses few frameworks
(I use kstenerud/iOS-Universal-Framework)
And I get this error.
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_MyFrameworkClass", referenced from:
objc-class-ref in MyUI(iPhone_MyVC.o)
MyFrameworkClass is defined in my framework, Which I built using the below settings
In my project, I use this framework and have the same build setting as above.
I am sure, I have the same setting throughout. But still I get this error. Am unsure, what else Am I missing.
You can build the static library for individual architectures and then 'lipo' them together.
Check out the excellent article here: http://blog.diogot.com/blog/2013/09/18/static-libs-with-support-to-ios-5-and-arm64/
You are building your lib for arm only, then you try to run it on x86 (simulator).
Make a lib that supports x86 (64) and use that for debug, the one you have for production.
You should include more of the error message for better help, but it looks as though whatever is providing the MyFrameworkClass class doesn't have a simulator x86_64 slice.
With Xcode 6, you update your project to use Xcode's provided framework target instead of using kstenerud/iOS-Universal-Framework
I'm trying to add the Dropbox Sync API (v1.1.2) to an iOS app built with Marmalade (v6.3). I'm getting the following link error:
Undefined symbols for architecture armv7:
"___udivmodsi4", referenced from:
_sqlite3BitvecSet in libDropbox.a(sqlite3.o)
_sqlite3BitvecClear in libDropbox.a(sqlite3.o)
_sqlite3BitvecTest in libDropbox.a(sqlite3.o)
ld: symbol(s) not found for architecture armv7
Googling for pertinent parts of that error message finds a number of users of a certain SQLCipher library experiencing the same issue, and suggestions that the problem is caused by an inconsistency of the compilers used to build that library and the various projects using it.
As the build system for our project is set up by the Marmalade toolset, changing the compiler (currently a version of GCC 4.4 supplied by Marmalade, I believe) is not an option, I think.
Can anyone tell me more precisely what is going wrong? Are there any other workarounds to this problem?
On processors like ARM, with relatively simple instruction sets, some more complex operations are mapped on to function calls rather sequences of instructions. When you link using the "correct" compiler, the implementations of these are pulled in and it works! You on't normally see it.
The obvious solution here would be to use marmalade to compile the dropbox library, then it will use a compatible compiler.
The question then, I guess, is whether there is a reason you are not doing this to start with? Current Marmalade compilers don't support ARC. I guess that would be a reason not to compile under ARC.
The other answer is correct. However, for this specific problem (if these are the only linker errors you are getting) I see two workarounds:
Grab the source from sqlite3 that includes sqlite3BitvecSet and compile those functions in your own project to override the library. They will pick up whatever divmod support is offered by your own compiler.
Implement your own udivmodsi4. You don't have to implement bitwise division (although you can go get that basic C implementation from the GCC source). You just have to implement it in native operations and let your compiler call whatever internal support it needs.
This is untested, but should give you the basic idea. You may need more underscores on the name to match the behavior/naming of the other build environment:
unsigned long
udivmodsi4(unsigned long num, unsigned long den, int modwanted)
{
if (modwanted)
return num % den;
else
return num / den;
}