Legoless
Legoless

Reputation: 11112

Recompilation with Bitcode changes LC_ID_DYLIB

I'm building a dynamic framework from source for iOS with bitcode enabled (using cmake and xcodebuild). I use lipo and install_name_tool to make a fat binary and update LC_ID_DYLIB, for binary to be loaded correctly. When I archive the application, the framework is correctly signed and packaged with the application. This is the output of file:

MyFramework: Mach-O universal binary with 3 architectures: [arm_v7: Mach-O dynamically linked shared library arm_v7] [arm_v7s] [arm64]
MyFramework (for architecture armv7):   Mach-O dynamically linked shared library arm_v7
MyFramework (for architecture armv7s):  Mach-O dynamically linked shared library arm_v7s
MyFramework (for architecture arm64):   Mach-O 64-bit dynamically linked shared library arm64

Looking at otool -l output for LC_ID_DYLIB shows this:

Load command 4
          cmd LC_ID_DYLIB
      cmdsize 64
         name @rpath/MyFramework.framework/MyFramework (offset 24)
   time stamp 1 Thu Jan  1 01:00:01 1970
      current version 1.0.0
compatibility version 1.0.0

It all seems correct. If I upload this archive to App Store, it gets uploaded and processed correctly. After running it from App Store, it crashes right after start, due to loading dynamic frameworks. It is known that apps get recompiled from Bitcode on App Store, so I simulated that by exporting as Ad-Hoc and leaving "Rebuild from Bitcode" option enabled as suggested in Technical Note TN2432. Inspecting the .ipa (which also crashed after start) and the framework in question, this is the output of otool -l:

Load command 3
          cmd LC_ID_DYLIB
      cmdsize 128
         name /Users/legoless/Downloads/ios/build/build-iphoneos/lib/Release/MyFramework_ios.framework/MyFramework_ios (offset 24)
   time stamp 1 Thu Jan  1 01:00:01 1970
      current version 1.0.0
compatibility version 1.0.0

So obviously the LC_ID_DYLIB of this library is incorrect and this the absolute path to the location where the framework was originally built, before making a fat binary. This is replaced at the Rebuild from Bitcode step, but I have no idea why or even where this path is stored in the existing Mach-O file. I used both otool and objdump tools to try to find the reference in the Mach-O binary, with no luck.

In practice, another framework depends on this one and this is the load command for the target framework:

Load command 14
          cmd LC_LOAD_DYLIB
      cmdsize 64
         name @rpath/MyFramework.framework/MyFramework (offset 24)
   time stamp 2 Thu Jan  1 01:00:02 1970
      current version 1.0.0
compatibility version 1.0.0

Again after Rebuild with Bitcode, the reference gets changed here as well:

Load command 13
          cmd LC_LOAD_DYLIB
      cmdsize 128
         name /Users/legoless/Downloads/ios/build/build-iphoneos/lib/Release/MyFramework_ios.framework/MyFramework_ios (offset 24)
   time stamp 2 Thu Jan  1 01:00:02 1970
      current version 1.0.0
compatibility version 1.0.0

This only happens for the framework in question, but not for other frameworks, where @rpath is left as it was.

My question still remains:

Where is this absolute path reference stored? And how to remove it, so Rebuild from Bitcode does not affect it anymore?

Thank you!

Upvotes: 5

Views: 1059

Answers (2)

mfj
mfj

Reputation: 172

I was experiencing exactly the same issue. The accepted answer got me close, but setting the INSTALL_NAME_DIR parameter by itself didn't fix it as it doesn't allow you to set the complete value passed to the linker as install_name (only the directory, as you might expect).

Instead I set XCODE_ATTRIBUTE_LD_DYLIB_INSTALL_NAME which overwrites the complete value in one go. Obviously this only works on the XCode generator in CMake, but this was fine for what I wanted. To summarise, my CMake file for this framework now contains:

set_target_properties(${LIBRARY_NAME} PROPERTIES XCODE_ATTRIBUTE_LD_DYLIB_INSTALL_NAME "@rpath/${LIBRARY_NAME}.framework/${LIBRARY_NAME}")
set_target_properties(${LIBRARY_NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH 1)

Upvotes: 1

Legoless
Legoless

Reputation: 11112

Doing detailed investigation into this issue, showed that the .xar archive that contains bitcode inside Mach-O file stores quite a bit of information, including linker flags. This information is stored in Table of Contents of the archive and is used to recompile/relink the libraries on bitcode recompilation.

In my case, I was building the framework with cmake, which added this information into linker flags. Configuring INSTALL_NAME_DIR and BUILD_WITH_INSTALL_RPATH and using @rpath effectively fixed the issue, and install_name_tool was not required anymore.

Upvotes: 3

Related Questions