Skip to content

NativeAOT's implementation of TrimmerRootAssembly is too aggressive #92271

Closed
@vitek-karas

Description

@vitek-karas

For example:

TestApp.csproj (normal console app)

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\TestLib\TestLib.csproj" />
    <TrimmerRootAssembly Include="TestLib" />
  </ItemGroup>

</Project>

And then the TestLib contains

using System.Runtime.Loader;

namespace TestLib;

internal class Derived : AssemblyLoadContext
{
}

Now running (on the TestApp):

dotnet publish --self-contained /p:PublishTrimmed=true

The output will contain AssemblyLoadContext class in CoreLib, but for example the AssemblyLoadContext.GetAssemblyName method will be missing from it (since it's not used anywhere).

Now publishing the same app with NativeAOT:

dotnet publish /p:PublishAot=true

Using sizoscope tells us that AssemblyLoadContext.GetAssemblyName is actually preserved in the app.

In short, the NativeAOT implementation of assembly rooting seems to be rooting too much. For example it seems to fully root all base types of the types from the target assembly, including all members on such base types.

This is not a problem necessarily for cases where TrimmerRootAssembly is used as a workaround to trim incompatibilities (it's adds more code), but it can matter more when it's used in a test app to validate trim/AOT compatibility of a library.

It would also be nice to make the behavior consistent between illink and ilc.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions