Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime Identifier for Universal Binaries #53156

Open
yaakov-h opened this issue May 24, 2021 · 2 comments
Open

Runtime Identifier for Universal Binaries #53156

yaakov-h opened this issue May 24, 2021 · 2 comments
Milestone

Comments

@yaakov-h
Copy link
Member

yaakov-h commented May 24, 2021

Lifting this out of the discussion at dotnet/designs#217:

Overview

.NET apps, at the very end of the day, have to communicate with native APIs. Not everything is done in managed code. Many third-party libraries and SDKs are written in code that gets compiled to native CPU instructions, and its not just C/C++ code, there is also a trend now to rewrite common libraries in Rust.

Some platforms have a concept of "fat binaries" or "universal binaries", which are binaries that contain CPU instructions for multiple CPUs.

In Apple's environments this has been a feature since NeXTSTEP 3.1, and has been critical in the migration from PowerPC processor to Intel processors, from Intel 32-bit to Intel 64-bit, and again now from Intel processors to Apple Silicon (Arm). It has also been used heavily (though relatively quietly) in iPhone OS/iOS/iPadOS as Apple have moved through armv6, armv7, armv7s and arm64.

Though I'm not familiar with the details, dotnet/sdk#16896 suggests that Windows now has a similar mechanism to produce universal binaries.

As it currently stands I believe that the .NET Runtime does not take this into consideration when searching for files when attempting to load native binaries, which results in extra development effort or wasted resources.

Use-Case

Let's say I have a really cool library named Foo, as all great things are named. For Windows I would generally produce a x86 foo.dll and a x64 foo.dll. For Linux I would generally produce a i386 libfoo.so and a x86-64 libfoo.so.

For macOS, there is no need to follow this pattern. I can produce a single libfoo.dylib that contains x86 code, x86_64 code, and arm64e code all in the same file. For iOS it is similar but with different platforms.

Package managers or proprietary 3rd-party SDKs may ship in this format, so this becomes input for the .NET SDK and .NET Runtime.

As it stands, I believe there are only two ways to consume such a binary, neither of which are satisfactory:

1. Duplicate the binary:

I can take the existing libfoo.dylib and copy it into multiple locations. I don't know the runtime paths off the top of my head but for a NuGet package I would need to copy it to:

  • runtimes/osx-x64/native/libfoo.dylib
  • runtimes/osx-arm64/native/libfoo.dylib

This is wasteful as it consumes:

  • additional disk space when unpacked
  • additional disk space when packaged, as ZIP files (.nupkg) do not apply compression across multiple entries,
  • additional network transfer (see above, additional disk space when packaged)

2. Split the binary:

I can take the existing libfoo.dylib and use lipo to split it into multiple slices. I can then place each of these slices at runtimes/osx-<arch>/native/libfoo.dylib.

This requires additional effort on the consumer's part to subvert the native operating system's capabilities and split the binary into separate files just so that .NET's loader will find them in the correct locations.

Proposal

I would like to suggest that .NET should be able to consume universal binaries as-is, without duplication or splitting. In the example above, I expect there would be a universal RID defined for each platform (e.g. osx or osx-universal, ios or ios-universal, etc.) and I can place the native assembly in that folder.

Then, when .NET attempts to load the universal binary, the native operating system dynamic library loader should automatically select the correct architecture slice to match the CPU architecture of the .NET runtime process, as it does for any other application/platform/runtime.

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label May 24, 2021
@krwq
Copy link
Member

krwq commented Jul 12, 2021

cc: @terrajobst

@krwq krwq added this to the 7.0.0 milestone Jul 12, 2021
@krwq krwq removed the untriaged New issue has not been triaged by the area owner label Jul 12, 2021
@danmoseley danmoseley modified the milestones: 7.0.0, Future Jul 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Development

No branches or pull requests

4 participants