Code signing of dylib for use under iOS

We are in the process of developing our text to speech and speech analysis tools over to mobile platforms = cross platform development.

The tools are written in C++ and are compiled using CMake with an ios specific toolchain targetting the correct platform sdk.

One of the parts of this toolkit is the dynamic loading of language specific dylibs via dlopen.

I have seen that this can only be done if the dylib has been signed with the same certificate as the application.

Note that we are still using "free" developer certificates generated automatically by XCode.

When I run the test application, at the point where the dylib should be loaded via dlopen, the load fails and dlerror returns the following :

dlopen(<path to dylib>, 0x0001): code signature invalid (errno=1) sliceOffset=0x00000000, codeBlobOffset=0x0205D0F0, codeBlobSize=0x000453B0 for '<path to dylib>'

However, when I check the code signature with :

codesign -d --verbose=2 --extract-certificates <path>

I get the same certificate output from both the application bundle and the dylib in question.
For example :


Identifier=libnormaliser_fr
Format=Mach-O thin (arm64)
CodeDirectory v=20400 size=265332 flags=0x0(none) hashes=8286+2 location=embedded
Signature size=4755
Authority=Apple Development: <our apple id>
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
Signed Time=4 Jan 2021 at 09:27:14
Info.plist=not bound
TeamIdentifier=<our team id>
Sealed Resources=none
Internal requirements count=1 size=192

Now, when I run the command :

codesign --verify --deep --verbose=2

it outputs :

./libxxx.dylib: valid on disk
./libxxx.dylib: satisfies its Designated Requirement

I am testing on an iPad with iOS version 14.0.1, by the way.

So, something is still missing, but what ?
Can anyone help me with this please ?

Replies

In fact, there is only one instance of my dylib, the one inside the
Frameworks directory.

Hmmm, that seems wonky. Try this:
  1. Build your test app, the one including the framework wrapped around your shared library.

  2. Run the following command below to dump its structure:

  3. Post the results (if it’s small, post it inline; if it’s large, use a text attachment).

.

Code Block
% find /path/to/your.app


Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
Hi Quinn,

Good news for today.
After having thought that I could at last load the dylib - the app crashes without warning = no exception or error message.

So ...
  • I wrote a really simple dylib with one really simple function and compiled it.

  • I then signed it with the same bundle identifier as the application

  • I created a framework for it and slotted it in.

  • The application then copies the framework into the applications Library directory

  • I then load the dylib directly from C++ code using dlopen

  • I can then obtain a pointer to the simple function and execute it successfully

So, all I need to do now is find what library initialisations are causing the crash.
Thank you for all your help.

Hi Quinn (@eskimo),

We're having similar issues right now. We have a bunch of C shared libraries that depend on each other that we'd like to include as a framework into an iOS application.

They are all compiled to .dylib and we've created a universal framework that has all the libraries set as "Link Binary With Libraries".

This however was not enough to add the .dylibs into the framework. We also had to add a "Copy Bundle Resources Phase" where we also listed the libraries.

This now creates a .framework that contains the .dylibs and the main framework executable links to the dylibs so they are automatically loaded. This is where the issue starts however.

It seems that something is going wrong at codesigning because we get the following error when the framework is loading:

'$PATH_TO_DYLIB_HERE' not valid for use in process: mapped file has no cdhash, completely unsigned?

I was under the impression that if I add a framework to the Framework Libraries and Embedded Content step and mark it as Embed & Sign then it and all its contents would be codesigned.

To me it seems as if the .dylibs inside the framework are simply ignored for codesigning. Is there any automatic process or setting that I'm missing here? Or is the general case just that we need a framework for each dylib (which would be extremely annoying as we have about 20 of them) because the code signing system can't handle signing files inside frameworks?

This now creates a .framework that contains the .dylibs and the main framework executable links to the dylibs so they are automatically loaded.

This won’t work. iOS does not support standalone shared libraries (.dylib) [1]. Providing an outer layer of .framework wrapper does not get around that limitation. You’d need to wrap each library in its own .framework wrapper.

A more sensible solution, however, would be to build each component into a static library and link them all together into a single dynamic library and wrap that in a .framework. Or link them directly into your product, assuming you don’t have some other need for the framework.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Except for those from the Swift runtime.