XMPPFramework / iOS / libidn / -all_load failure

As I integrated the XMPPFramework it requires the libidn library for a couple of methods it uses.  The latest version of libidn that the XMPPFramework included as of this writing is 1.15.

Unfortunately, this version seems incompatible with the “Other Linker Flag” -all_load that other libraries seem to want to use.  The all_load linker flag appears be a work around for a linker bug that pops up when object files within a static library only contain a category and no classes.

From the source: http://developer.apple.com/mac/library/qa/qa2006/qa1490.html

IMPORTANT: For 64-bit and iPhone OS applications, there is a linker bug that prevents -ObjC from loading objects files from static libraries that contain only categories and no classes. The workaround is to use the -all_load or -force_load flags. -all_load forces the linker to load all object files from every archive it sees, even those without Objective-C code. -force_load is available in Xcode 3.2 and later. It allows finer grain control of archive loading. Each -force_load option must be followed by a path to an archive, and every object file in that archive will be loaded.

Interestingly, most coding conventions will create header and implementation files for categories that are just the categories; hence exposing the defect.  Some library developers have come up with a simple workaround so that the force_load or all_load flags are not required.  The workaround is a simple macro that is added to the category implementation file that just creates a class so that the bug is not exposed.  Don’t know if it originated in Three20, but this is first place I came across it.  RestKit also implemented the macro.

Unfortunately, other library developers have not yet implemented this, and still require the all_load flag to be used.

The use of the all_load flag has been causing the XMPPFramework’s use of libidn to fail to link.  In part this appears to be the use of older libidn that is included as fat binary,  static library.  Use of the all_load flag often causes this linker error:

Undefined symbols for architecture i386:
“_libintl_bindtextdomain”, referenced from:
_idna_strerror in libidn.a(strerror-idna.o)
_pr29_strerror in libidn.a(strerror-pr29.o)
_punycode_strerror in libidn.a(strerror-punycode.o)
_stringprep_strerror in libidn.a(strerror-stringprep.o)
_tld_strerror in libidn.a(strerror-tld.o)
“_libintl_dgettext”, referenced from:
_idna_strerror in libidn.a(strerror-idna.o)
_pr29_strerror in libidn.a(strerror-pr29.o)
_punycode_strerror in libidn.a(strerror-punycode.o)
_stringprep_strerror in libidn.a(strerror-stringprep.o)
_tld_strerror in libidn.a(strerror-tld.o)
“_libiconv”, referenced from:
_str_cd_iconv in libidn.a(striconv.o)
_mem_cd_iconv in libidn.a(striconv.o)
“_libiconv_open”, referenced from:
_str_iconv in libidn.a(striconv.o)
“_libiconv_close”, referenced from:
_str_iconv in libidn.a(striconv.o)
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The iconv errors can be fixed because the iOS framework includes libiconv.a.  It does not include libintl.a.  After downloading version 1.25 of libidn and getting it to install on iOS 5.0, I was able to get rid of errors related to intl.

Here are the commands I used to compile libidn.1.25 and create a fat binary with arm6, arm7, and i386 architecture files.


$ ./configure --host=arm-apple-darwin --disable-shared CC=/Developer/Platforms/iPhoneOS.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-gcc-4.2 CFLAGS="-arch armv7 -fmessage-length=0 -pipe -std=c99 -Wno-trigraphs -fpascal-strings -O0 -Wreturn-type -Wunused-variable -Wunused-value -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -fvisibility=hidden -gdwarf-2 -mthumb -miphoneos-version-min=4.0 " CPP=/Developer/Platforms/iPhoneOS.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-cpp-4.2 AR=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ar
$ make clean
$ make
$ cp lib/.libs/libidn.a libidn-arm7.a

$ ./configure --host=arm-apple-darwin --disable-shared CC=/Developer/Platforms/iPhoneOS.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-gcc-4.2 CFLAGS="-arch armv6 -fmessage-length=0 -pipe -std=c99 -Wno-trigraphs -fpascal-strings -O0 -Wreturn-type -Wunused-variable -Wunused-value -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -fvisibility=hidden -gdwarf-2 -mthumb -miphoneos-version-min=4.0 " CPP=/Developer/Platforms/iPhoneOS.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-cpp-4.2 AR=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ar
$ make clean
$ make
$ cp lib/.libs/libidn.a libidn-arm6.a

$ ./configure --host=i686-apple-darwin --disable-shared CC=/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-gcc-4.2 CFLAGS="-arch i386 -fmessage-length=0 -pipe -std=c99 -Wno-trigraphs -fpascal-strings -O0 -Wreturn-type -Wunused-variable -Wunused-value -isysroot /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk -fvisibility=hidden -gdwarf-2 -mthumb -miphoneos-version-min=4.0 " CPP=/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-cpp-4.2 AR=/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/ar
$ make clean
$ make
$ cp lib/.libs/libidn.a libidn-i386.a

$ lipo -create libidn-i386.a libidn-arm6.a libidn-arm7.a -output libidn.a

After importing the new libidn,a, I still received the linking errors with libiconv.  Added that to the library link list and afterwards all worked.