Skip to content

Cross built ILCompiler NuGet contains HostOS (Linux) ELFs not TargetOS (FreeBSD) ELFs #104497

Description

@Thefrank

Overview:

When cross compiling runtime, the ILCompiler (and NuGet) that is produced contains HostOS (Linux) and not TargetOS (FreeBSD) ELFs.

Reproduction:

Using a docker container on Linux pulled from: https://raw.githubusercontent.com/dotnet/versions/master/build-info/docker/image-info.dotnet-dotnet-buildtools-prereqs-docker-main.json containing a FreeBSD ROOTFS and the most recent net9 preview tag from runtime: v9.0.0-preview.5.24306.7

docker run -e ROOTFS_DIR=/crossrootfs/$ROOTFSARCH -v ${BUILD_SOURCESDIRECTORY}/runtime:/runtime $DOTNETDOCKERCONTAINERUSED /runtime/build.sh -c ${{ parameters.buildType }} -cross -os freebsd -arch $ROOTFSARCH -ci /p:OfficialBuildId=$OFFICIALBUILDID --subset clr+mono+mono.manifests+tools+libs+host+packs

Expected behavior:

ELFs should be like other items generated for TargetOS:

$file crossgen2 
crossgen2: ELF 64-bit LSB pie executable, x86-64, version 1 (FreeBSD), dynamically linked, interpreter /libexec/ld-elf.so.1, for FreeBSD 13.2, FreeBSD-style, BuildID[sha1]=54a7f1c2a4752435c2cffd15eeb959f609966907, stripped

Actual behavior:

The resulting runtime.freebsd-x64.Microsoft.DotNet.ILCompiler.9.0.0-preview.5.24306.7.nupkg contains Linux ELFs and FreeBSD libs.

$find ./ * | xargs file
./:                                           directory
./ILCompiler.RyuJit.pdb:                      Microsoft Roslyn C# debugging symbols version 1.0
./libclrjit_unix_x64_x64.so:                  ELF 64-bit LSB shared object, x86-64, version 1 (FreeBSD), dynamically linked, for FreeBSD 13.2, BuildID[sha1]=66177aebc4ab51f16fe1e6a5faa90a7ade09b674, stripped
./libclrjit_win_x86_x64.so:                   ELF 64-bit LSB shared object, x86-64, version 1 (FreeBSD), dynamically linked, for FreeBSD 13.2, BuildID[sha1]=79ecdf1053497bde0393928dee1a727bc6b6b6a1, stripped
./libclrjit_universal_arm_x64.so:             ELF 64-bit LSB shared object, x86-64, version 1 (FreeBSD), dynamically linked, for FreeBSD 13.2, BuildID[sha1]=b15d888e793cca18b6dd42b3b672f7144fbe45ec, stripped
./libjitinterface_x64.so:                     ELF 64-bit LSB shared object, x86-64, version 1 (FreeBSD), dynamically linked, for FreeBSD 13.2, BuildID[sha1]=02a2d4a17bcbd0ff35f3caca9252853f95529a3c, stripped
./ILCompiler.TypeSystem.pdb:                  Microsoft Roslyn C# debugging symbols version 1.0
./ILCompiler.DependencyAnalysisFramework.pdb: Microsoft Roslyn C# debugging symbols version 1.0
./ILCompiler.Compiler.pdb:                    Microsoft Roslyn C# debugging symbols version 1.0
./libclrjit_universal_arm64_x64.so:           ELF 64-bit LSB shared object, x86-64, version 1 (FreeBSD), dynamically linked, for FreeBSD 13.2, BuildID[sha1]=0b476dc684291af72ab673b77dccbbf7f386cbf8, stripped
./libclrjit_win_x64_x64.so:                   ELF 64-bit LSB shared object, x86-64, version 1 (FreeBSD), dynamically linked, for FreeBSD 13.2, BuildID[sha1]=a58827fd1b7ac68612408fa5e13c7db9091938a2, stripped
./ilc.pdb:                                    Microsoft Roslyn C# debugging symbols version 1.0
./ilc:                                        ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=41cb9e347020ad19b6402190528b968b6850c46f, stripped

Regression:

To the best of my knowledge, it has always been this way. This was uncaught until recently when I tried to use a cross built package to bootstrap a native build.

Known Workarounds:

None?

Other info:

TargetOS=linux only appears three places in the .binlog for the build that are after Evaluation. All three are from ILCompiler.cspoj : The first seems to come as a return from ResolveReadyToRunCompilers and the other two (_PrepareForReadyToRunCompilation) and (_CreateR2RImages) use it. NativeAotSupported is reassigned here:

<NativeAotSupported Condition="'$(TargetOS)' == 'freebsd' and '$(CrossBuild)' == 'true'">false</NativeAotSupported>
but Crossgen2 is still used on the ilc binary and the process does not error from
_targetPlatform != hostPlatform ||
!GetCrossgenComponentsPaths())
{
Log.LogError(Strings.ReadyToRunTargetNotSupportedError);
return false;

There is no "Property reassignment" note in the binlog when ResolveReadyToRunCompilers changes(?) the TargetOS=freebsd to linux

.binlog is not an allowed attachment type so hopefully a screenshot from the MSBuild Structured Log Viewer is enough to help explain what I am seeing.

image

The Crossgen2 project seems to avoid the ReadyToRun part during packaging:

<Target Name="RunPublishedCrossgen" AfterTargets="PublishCrossgen"
Condition="'$(TargetOS)' == '$(HostOS)' and '$(TargetArchitecture)' == '$(BuildArchitecture)' and '$(CrossBuild)' != 'true'">

Activity

  1. ghost added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Jul 5, 2024
  2. added and removed
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Jul 5, 2024
  3. dotnet-policy-service commented on Jul 5, 2024

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
    See info in area-owners.md if you want to be subscribed.

  4. jkotas commented on Jul 5, 2024

    @jkotas
    Member

    This was refactored in #103508 . Does the problem still exist in current main?

  5. Thefrank commented on Jul 6, 2024

    @Thefrank
    ContributorAuthor

    @jkotas

    Just checked. It still exists as of e125e93

  6. jkotas commented on Jul 6, 2024

    @jkotas
    Member

    Ok, here is the problem:

    The regular build uses "last known good" toolchain (the one that comes from https://github.com/dotnet/runtime/blob/main/global.json#L3) for aot compilation of most managed tools and components. The one exception is crossgen2 that uses live built aot toolchain that leads to interesting problems every once in a while.

    When cross-compiling from Linux to FreeBSD, there is no "last known good" aot toolchain that is able to cross-compile from Linux to FreeBSD. We end up doing Linux->Linux publish instead due to some incorrect build logic and produce binaries for wrong architecture as you have observed.

    I can think about two options for addressing this:

    • Enable use of live built aot toolchain for compilation of managed tools and components. The current prior art with crossgen2, is complicated and fragile. We had several attempts to extend it to other tools and components (e.g. Crossgen2 should publish with NativeAOT at the beginning of the build #71557) that we ended up not pursuing. I think that the proper solution would be a multi-stage build: We need to first build the compiler that is then going to be used to compile the shipping compiler. It should be opt-in option and we should keep using "last known good" to keep the default build simple. The source build bootstrapping does similar multi-stage build already, perhaps we can use some of the same controls.
    • Disable aot compilation of the managed tools and components when there is no "last known good" toolchain. It may be simpler, but it would mean that the cross-compiling Linux to FreeBSD build does not have full fidelity with the native FreeBSD build.
  7. Thefrank commented on Jul 9, 2024

    @Thefrank
    ContributorAuthor

    Would disabling AOT for managed items still give FreeBSD (TargetOS) ELF's on components like ILCompiler?
    If yes, that might be the simplest solution for the time being.

    The multi-stage build sounds like the best solution for this issue. But, unless there is something I am missing, this issue seems limited to a niche case of cross compiling from Linux to FreeBSD/Illumos/Haiku/Other. If it helps other build scenarios for officially supported platforms then there is a better case for this but I am not familiar enough with intricacies here.

  8. jkotas commented on Jul 9, 2024

    @jkotas
    Member

    unless there is something I am missing, this issue seems limited to a niche case of cross compiling from Linux to FreeBSD/Illumos/Haiku/Other

    I think having the recipe for how to use live-built AOT compilers would be generally useful. We keep having long discussions about whether it is better to use live-built AOT compiler or last-known-good AOT compiler. So having both options and use the one that's more appropriate for given situation would be best.

    Would disabling AOT for managed items still give FreeBSD (TargetOS) ELF's on components like ILCompiler?

    It would require some extra build logic. Nothing impossible.

  9. jkotas commented on Jul 9, 2024

    @jkotas
    Member
  10. added this to the Future milestone on Jul 10, 2024
  11. 12 remaining items

  12. Thefrank commented on Aug 20, 2024

    @Thefrank
    ContributorAuthor

    Which FreeBSD version and which LLVM/Clang version?

  13. sec commented on Aug 20, 2024

    @sec
    Contributor

    13.3-RELEASE-p3 and llvm18 (had the same with llvm15). Tried to native build runtime with -p:LinkerFlavor=bfd switch, but this does not changed anything. Also there's one version of ilc that does work from native build:

    sec@amper:/mnt/sec/dotnet-core-freebsd-source-build/runtime % find . -type f -name ilc -exec ls -al {} +
    -rwxr--r--  1 sec  sec  16606596 Aug 19 16:56 ./.packages/runtime.freebsd-arm64.microsoft.dotnet.ilcompiler/9.0.0-preview.7.24405.7/tools/ilc
    -rwxr-xr-x  1 sec  sec  14560696 Aug 20 11:58 ./artifacts/bin/coreclr/freebsd.arm64.Release/ilc-published/ilc
    -rwxr-xr-x  1 sec  sec     72480 Aug 20 11:57 ./artifacts/bin/coreclr/freebsd.arm64.Release/ilc/ilc
    -rwxr-xr-x  1 sec  sec  14560696 Aug 20 11:58 ./artifacts/bin/coreclr/freebsd.arm64.Release/ilc/native/ilc
    sec@amper:/mnt/sec/dotnet-core-freebsd-source-build/runtime % ./.packages/runtime.freebsd-arm64.microsoft.dotnet.ilcompiler/9.0.0-preview.7.24405.7/tools/ilc|head -n 2
    Required argument missing for command: 'ilc'.
    
    Description:
      .NET Native IL Compiler
    sec@amper:/mnt/sec/dotnet-core-freebsd-source-build/runtime % ./artifacts/bin/coreclr/freebsd.arm64.Release/ilc-published/ilc|head -n 2
    Segmentation fault (core dumped)
    sec@amper:/mnt/sec/dotnet-core-freebsd-source-build/runtime % ./artifacts/bin/coreclr/freebsd.arm64.Release/ilc/ilc|head -n 2
    Required argument missing for command: 'ilc'.
    
    Description:
      .NET Native IL Compiler
    sec@amper:/mnt/sec/dotnet-core-freebsd-source-build/runtime % ./artifacts/bin/coreclr/freebsd.arm64.Release/ilc/native/ilc|head -n 2
    Segmentation fault (core dumped)
    

    edit: hm, looks like ilc is build without symbol exported from #105587 when doing native build, will look into that.

  14. Thefrank commented on Aug 20, 2024

    @Thefrank
    ContributorAuthor

    ./artifacts/bin/coreclr/freebsd.arm64.Release/ilc/ilc might be the same from ./.packages/runtime.freebsd-arm64.microsoft.dotnet.ilcompiler/9.0.0-preview.7.24405.7/tools/ilc which might explain why it works but not why there is a difference between the crossbuilt and native build ILC

  15. sec commented on Aug 20, 2024

    @sec
    Contributor

    The size of working ones are different - native one is 72480 bytes and the one from cross nuget is 16606596. the other two, that don't work don't have symbols exported that added them on crossbuild - have build running with symbol export added without Condition="'$(_targetOS)' == 'freebsd' and '$(IsNativeExecutable)' == 'true'" to verify if this is the issue or is it somewhere else, will know if few minutes.

    edit: it sill segfault and there's no progname and environ symbol exported

    sec@amper:/mnt/sec/dotnet-core-freebsd-source-build/runtime % nm --dynamic ./.packages/runtime.freebsd-arm64.microsoft.dotnet.ilcompiler/9.0.0-preview.7.24405.7/tools/ilc|grep prog
    00000000009efaf8 D __progname
    sec@amper:/mnt/sec/dotnet-core-freebsd-source-build/runtime % nm --dynamic ./artifacts/bin/coreclr/freebsd.arm64.Release/ilc-published/ilc|grep prog
    sec@amper:/mnt/sec/dotnet-core-freebsd-source-build/runtime %
    

    @am11 maybe you could point in right direction, why those symbol exports could be missing when doing native build of ILC?

  16. sec commented on Aug 22, 2024

    @sec
    Contributor

    Could anyone explain why with the same target WriteIlcRspFileForCompilation, arguments list is different?
    this one is missing export of __progname and environ:
    image

    while this one have them:
    image

  17. am11 commented on Aug 22, 2024

    @am11
    Member

    @sec, I haven't tested it with freebsd-arm64, but if all things being equal (to x64) it should work the same way. I think the first run of the target is getting targetOS == linux for some reason in your environment.

  18. sec commented on Aug 22, 2024

    @sec
    Contributor

    @sec, I haven't tested it with freebsd-arm64, but if all things being equal (to x64) it should work the same way. I think the first run of the target is getting targetOS == linux for some reason in your environment.

    I have changed those conditions to <IlcArg Include="--export-dynamic-symbol:__progname" /> so they should always be included, but I'm missing something.

  19. am11 commented on Aug 22, 2024

    @am11
    Member

    I have changed those conditions to <IlcArg Include="--export-dynamic-symbol:__progname" /> so they should always be included, but I'm missing something.

    Have you changed it in all find ~/.nuget -name '*Unix.targets'?

  20. sec commented on Aug 22, 2024

    @sec
    Contributor

    Hm, based on your PR, you only added those to src/coreclr/nativeaot/BuildIntegration/Microsoft.NETCore.Native.targets

    I tried to look where --export-dynamic-symbol:DotNetRuntimeDebugHeader is added and it's only also in this file and it's added in both runs.

    Microsoft.NETCore.Native.Unix.targets don't contains any IlcArgs

  21. am11 commented on Aug 22, 2024

    @am11
    Member

    Ah I meant find ~/.nuget -name '*Native.targets'. I'll create a VM on osx arm64 and try to debug it on the weekend.

  22. sec commented on Aug 22, 2024

    @sec
    Contributor

    Ok, I think I've found the issue, indeed I had some old nuget's cached - will confirm that in few minutes.

    edit: yes, I had old Microsoft.DotNet.ILCompiler.9.0.0-previ ew.7.24405.7 inside runtime/.packages that didn't had those args added (that was from the crossbuild before ilc patch...) - thanks @am11 :)

    full build of runtime/aspnet/sdk was completed under freebsd-arm64 :)

  23. Thefrank commented on Aug 22, 2024

    @Thefrank
    ContributorAuthor

    One of those IlcArg items in both projects might(should?) contain a --targetos: value. If it does read linux or that entry is not there, then you might have to fish around the log and find where it got misassigned.

  24. added
    in-prThere is an active PR which will close this issue when it is merged
    on Aug 29, 2024
  25. locked and limited conversation to collaborators on Feb 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-NativeAOT-coreclrin-prThere is an active PR which will close this issue when it is mergedos-freebsdFreeBSD OS

    Type

    No type

    Projects

    • Status
      No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions