Adding dylib to SysExt

I've implemented a custom system-extension VPN (Packet Tunnel Provider) for macOS. At the extension, I need to use a 3rd party dynamic lib. The steps I did: Build phases:

  • Copy files, with Frameworks destination
  • Link Binary With Libraries

Build Settings:

  • I set 'Dynamic Library Install Name', 'Dynamic Library Install Name Base', and 'Library Search Path' to the lib folder
  • I set 'Header Search Path' to the headers folder

But when running the extension, it's crashing with the error

Termination Reason: Namespace DYLD, Code 1 Library missing

Library not loaded: @loader_path/somelib.dylib

And

Reason: tried: '/Library/SystemExtensions/A1111-someID-11111/com.myapp.myappSysExtension.systemextension/Contents/MacOS/libwavmodapi.dylib' (no such file), '/usr/local/lib/libwavmodapi.dylib' (no such file), '/usr/lib/libwavmodapi.dylib' (no such file)

(terminated at launch; ignore backtrace)

Any idea what I'm doing wrong here? Also, is it even possible to use dynamic libs from a sys-ext?

Also, is it even possible to use dynamic libs from a sys-ext?

Yes. If you build a dylib externally and then drag it into your Network System Extension target you should see it show up under the Frameworks and Libraries pane in Xcode. If you switch the embed option to "Emed & Sign" and then build your entire project you will see something that looks like this:

YourApp.app
  Contents/
    _CodeSignature/
    Info.plist
    Library/
      SystemExtensions/
        com.example.apple-samplecode.TestDylib.TestNetworkExt.systemextension/
          _CodeSignature/
          embedded.provisionprofile
          Frameworks/
            YourDylib.dylib
          Info.plist
          MacOS/
    MacOS/
      YourApp
    PkgInfo
    Resources/
Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

@meaton Thanks for the reply! I tried what you said, but I'm still getting the 'Library not loaded: @loader_path/3rdParty.dylib' error.

What I've tried so far:

  1. Drag the dylib files into the sysExt target (so they will be included at the 'Link Binary With Libraries'
  2. Add the dylib files into the 'Embed Frameworks' (build Settings)
  3. Then I tried to add the dylib files both to 'Link Binary With Libraries' and to 'Embed Frameworks'

As I wrote before, I also set 'Dynamic Library Install Name', 'Dynamic Library Install Name Base', and 'Library Search Path' to the relevant lib folder. $(PROJECT_DIR)/libs

But the extension still fails to find the dylib

More details:

  • I didn't create those dylibs, they are from a 3rd party
  • I have also the headers, and I'm calling some of 'their' functions ('I'm using the API') - is it even possible to embed the dylib in such case?
  • In case it relevant - I'm running the my app from Xcode, not from the Application folder (for development purpose)

What else can I try here? Is there any way to 'debug' this / get more useful information?

Matt has explained how to fix this but I wanted to explain why it’s an issue…

This is necessary because, during the installation process, the system copies your sysex to a private directory that it manages. So, when the sysex launches, it can’t reference anything in its container app because it’s not running from its container app.

Share and Enjoy

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

What else can I try here? Is there any way to 'debug' this / get more useful information?

I would recommend taking a look at your container app bundle on disk each time you build a new copy of your app. That is typically how I debug these types of issues. For example, make a change to either your container app or Network System Extension and then do Product -> Build, in Xcode. From there go to Product -> Show Build Folder in Finder. Then do Show Package Contents on your Container App and your Network System Extension. Your Network System Extension will be located inside your Container App at: YourApp.app/Contents/Library/SystemExtensions/. This allows you to see where your dylibs are being placed in your bundle and if they are located in the correct place, i.e., Frameworks/. If the dylib is not is a Frameworks/ directory, that could be causing an issue here.

Regarding:

I didn't create those dylibs, they are from a 3rd party

That's fine, as long as they are built for your app's architecture and you are signing them you should be fine here.

Regarding:

In case it relevant - I'm running the my app from Xcode, not from the Application folder

That's fine. Until you get this sorted out you can just do a build from Xcode to resolve this issue and then once that is done, run it how it makes sense for your development and debugging purposes.

Regarding:

As I wrote before, I also set 'Dynamic Library Install Name', 'Dynamic Library Install Name Base', and 'Library Search Path' to the relevant lib folder. $(PROJECT_DIR)/libs

Xcode should do the "right thing" for you if you just drag the dylib to your Network System Extension target. Double check that these values you are setting are not causing you an issue. For example, try letting Xcode set these values for you and then compare them against the ones you set, if there is a difference then this could be causing an issue also.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

Thanks for all the detailed answers!

I checked at the relevant place ('YourApp.app/Contents/Library/SystemExtensions/mySysEtx/Contents/Frameworks/' and when I dragged the files to the extension, and also added them as embedded frameworks, the dylibs were at the correct location. However, when I run the extension it's still crashing with the same error:

Termination Reason: Namespace DYLD, Code 1 Library missing Library not loaded: @loader_path/libwaresource.dylib Referenced from: /Library/SystemExtensions/*/com.myapp.mySysExtension Reason: tried: '/Library/SystemExtensions/C23234-someUID-1111111/com.myApp.mySysExtension.systemextension/Contents/MacOS/somelib.dylib' (no such file), '/usr/local/lib/somelib.dylib' (no such file), '/usr/lib/somelib.dylib' (no such file) (terminated at launch; ignore backtrace)

If the dylibs are at the right place, why am I getting the 'Library not loaded' error?

I did find some workaround - I saw from crash logs that the extension is looking for the dylib at /Contents/MacOS/ and not at Contents/Frameworks (where the files are actually at) - I don't know what's causing it, but if I'm adding a 'Copy Files' build rule, and copying them as an 'Executables', it copies them to the /MacOS folder, so the extension can load them.

  1. Is it a good solution? How can I make the extension to try to search them at the framework folder?
  2. Once loaded, the dylib will search for a '.cfg' file in the same location. How can I copy 'cfg' file to the dylib location?

it copies them to the /MacOS folder, so the extension can load them. Is it a good solution?

No because /Frameworks is where dylibs and frameworks are supposed to be located, not in /MacOS. See Quinn's article on Placing Content in a Bundle.

Regarding:

Once loaded, the dylib will search for a '.cfg' file in the same location. How can I copy 'cfg' file to the dylib location?

Any config or script files should be stored in the /Resources directory, also see this in the article above.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

Thanks @meaton To conclude - all the dylibs are at the correct location (/Frameworks), the cfg is also at the correct location (/Resources), but I'm still getting the original error - Application Specific Information: Library not loaded: @loader_path/libwaresource.dylib

Reason: tried: '/Library/SystemExtensions/A1111-someID-11111/com.myapp.myappSysExtension.systemextension/Contents/MacOS/libwavmodapi.dylib' (no such file), '/usr/local/lib/libwavmodapi.dylib' (no such file), '/usr/lib/libwavmodapi.dylib' (no such file)

Why the extension is not looking the dylibs at the /Frameworks folder? Is it something I need to change at the Build Settings (search path for example)?

And last details: Running otool on one of the dylibs, shows this

otool -L somelib.dylib somelib.dylib: @loader_path/somelib.dylib (compatibility version 4.0.0, current version 4.3.0) @loader_path/somelib.dylib (compatibility version 4.0.0, current version 4.3.0) /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)

Is it possible that the 'problem' is related to how the dylib was built? If the answer is yes, can I change some build settings at my extension to workaround this problem?

@loader_path is a tricky one. I generally recommend that you do all of this stuff relative to @rpath. I delved into this in some detail in Embedding Nonstandard Code Structures in a Bundle.

IMPORTANT if you’re building the dynamic libraries from source, you don’t need to mess around with install_name_tool. Rather, pass the install name into the linker, using the -install_name option, when you link the library. That’ll set it correctly in the library itself, and clients will pick it up from there.

Share and Enjoy

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

@meaton @eskimo - Thank you very much for all the answers!

Since I got those dylibs from a 3rd party, I'll ask them if they can rebuild them without the @loader_path reference.

Adding dylib to SysExt
 
 
Q