XcodesOrg / xcodes

The best command-line tool to install and switch between multiple versions of Xcode.
MIT License
3.86k stars 133 forks source link

Attempting to link `LibFido2Swift` to `xcodes` #387

Open hi2gage opened 1 month ago

hi2gage commented 1 month ago

Do not merge

I'm looking for some help linking LibFido2Swift to the xcodes executableTarget.

The project builds, it will run as expected if running within Xcode.

Screenshot 2024-10-18 at 12 58 51 PM

but as soon as I go to run that exact same build from the command line /Users/gage/Library/Developer/Xcode/DerivedData/xcodes-eftbhwhateverlfqcanhgofyytinnj/Build/Products/Debug/xcodes list

I get this error

dyld[86238]: Library not loaded: @rpath/libcrypto.3.dylib
  Referenced from: <BBAB7C02-49D7-3938-BC1C-CB4BF14387FB> /Users/gage/Library/Developer/Xcode/DerivedData/xcodes-eftbhsfbyqplfqcanhgofyytinnj/Build/Products/Debug/xcodes
  Reason: tried: '/Users/gage/Library/Developer/Xcode/DerivedData/xcodes-eftbhsfbyqplfqcanhgofyytinnj/Build/Products/Debug/PackageFrameworks/libcrypto.3.dylib' (no such file),
'/System/Volumes/Preboot/Cryptexes/OS/Users/gage/Library/Developer/Xcode/DerivedData/xcodes-eftbhsfbyqplfqcanhgofyytinnj/Build/Products/Debug/PackageFrameworks/libcrypto.3.dylib' (no such file),
 '/Users/gage/Library/Developer/Xcode/DerivedData/xcodes-eftbhsfbyqplfqcanhgofyytinnj/Build/Products/lib/libcrypto.3.dylib' (no such file),
 '/Users/gage/Library/Developer/Xcode/DerivedData/xcodes-eftbhsfbyqplfqcanhgofyytinnj/Build/Products/Debug/PackageFrameworks/libcrypto.3.dylib' (no such file),
 '/System/Volumes/Preboot/Cryptexes/OS/Users/gage/Library/Developer/Xcode/DerivedData/xcodes-eftbhsfbyqplfqcanhgofyytinnj/Build/Products/Debug/PackageFrameworks/libcrypto.3.dylib' (no such file),
 '/Users/gage/Library/Developer/Xcode/DerivedData/xcodes-eftbhsfbyqplfqcanhgofyytinnj/Build/Products/lib/libcrypto.3.dylib' (no such file)
[1]    86238 abort

At first I thought it was an issue with LibFido2Swift not linking libcrypto but I tried adding

linkerSettings: [
    .linkedFramework("LibCbor"),
    .linkedFramework("LibCrypto"),
    .linkedFramework("libfido2"),
]

but that did not work either.

I then tried to just copy the content LibFido2Swift into this project then create a "local" LibFido2Swift module internally and that didn't work either.

I'm wondering if I'm missing something about the linking binaryTargets to a executable product.

@kinoroy or @MattKiazyk if could take a look at this branch see if I'm missing anything that would be awesome!

kinoroy commented 1 month ago

Hm, I guess the problem is that LibFido2Swift relies on dynamic/shared libraries. Shared libraries aren't merged into the executable. If you have an app bundle, they're embedded in the bundle (example: Xcodes.app/Contents/Frameworks/...), but with an executable there's no obvious place to store those libraries, unless we install them somewhere explicitly on the users machine and then set the runtime load path to search there. Will have to think a bit more about how we could do this. We could bundle the CLI tool in the Xcodes.app bundle, and users could run it from there but I'm not sure how easy that would be given the projects are split into 2 separate repos

kinoroy commented 1 month ago

Also I believe that it works when running from within Xcode because Xcode does some magic by setting DYLD_LIBRARY_PATH environment variable to be the DerivedData folder when running the executable, which overrides the dylib's baked in ID path of @rpath/libcrypto.3.dylib or the equivalent for other libraries. When you run from the command line, it goes back to trying to load from @rpath/libcrypto.3.dylib and since we haven't installed the dylib anywhere in the rpath, it won't be able to find it. In the Xcodes.app bundle we install it in the Frameworks folder which is in the rpath.

hi2gage commented 1 month ago

Interesting. Yeah I kind of assumed that Xcode was crating some sort of magic sandboxing.

Is there a way we could "bundle" all of the dependencies into a single package that includes the static dependencies?

Seems like there are other people dealing with adjacent issues. https://forums.swift.org/t/swiftpm-binary-target-with-sub-dependencies/40197

It would be great if we could keep this separate from Xcodes.app.

I can't find the forum post, but there was a forum reply somewhere talking telling the compiler to look at the global install path. But that doesn't allow us to only tell users who need this to install the given dependencies.

hi2gage commented 1 month ago

or we rewrite libfido2 in swift along with all of it's dependencies 😜

kinoroy commented 1 month ago

Is there a way we could "bundle" all of the dependencies into a single package that includes the static dependencies?

I'm not sure if this is possible with SPM. I could maybe just make a .bundle manually, or with a script that just puts all the dylib dependencies together and release that as a release artifact on the LibFido2Swift repo. But then for using this in xcodes I'm not sure if SPM even provides any way to set the rpath for an executable? We might need to be using an Xcode project to do that, and not just a Swift package as its currently set up.

Maybe mergable libraries could also help, but that's also an Xcode feature and not supported in SPM to my knowledge.

MattKiazyk commented 1 month ago

Sorry busy trying to understand crypto for the login issue so haven't been responding.

What is LibCrypto and LibCbor in your repo?

The idea that I had was that if the only dependency is libfido2 - then you check (similar to aria2) for that executable on the users device if they are trying to use key based authentication. If they don't have it, they have to install it, like aria2 (or pass in the path).

kinoroy commented 1 month ago

libcrypto and libcbor are both dynamic library dependencies of libfido2. libcrypto is part of OpenSSL, and libcbor is an open source library for parsing CBOR. libfido2 uses them at runtime, so I don't think there's a way around needing to have them installed somewhere.

Yeah perhaps we can do something like what is done for aria2 where the user provides the dependencies, but it's a bit trickier since these are not executables but dynamic libraries. If SPM supports optionally linking in dynamic libraries and then loading them dlopen then it could maybe be done that way. But I don't know if that's possible with SPM.

hi2gage commented 1 month ago

libcrypto and libcbor are both dynamic library dependencies of libfido2. libcrypto is part of OpenSSL, and libcbor is an open source library for parsing CBOR. libfido2 uses them at runtime, so I don't think there's a way around needing to have them installed somewhere.

Yeah perhaps we can do something like what is done for aria2 where the user provides the dependencies, but it's a bit trickier since these are not executables but dynamic libraries. If SPM supports optionally linking in dynamic libraries and then loading them dlopen then it could maybe be done that way. But I don't know if that's possible with SPM.

Yeah I don't think there is optional linking in SPM currently.

IMO libfido2 is a little bloated for our use case all we really want to do is get the HID device list and then have it respond to the challenge then we pass that back. We don't care about linux or all of the other editing and saving of pins etc...

Looking deeper into libfido2's implementation it's using IOKit/hid which Apple provides Swift wrappers. I would vote that we just implement the functionality needed in swift using swift IOKit and do not use the libfido2 at all. I started coding some examples last night and I was able to get IOHIDManager reading my hardware keys.

It will still be a good chunk of work but I think long term it's better than doing the dynamic linking gymnastics.

I'm more than willing to continue working on this to get it over the finish line

kinoroy commented 1 month ago

Yeah honestly I think that's a really good idea. I had a lot of trouble getting it working for Xcodes.app in the first place lol so I would be down for this approach.

Lmk if you need any help :)