Marking a symbol as undefined in iOS framework - ios

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.

Related

Xcode: Handling "duplicate symbol" for external libraries

In our project we rely on 2 printer libraries: StarIO & Epson ePOS, and during compile i hit this error:
duplicate symbol _GetOnlineStatus in:
/Users/brendan/Development/xxxx/Frameworks/StarIO.framework/StarIO(StarIOPort.o)
/Users/brendan/Development/xxxx/xxxxApp/SDKs/Epson/libepos2.a(eposprint_common_status.o)
ld: 1 duplicate symbol for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see
invocation)
Need some help with the best way to resolve this issue please.
This situation can sometimes be challenging — since both libraries are seemingly using the same symbol and because the implementations could be different. The only reliable way to fix this is to have each library vendor use a prefix for each of their classes/symbols within their libraries.
Apart from that, since I assume you only have the compiled version of the epson library (libepos2.a) you'll either want to:
Rename the symbol(s) that clash in StarIO.framework
Combine both libraries.
Option 1:
If you decide to simply rename the symbol causing the issue do a search in StarIO.framework:
GetOnlineStatus
Then rename it everywhere (in 12 files) it shows up in that library to something slightly different:
StarIO_GetOnlineStatus
Located in the following:
StarIO.framework/Versions/A/Headers/SMPort.h
StarIO.framework/Versions/A/Headers/WBluetoothPort.h
StarIO.framework/Versions/A/Headers/BluetoothPort.h
StarIO.framework/Versions/A/Headers/starmicronics/StarIOPort.h
StarIO.framework/Versions/Current/Headers/SMPort.h
StarIO.framework/Versions/Current/Headers/WBluetoothPort.h
StarIO.framework/Versions/Current/Headers/BluetoothPort.h
StarIO.framework/Versions/Current/Headers/starmicronics/StarIOPort.h
StarIO.framework/Headers/SMPort.h
StarIO.framework/Headers/BluetoothPort.h
StarIO.framework/Headers/WBluetoothPort.h
StarIO.framework/Headers/starmicronics/StarIOPort.h
Option 2:
The other option would be to combine the two libraries into one, although that can be quite a bit more complicated and present other issues perhaps. For details on how to go about doing so please see this answer here on stackoverflow.

Avoid symbol reference in static library

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.

Link error adding library built with Clang to iOS app built with GCC

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;
}

Suppress linker warning: "Meta method X in category from Y overrides method from class in Z"

I am intentionally using a category to override a method that I know is already implemented on a primary class. I know this is typically a sign of weak design-- please, no lectures-- but I can't subclass cleanly in this case. I know swizzling may also be an option.
But for right now, how can I suppress this warning? llvm throws a compiler warning that I can disable (diagnostic ignored "-Wobjc-protocol-method-implementation"). But then the linker also complains.
This asks a similar question but was looking for a different answer. How can I tell the linker not to complain?
Thanks.
Unfortunately, there's no good answer.
The only linker-based solution is to pass -Wl,-w at link time; that is, tell Clang to pass the -w option through to the linker. This will suppress all linker warnings, potentially including ones you'd still like to see.
A higher-level workaround is to pipe the linker's output through grep -v. The details of that solution would tend to depend heavily on your shell and your build system.

Preventing "duplicate symbol" errors with iOS frameworks

Apple uses the following code in the header of all its framework classes.
#if !defined(__COREFOUNDATION_CFARRAY__)
#define __COREFOUNDATION_CFARRAY__ 1
...
#endif
Is this a recommended approach for eliminating "duplicate symbol" linker errors, when designing classes or categories for framework use, or are these left over protection from the use of #include instead of #import in c?
Research into this has lead me to this article on include guard
NOTE: this question is not asking how to fix a duplicate symbol error, but instead asking if there is any way of preventing your own code from causing the problem if its included more than once in a project.
You're right about the include guard - there's probably some compatibility reason it's not been removed from the source.
However, this won't really protect you against duplicate symbols much.
For example,
What if you have two third party library, each of which uses the SBJSON library (I had this happen to a colleague a few weeks ago).
Each of the libraries was compiled seperately so, from their point of view, SBJSON was only included once. However, when I came to link my app I couldn't because I had duplicate symbols.
I had to solve this by manually removing the symbols from one of the .a library files (This link shows it's quite a common problem!)
EDIT : This link is a much clearer step by step solution to the problem
Apple uses the following code in the header of all its framework classes.
Not necessarily for the ObjC APIs. But CoreFoundation, yes they use include guards. It's still idiomatic in many camps to use traditional #includes in C sources, and to use #import for objc sources (#import is a compiler extension, not traditional C).
Is this a recommended approach for eliminating "duplicate symbol" linker errors
No, it does not prevent linker errors; it can result in duplicate declaration errors during compile phases.
If you're getting duplicate symbol linker errors, the problem is something else, such as visibility of definition. For that, you should provide an example of your troubling program.

Resources