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

Deterministic MVIDs is needed for assemblies referencing System.Private.CoreLib across different architectures #67660

Closed
akoeplinger opened this issue Apr 6, 2022 · 6 comments · Fixed by #72143
Assignees
Milestone

Comments

@akoeplinger
Copy link
Member

akoeplinger commented Apr 6, 2022

We're hitting an issue where the MVID of assemblies is different depending on the architecture of the runtime pack, e.g. x64 vs. arm64, for assemblies that are 100% managed code like System.Collections.dll or System.Private.Uri.dll but compile against System.Private.CoreLib.

This causes an issue for Xamarin Android and other internal teams that use the MVID to deduplicate assemblies that are the same across architectures (right now only System.Private.CoreLib.dll is actually architecture-specific).

The reason this is showing up now is because of a linker/Cecil fix (dotnet/linker#2203, dotnet/cecil#31) which fixes the PDB ID calculation which in turn affects the MVID calculation (we actually just got lucky in net6 since we run the linker against all assemblies in the libraries build which "unified" the MVID).

Since the PDB is storing the MVID of all assembly references in "Compilation Metadata References" (described in https://github.com/dotnet/runtime/blob/424a09cb81c678fb1ba1c27211b80aba2de070ad/docs/design/specs/PortablePdb-Metadata.md#CompilationMetadataReferences) we now get a different MVID on the assemblies because these projects compile directly against System.Private.CoreLib.dll whose MVID gets embedded in the PDB:
PDB

There is interest to backport the linker/Cecil fix to net6 so I discussed with @vitek-karas and we agreed the least invasive change is to simply hardcode System.Private.CoreLib.dll as the only architecture-specific assembly in Xamarin Android for net6. EDIT it turns out this plan won't work since the Mono AOT compiler verifies the MVIDs of assembly references and will error out if they don't match with the actual assembly.

For net7/net8 we'd like to come up with a "proper" fix though, some ideas:

  • Create a reference assembly for System.Private.CoreLib and compile against that one, instead of the real assembly
    • I tried using Roslyn's /refout feature but the reference assembly created by it still differs between architectures since Roslyn includes private fields like Padding.cs#L9-L16, so this'd likely need to be a real reference assembly
  • Make System.Private.CoreLib architecture-agnostic by removing all of the TARGET_64BIT and TARGET_32BIT ifdefs.
  • Tweak the (official) build so we only ever build the non-architecture-specific assemblies once and copy them into the respective runtime packs, ensuring that we only have a single version of the assemblies
    • As an extension we could split out the assemblies into another package, so they're not duplicated in the runtime packs. This would also help reducing download size.

/cc @ViktorHofer @vitek-karas @agocke @marek-safar @steveisok

@akoeplinger akoeplinger added this to the 7.0.0 milestone Apr 6, 2022
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Apr 6, 2022
@ghost
Copy link

ghost commented Apr 6, 2022

Tagging subscribers to this area: @dotnet/runtime-infrastructure
See info in area-owners.md if you want to be subscribed.

Issue Details

We're hitting an issue where the MVID of assemblies is different depending on the architecture of the runtime pack, e.g. x64 vs. arm64, for assemblies that are 100% managed code like System.Collections.dll or System.Private.Uri.dll but compile against System.Private.CoreLib.

This causes an issue for Xamarin Android and other internal teams that use the MVID to deduplicate assemblies that are the same across architectures (right now only System.Private.CoreLib.dll is actually architecture-specific).

The reason this is showing up now is because of a linker/Cecil fix (dotnet/linker#2203, dotnet/cecil#31) which fixes the PDB ID calculation which in turn affects the MVID calculation (we actually just got lucky in net6 since we run the linker against all assemblies in the libraries build which "unified" the MVID).

Since the PDB is storing the MVID of all assembly references in "Compilation Metadata References" (described in https://github.com/dotnet/runtime/blob/424a09cb81c678fb1ba1c27211b80aba2de070ad/docs/design/specs/PortablePdb-Metadata.md#CompilationMetadataReferences) we now get a different MVID on the assemblies because these projects compile directly against System.Private.CoreLib.dll whose MVID gets embedded in the PDB:
PDB

There is interest to backport the linker/Cecil fix to net6 so I discussed with @vitek-karas and we agreed the least invasive change is to simply hardcode System.Private.CoreLib.dll as the only architecture-specific assembly in Xamarin Android for net6.

For net7/net8 we'd like to come up with a "proper" fix though, some ideas:

  • Create a reference assembly for System.Private.CoreLib and compile against that one, instead of the real assembly
    • I tried using Roslyn's /refout feature but the reference assembly created by it still differs between architectures since Roslyn includes private fields like Padding.cs#L9-L16, so this'd likely need to be a real reference assembly
  • Tweak the (official) build so we only ever build the non-architecture-specific assemblies once and copy them into the respective runtime packs, ensuring that we only have a single version of the assemblies
    • As an extension we could split out the assemblies into another package, so they're not duplicated in the runtime packs. This would also help reducing download size.

/cc @ViktorHofer @vitek-karas @agocke @marek-safar @steveisok

Author: akoeplinger
Assignees: -
Labels:

discussion, area-Infrastructure

Milestone: 7.0.0

@steveisok steveisok removed the untriaged New issue has not been triaged by the area owner label Apr 7, 2022
jonpryor pushed a commit to dotnet/android that referenced this issue May 5, 2022
Context: dotnet/runtime#56989
Context: dotnet/runtime#68734
Context: dotnet/runtime#68914
Context: dotnet/runtime#68701

Changes: dotnet/installer@04e40fa...c7afae6

	% git diff --shortstat 04e40fa9...c7afae69
	 98 files changed, 1788 insertions(+), 1191 deletions(-)

Changes: dotnet/runtime@a21b9a2...c5d40c9

	% git diff --shortstat a21b9a2d...c5d40c9e
	 28347 files changed, 1609359 insertions(+), 1066473 deletions(-)

Changes: dotnet/linker@01c4f59...04c49c9

	% git diff --shortstat 01c4f590...04c49c9d
	 577 files changed, 28039 insertions(+), 10835 deletions(-)

Updates to build with the .NET 7 SDK and use the runtime specified by
the SDK.  We no longer use different SDK & runtime versions.

This produces a 7.0.100 Android workload.

After this is merged we should be able to enable Maestro to consume
future .NET 7 builds from dotnet/installer/main.

~~ Known Issues ~~

AOT+LLVM crashes on startup:

  * dotnet/runtime#68914

Xamarin.Build.Download hits a breaking change with `ZipEntry`:

  * dotnet/runtime#68734
  * xamarin/XamarinComponents#1368

illink outputs different MVIDs per architecture:

  * dotnet/linker#2203
  * dotnet/runtime#67660

Size of `libmonosgen-2.0.so` regressed:

  * dotnet/runtime#68330
  * dotnet/runtime#68354

Newer .NET 7 builds crash on startup:

  * dotnet/runtime#68701
  * This is worked around by *disabling* lazy loading of AOT'd
    assemblies 6dc426f.
  * TODO: re-enable once we get a fixed .NET 7 runtime.

TODO: We can't yet push to the `dotnet7` feed. Working on this.

Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com>
Co-authored-by: Peter Collins <pecolli@microsoft.com>
@akoeplinger
Copy link
Member Author

akoeplinger commented May 9, 2022

We had a meeting on Friday and discussed this a bit more, here are the results:

  • Creating a reference assembly for System.Private.CoreLib.dll looks like the best/easiest option for unblocking the Android team
  • We discussed making CoreLib architecture-agnostic as it would help save a lot of size for Android apps (CoreLib needs to be duplicated across four architectures right now), but it's not fully clear yet how much work this entails and it's dependent on the runtime team supporting this approach. In addition to that we'll likely run into issues with the "library trimming" mode that we use in the build, we'd need to make sure it doesn't optimize code for one architecture. It can be done independently and/or in addition to the reference assembly work.

@ViktorHofer also explained that having a CoreLib reference assembly has other benefits too:

  • Makes it possible to keep public APIs between the different flavours of CoreLib in sync and using existing tooling (ApiCompat) to validate that, see System.Private.CoreLib should expose consistent surface area across all OSes #7553.
  • Improves developer innerloop when libraries build against CoreLib since a change no longer triggers rebuilding libs
  • Removes the current hack in our build system that allows an AnyCPU libs to reference an arch specific lib.
  • Keeps the MVID not just between different architectures in sync but also between diferent OS specific implementations.

A downside is that we'll need to manually build and maintain the reference assembly since the /refout switch doesn't work here. We also don't know yet whether we have mismatches in the public API surface area between OS/arch that we need to reconcile.

/cc @jkotas @ericstj

@marek-safar marek-safar changed the title Discussion: Deterministic MVIDs for assemblies referencing System.Private.CoreLib across different architectures Deterministic MVIDs is needed for assemblies referencing System.Private.CoreLib across different architectures May 10, 2022
@ViktorHofer
Copy link
Member

ViktorHofer commented May 10, 2022

<!-- Disable TargetArchitectureMismatch warning when we reference CoreLib as it is platform specific. -->
<Target Name="DisableProjectReferenceArchitectureMismatchWarningForCoreLib"
Condition="'@(_coreLibProjectReference)' != ''"
BeforeTargets="ResolveAssemblyReferences">
<PropertyGroup>
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
</PropertyGroup>
</Target>
We explicitly had to disable an msbuild check to be able to reference an arch specific assembly from an AnyCPU assembly. When doing the proposed set of work (either introducing a reference assembly or making CoreLib arch agnostic), we should also eliminate this hack

@jonathanpeppers
Copy link
Member

One issue that came up, is when using a .NET 7 SDK to build a net6.0-android app:

  • You have the .NET 7 linker, and it's newer Mono.Cecil behavior
  • You have the old .NET 6 System.Private.CoreLib.dll

So if there was a ref assembly for System.Private.CoreLib.dll, it wouldn't fix building net6.0 apps in .NET 7?

@vitek-karas
Copy link
Member

@jonathanpeppers I think the answer is yes - the linker would produce different MVIDs for different archs of framework assemblies since they would be .NET 6 framework which would have internally different assemblies per arch (if nothing else, due to the CoreLib difference).

@ghost
Copy link

ghost commented Jul 6, 2022

Tagging subscribers to this area: @directhex
See info in area-owners.md if you want to be subscribed.

Issue Details

We're hitting an issue where the MVID of assemblies is different depending on the architecture of the runtime pack, e.g. x64 vs. arm64, for assemblies that are 100% managed code like System.Collections.dll or System.Private.Uri.dll but compile against System.Private.CoreLib.

This causes an issue for Xamarin Android and other internal teams that use the MVID to deduplicate assemblies that are the same across architectures (right now only System.Private.CoreLib.dll is actually architecture-specific).

The reason this is showing up now is because of a linker/Cecil fix (dotnet/linker#2203, dotnet/cecil#31) which fixes the PDB ID calculation which in turn affects the MVID calculation (we actually just got lucky in net6 since we run the linker against all assemblies in the libraries build which "unified" the MVID).

Since the PDB is storing the MVID of all assembly references in "Compilation Metadata References" (described in https://github.com/dotnet/runtime/blob/424a09cb81c678fb1ba1c27211b80aba2de070ad/docs/design/specs/PortablePdb-Metadata.md#CompilationMetadataReferences) we now get a different MVID on the assemblies because these projects compile directly against System.Private.CoreLib.dll whose MVID gets embedded in the PDB:
PDB

There is interest to backport the linker/Cecil fix to net6 so I discussed with @vitek-karas and we agreed the least invasive change is to simply hardcode System.Private.CoreLib.dll as the only architecture-specific assembly in Xamarin Android for net6. EDIT it turns out this plan won't work since the Mono AOT compiler verifies the MVIDs of assembly references and will error out if they don't match with the actual assembly.

For net7/net8 we'd like to come up with a "proper" fix though, some ideas:

  • Create a reference assembly for System.Private.CoreLib and compile against that one, instead of the real assembly
    • I tried using Roslyn's /refout feature but the reference assembly created by it still differs between architectures since Roslyn includes private fields like Padding.cs#L9-L16, so this'd likely need to be a real reference assembly
  • Make System.Private.CoreLib architecture-agnostic by removing all of the TARGET_64BIT and TARGET_32BIT ifdefs.
  • Tweak the (official) build so we only ever build the non-architecture-specific assemblies once and copy them into the respective runtime packs, ensuring that we only have a single version of the assemblies
    • As an extension we could split out the assemblies into another package, so they're not duplicated in the runtime packs. This would also help reducing download size.

/cc @ViktorHofer @vitek-karas @agocke @marek-safar @steveisok

Author: akoeplinger
Assignees: -
Labels:

area-Infrastructure-mono

Milestone: 7.0.0

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jul 13, 2022
akoeplinger added a commit that referenced this issue Aug 2, 2022
We now compile against the reference assembly in all places where we were compiling against the mono/coreclr System.Private.CoreLib.dll implementation assembly before.

The new reference assembly consumes sources from the existing contracts to avoid checking in a generated version of SPC.dll (this would add ~20k lines of .cs which is mostly duplicated with System.Runtime.cs)

Since a few contracts have only partially moved types to SPC we wrap contract types with `#if !BUILDING_CORELIB_REFERENCE` so we can hide them when compiling the SPC reference assembly. This needed a few GenAPI changes which are implemented here: dotnet/arcade#10003.

Note that this means that the types which live in CoreLib are moved to the end of the ref .cs file which causes a GitHub diff to show up, but it is actually a no-op.

Regenerating the ref .cs files works the same way as before, by running the `GenerateReferenceAssemblySource` target in the contract's src\ folder.

Fixes #67660

Co-authored-by: Viktor Hofer <viktor.hofer@microsoft.com>
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Aug 2, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 1, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

7 participants