diff --git a/.github/alpine/setup-node.sh b/.github/alpine/setup-node.sh new file mode 100755 index 0000000000..bde0d9d300 --- /dev/null +++ b/.github/alpine/setup-node.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +[ -n "$1" ] || { echo "Usage: $0 "; exit 1; } + +# A workaround for "JavaScript Actions in Alpine containers are only supported on x64 Linux runners." +# https://github.com/actions/runner/blob/8a9b96806d12343f7d123c669e29c629138023dd/src/Runner.Worker/Handlers/StepHost.cs#L283-L290 +if [ "$(uname -m)" != "x86_64" ]; then + mkdir -p $1 + ln -s /usr/bin/node $1 + ln -s /usr/bin/npm $1 + sed -i 's/ID=alpine/ID=unknown/' /usr/lib/os-release +fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4739f96a9f..649fbec18f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,12 @@ jobs: rid: linux-musl-x64 container: image: ghcr.io/getsentry/sentry-dotnet-alpine:3.21 + - os: ubuntu-24.04-arm + rid: linux-musl-arm64 + container: + image: ghcr.io/getsentry/sentry-dotnet-alpine:3.21 + volumes: + - /tmp/node20:/__e/node20 - os: macos-15 # Pin macos to get the version of Xcode that we need: https://github.com/actions/runner-images/issues/10703 rid: macos # universal (osx-arm64 + osx-x64) - os: windows-latest @@ -35,6 +41,11 @@ jobs: rid: win-arm64 steps: + - name: Initialize Alpine Linux + if: ${{ contains(matrix.container.image, 'alpine') }} + run: | + curl -sSL https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}/.github/alpine/setup-node.sh | sudo bash /dev/stdin /__e/node20/bin/ + - name: Checkout uses: actions/checkout@v4 @@ -83,6 +94,14 @@ jobs: image: ghcr.io/getsentry/sentry-dotnet-alpine:3.21 volumes: - /var/run/docker.sock:/var/run/docker.sock + - os: ubuntu-24.04-arm + rid: linux-musl-arm64 + slnf: Sentry-CI-Build-Linux-musl.slnf + container: + image: ghcr.io/getsentry/sentry-dotnet-alpine:3.21 + volumes: + - /tmp/node20:/__e/node20 + - /var/run/docker.sock:/var/run/docker.sock - os: macos-15 # Pin macos to get the version of Xcode that we need: https://github.com/actions/runner-images/issues/10703 rid: macos # universal (osx-arm64 + osx-x64) slnf: Sentry-CI-Build-macOS.slnf @@ -94,6 +113,11 @@ jobs: slnf: Sentry-CI-Build-Windows-arm64.slnf steps: + - name: Initialize Alpine Linux + if: ${{ contains(matrix.container.image, 'alpine') }} + run: | + curl -sSL https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}/.github/alpine/setup-node.sh | sudo bash /dev/stdin /__e/node20/bin/ + - name: Cancel Previous Runs if: github.ref_name != 'main' && !startsWith(github.ref_name, 'release/') uses: styfle/cancel-workflow-action@85880fa0301c86cca9da44039ee3bb12d3bedbfa # Tag: 0.12.1 @@ -140,6 +164,14 @@ jobs: key: sentry-native-linux-musl-x64-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }} fail-on-cache-miss: true + - name: Download sentry-native (linux-musl-arm64) + if: ${{ (env.CI_PUBLISHING_BUILD == 'true') || (matrix.rid == 'linux-musl-arm64') }} + uses: actions/cache/restore@v4 + with: + path: src/Sentry/Platforms/Native/sentry-native + key: sentry-native-linux-musl-arm64-${{ hashFiles('scripts/build-sentry-native.ps1') }}-${{ hashFiles('.git/modules/modules/sentry-native/HEAD') }} + fail-on-cache-miss: true + - name: Download sentry-native (macos) if: ${{ (env.CI_PUBLISHING_BUILD == 'true') || (matrix.rid == 'macos') }} uses: actions/cache/restore@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b211716ed..4a51bf8230 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Native AOT: don't load SentryNative on unsupported platforms ([#4347](https://github.com/getsentry/sentry-dotnet/pull/4347)) - Fixed issue introduced in release 5.12.0 that might prevent other middleware or user code from reading request bodies ([#4373](https://github.com/getsentry/sentry-dotnet/pull/4373)) - SentryTunnelMiddleware overwrites the X-Forwarded-For header ([#4375](https://github.com/getsentry/sentry-dotnet/pull/4375)) +- Native AOT support for `linux-musl-arm64` ([#4365](https://github.com/getsentry/sentry-dotnet/pull/4365)) ### Dependencies diff --git a/integration-test/runtime.Tests.ps1 b/integration-test/runtime.Tests.ps1 index c8a0a55e15..158890edc3 100644 --- a/integration-test/runtime.Tests.ps1 +++ b/integration-test/runtime.Tests.ps1 @@ -60,11 +60,17 @@ internal class FakeTransport : ITransport } else { - if ("Arm64".Equals([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString())) + $musl = (ldd --version 2>&1) -match 'musl' + $arm64 = "Arm64".Equals([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()) + if ($musl -and $arm64) + { + return "./console-app/bin/Release/$framework/linux-musl-arm64/publish/console-app" + } + elseif ($arm64) { return "./console-app/bin/Release/$framework/linux-arm64/publish/console-app" } - elseif ((ldd --version 2>&1) -match 'musl') + elseif ($musl) { return "./console-app/bin/Release/$framework/linux-musl-x64/publish/console-app" } diff --git a/scripts/build-sentry-native.ps1 b/scripts/build-sentry-native.ps1 index 8e7361901d..ee1d9a99ca 100644 --- a/scripts/build-sentry-native.ps1 +++ b/scripts/build-sentry-native.ps1 @@ -37,11 +37,17 @@ try } elseif ($IsLinux) { - if ("Arm64".Equals([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString())) + $musl = (ldd --version 2>&1) -match 'musl' + $arm64 = "Arm64".Equals([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()) + if ($musl -and $arm64) + { + $outDir += '/linux-musl-arm64' + } + elseif ($arm64) { $outDir += '/linux-arm64' } - elseif ((ldd --version 2>&1) -match 'musl') + elseif ($musl) { $outDir += '/linux-musl-x64' } diff --git a/src/Sentry/Platforms/Native/CFunctions.cs b/src/Sentry/Platforms/Native/CFunctions.cs index 3d87e5d009..582112fc4c 100644 --- a/src/Sentry/Platforms/Native/CFunctions.cs +++ b/src/Sentry/Platforms/Native/CFunctions.cs @@ -81,7 +81,6 @@ internal static void SetValueIfNotNull(sentry_value_t obj, string key, double? v public static bool Init(SentryOptions options) { - _isWindows = System.OperatingSystem.IsWindows(); var cOptions = sentry_options_new(); // Note: DSN is not null because options.IsValid() must have returned true for this to be called. @@ -441,7 +440,8 @@ private static void nativeTransportFree(IntPtr state) // The logger we should forward native messages to. This is referenced by nativeLog() which in turn for. private static IDiagnosticLogger? _logger; - private static bool _isWindows = false; + private static bool _isWindows = System.OperatingSystem.IsWindows(); + private static bool _isArm64 = RuntimeInformation.OSArchitecture == Architecture.Arm64; // This method is called from the C library and forwards incoming messages to the currently set _logger. // [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] // error CS3016: Arrays as attribute arguments is not CLS-complian @@ -495,21 +495,14 @@ private static void nativeLogImpl(int cLevel, IntPtr format, IntPtr args, IntPtr message = Marshal.PtrToStringAnsi(buffer); }); } + // For Linux/macOS, we must make a copy of the VaList to be able to pass it back... + else if (_isArm64) + { + message = FormatWithVaList(format, args); + } else { - // For Linux/macOS, we must make a copy of the VaList to be able to pass it back... - var argsStruct = Marshal.PtrToStructure(args); - var formattedLength = 0; - WithMarshalledStruct(argsStruct, argsPtr => - formattedLength = 1 + vsnprintf_linux(IntPtr.Zero, UIntPtr.Zero, format, argsPtr) - ); - - WithAllocatedPtr(formattedLength, buffer => - WithMarshalledStruct(argsStruct, argsPtr => - { - vsnprintf_linux(buffer, (UIntPtr)formattedLength, format, argsPtr); - message = Marshal.PtrToStringAnsi(buffer); - })); + message = FormatWithVaList(format, args); } } catch (Exception err) @@ -534,7 +527,7 @@ private static void nativeLogImpl(int cLevel, IntPtr format, IntPtr args, IntPtr // https://stackoverflow.com/a/4958507/2386130 [StructLayout(LayoutKind.Sequential, Pack = 4)] - private struct VaListLinux64 + private struct VaListX64 { private uint _gp_offset; private uint _fp_offset; @@ -542,6 +535,17 @@ private struct VaListLinux64 private IntPtr _reg_save_area; } + // https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#definition-of-va-list + [StructLayout(LayoutKind.Sequential)] + private struct VaListArm64 + { + private IntPtr __stack; + private IntPtr __gr_top; + private IntPtr __vr_top; + private int __gr_offs; + private int __vr_offs; + } + private static void WithAllocatedPtr(int size, Action action) { var ptr = IntPtr.Zero; @@ -562,4 +566,23 @@ private static void WithMarshalledStruct(T structure, Action action) Marshal.StructureToPtr(structure, ptr, false); action(ptr); }); + + private static string? FormatWithVaList<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(IntPtr format, IntPtr args) where T : struct + { + string? message = null; + var argsStruct = Marshal.PtrToStructure(args); + var formattedLength = 0; + WithMarshalledStruct(argsStruct, argsPtr => + formattedLength = 1 + vsnprintf_linux(IntPtr.Zero, UIntPtr.Zero, format, argsPtr) + ); + + WithAllocatedPtr(formattedLength, buffer => + WithMarshalledStruct(argsStruct, argsPtr => + { + vsnprintf_linux(buffer, (UIntPtr)formattedLength, format, argsPtr); + message = Marshal.PtrToStringAnsi(buffer); + })); + + return message; + } } diff --git a/src/Sentry/Platforms/Native/Sentry.Native.targets b/src/Sentry/Platforms/Native/Sentry.Native.targets index f425ac9b30..d79ec6e414 100644 --- a/src/Sentry/Platforms/Native/Sentry.Native.targets +++ b/src/Sentry/Platforms/Native/Sentry.Native.targets @@ -17,6 +17,8 @@ $(SentryNativeOutputDirectory)$(NativeLibRelativePath-linux-musl-x64)\ linux-arm64 $(SentryNativeOutputDirectory)$(NativeLibRelativePath-linux-arm64)\ + linux-musl-arm64 + $(SentryNativeOutputDirectory)$(NativeLibRelativePath-linux-musl-arm64)\ osx $(SentryNativeOutputDirectory)$(NativeLibRelativePath-osx)\ $(SentryNativeOutputDirectory-win-x64)lib$(SentryNativeLibraryName).lib @@ -24,6 +26,7 @@ $(SentryNativeOutputDirectory-linux-x64)lib$(SentryNativeLibraryName).a $(SentryNativeOutputDirectory-linux-musl-x64)lib$(SentryNativeLibraryName).a $(SentryNativeOutputDirectory-linux-arm64)lib$(SentryNativeLibraryName).a + $(SentryNativeOutputDirectory-linux-musl-arm64)lib$(SentryNativeLibraryName).a $(SentryNativeOutputDirectory-osx)lib$(SentryNativeLibraryName).a @@ -69,6 +72,13 @@ + + + true + \sentry-native\$(NativeLibRelativePath-linux-musl-arm64) + + + true diff --git a/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets b/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets index f2292d9a2c..792acfc289 100644 --- a/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets +++ b/src/Sentry/Platforms/Native/buildTransitive/Sentry.Native.targets @@ -19,7 +19,7 @@ true - true + true true @@ -52,11 +52,11 @@ - + - +