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.

3 comments to XMPPFramework / iOS / libidn / -all_load failure

  • Sean Rasmussen

    Thank you – you salvaged my Saturday night.

    /Sean

  • art-divin

    you have a few mistakes – in the bottom of the article you’ve wrote
    “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.”

    But still there is no libidn.1.25 in the Universe. :)
    Thanks for the article, btw! Couldn’t build it under MacOSX 10.6.7 :)
    error when trying to do make first part of your example
    libtool: line 947: cd: .libs/libidn.lax/libgnu.a: No such file or directory

  • Thanks Mike this was very helpful, now that the iPhone 5 has been released and armv6 is no longer supported.
    To help anyone else these were my settings:

    For i386
    ./configure –host=i686-apple-darwin –disable-shared CC=/Applications/Xcode4.5.app/Contents/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 /Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.0.sdk -fvisibility=hidden -gdwarf-2 -mthumb -miphoneos-version-min=5.1 ” CPP=/Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-cpp-4.2 AR=/Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/ar

    cp lib/.libs/libidn.a libidn-i386.a

    For armv7
    ./configure –host=arm-apple-darwin –disable-shared CC=/Applications/Xcode4.5.app/Contents/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 /Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk -fvisibility=hidden -gdwarf-2 -mthumb -miphoneos-version-min=5.1 ” CPP=/Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-cpp-4.2 AR=/Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ar

    cp lib/.libs/libidn.a libidn-armv7.a

    For armv7s
    ./configure –host=arm-apple-darwin –disable-shared CC=/Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-gcc-4.2 CFLAGS=”-arch armv7s -fmessage-length=0 -pipe -std=c99 -Wno-trigraphs -fpascal-strings -O0 -Wreturn-type -Wunused-variable -Wunused-value -isysroot /Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk -fvisibility=hidden -gdwarf-2 -mthumb -miphoneos-version-min=5.1 ” CPP=/Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/llvm-gcc-4.2/bin/llvm-cpp-4.2 AR=/Applications/Xcode4.5.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ar

    cp lib/.libs/libidn.a libidn-armv7s.a

    Finally:
    lipo -create libidn-armv7s.a libidn-armv7.a libidn-i386.a -output libidn.a

    NOTE: My Xcode app was renamed to Xcode4.5 in my Applications folder, you should rename Xcode4.5.app to Xcode.app or your own Xcode name

Leave a Reply

  

  

  

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>